Skip to content

Sample

The Sample class contains the main element of the data model: all the heterogeneity and generality capacities come from it. This Jupyter Notebook demonstrates various operations and methods involving a sample data structure using the PLAID library. We start with simple sample generation examples relying on Muscat reader, and finish with more advanced data handling examples.

# First imports
from pathlib import Path
import numpy as np
from plaid import Sample

Section 1: Simple sample creation using Muscat readers

Load a physics simulation in a given format, here in vtk.

# Load a vtk file
from Muscat.IO.VtkReader import VtkReader

reader = VtkReader()

try:
    mesh_path = Path(__file__).resolve().parent / "mesh.vtk"
except NameError: # for jupyter notebook execution
    mesh_path = Path("mesh.vtk").resolve()

mesh = reader.Read(fileName=str(mesh_path))
Kokkos::OpenMP::initialize WARNING: OMP_PROC_BIND environment variable not set
  In general, for best performance with OpenMP 4.0 or better set OMP_PROC_BIND=spread and OMP_PLACES=threads
  For best performance with OpenMP 3.1 set OMP_PROC_BIND=true
  For unit testing set OMP_PROC_BIND=false
# Convert it to a CGNS tree using Muscat's CGNS bridge
from Muscat.Bridges.CGNSBridge import MeshToCGNS
from plaid.utils import cgns_helper as CGH

tree = MeshToCGNS(mesh)
print("#---# Show CGNS Tree")
CGH.show_cgns_tree(tree)
#---# Show CGNS Tree
 CGNSLibraryVersion : (1,) [4.] float32 CGNSLibraryVersion_t
 Base_2_3 : (2,) [2 3] int32 CGNSBase_t
|_  Bulk : None Family_t
|_  Zone : (1, 3) [[50 96  0]] int64 Zone_t
   |_  ZoneType : (12,) Unstructured |S1 ZoneType_t
   |_  GridCoordinates : None GridCoordinates_t
      |_  CoordinateX : (50,) [0.         ... 0.15340106] float64 DataArray_t
      |_  CoordinateY : (50,) [ 0.         ... -0.15340106] float64 DataArray_t
      |_  CoordinateZ : (50,) [ 0.5        ... -0.45048442] float64 DataArray_t
   |_  Elements_TRI_3 : (2,) [5 0] int32 Elements_t
      |_  ElementRange : (2,) [ 1 96] int64 IndexRange_t
      |_  ElementConnectivity : (288,) [3 ... 7] int64 DataArray_t
   |_  VertexFields : None FlowSolution_t
      |_  GridLocation : (6,) Vertex |S1 GridLocation_t
      |_  OriginalIds : (50,) [ 1 ... 50] int64 DataArray_t
      |_  Normals_0 : (50,) [0.         ... 0.30680212] float32 DataArray_t
      |_  Normals_1 : (50,) [ 0.         ... -0.30680212] float32 DataArray_t
      |_  Normals_2 : (50,) [ 1.         ... -0.90096885] float32 DataArray_t
   |_  CellCenterFields : None FlowSolution_t
      |_  GridLocation : (10,) CellCenter |S1 GridLocation_t
      |_  OriginalIds : (96,) [ 1 ... 96] int64 DataArray_t
   |_  FamilyName : (4,) Bulk |S1 FamilyName_t

Initialize a plaid sample, populate it with a CGNS tree and add a global

sample = Sample()
sample.add_tree(tree)
sample.add_global("power", 1.)

Check the sample content

print(sample.check_completeness())
print(sample.summarize())
Sample Completeness Check:
==============================
Has scalars: True
Has meshes: True
Total unique fields: 4
Field names: Normals_0, Normals_1, Normals_2, OriginalIds

Sample Summary:
==================================================
Scalars (1):
  - power: 1.0

Meshes (1 timestamps):
    Time: 0.0
        Base: Base_2_3
            Zone: Zone
                Nodes (50)
                Location: Vertex
                    Fields (4): Normals_0, Normals_1, Normals_2, OriginalIds
                Location: CellCenter
                    Fields (1): OriginalIds
                Elements (96)
                    TRI_3 (96)
        Base: Global

Section 2: Advanced data handling

Section 2.1: Initializing an Empty Sample and Adding Data

This section demonstrates how to initialize an empty Sample and add scalars, and meshes / CGNS trees.

Create and display CGNS tree from an unstructured mesh

# Input data
from Muscat.MeshTools import MeshCreationTools as MCT

points = np.array(
    [
        [0.0, 0.0],
        [1.0, 0.0],
        [1.0, 1.0],
        [0.0, 1.0],
        [0.5, 1.5],
    ]
)

triangles = np.array(
    [
        [0, 1, 2],
        [0, 2, 3],
        [2, 4, 3],
    ]
)

Mesh = MCT.CreateMeshOfTriangles(points, triangles)
Mesh.nodeFields["test_node_field_1"] = np.random.randn(5)
Mesh.elemFields["test_elem_field_1"] = np.random.randn(3)
tree = MeshToCGNS(Mesh)
print("#---# Show CGNS Tree")
CGH.show_cgns_tree(tree)

print("\n#---# Summarize CGNS Tree")
CGH.summarize_cgns_tree(tree)

print("\n#---# Summarize CGNS Tree without additional Field Information")
CGH.summarize_cgns_tree(tree, verbose=False)
#---# Show CGNS Tree
 CGNSLibraryVersion : (1,) [4.] float32 CGNSLibraryVersion_t
 Base_2_2 : (2,) [2 2] int32 CGNSBase_t
|_  2D : None Family_t
|_  Zone : (1, 3) [[5 3 0]] int64 Zone_t
   |_  ZoneType : (12,) Unstructured |S1 ZoneType_t
   |_  GridCoordinates : None GridCoordinates_t
      |_  CoordinateX : (5,) [0.  1.  1.  0.  0.5] float64 DataArray_t
      |_  CoordinateY : (5,) [0.  0.  1.  1.  1.5] float64 DataArray_t
   |_  Elements_TRI_3 : (2,) [5 0] int32 Elements_t
      |_  ElementRange : (2,) [1 3] int64 IndexRange_t
      |_  ElementConnectivity : (9,) [1 ... 4] int64 DataArray_t
   |_  VertexFields : None FlowSolution_t
      |_  GridLocation : (6,) Vertex |S1 GridLocation_t
      |_  OriginalIds : (5,) [1 2 3 4 5] int64 DataArray_t
      |_  test_node_field_1 : (5,) [ 1.57228445 -0.79812797 -0.74235336 -0.28304151 -1.1860188 ] float64 DataArray_t
   |_  CellCenterFields : None FlowSolution_t
      |_  GridLocation : (10,) CellCenter |S1 GridLocation_t
      |_  OriginalIds : (3,) [1 2 3] int64 DataArray_t
      |_  test_elem_field_1 : (3,) [-1.10621078  0.29662337 -1.18089037] float64 DataArray_t
   |_  FamilyName : (2,) 2D |S1 FamilyName_t

#---# Summarize CGNS Tree
Number of Bases: 1
Number of Zones: 1
Number of Nodes: 5
Number of Elements: 3
Number of Fields: 6

Fields :
  Base_2_2/Zone/VertexFields/GridLocation
  Base_2_2/Zone/VertexFields/OriginalIds
  Base_2_2/Zone/VertexFields/test_node_field_1
  Base_2_2/Zone/CellCenterFields/GridLocation
  Base_2_2/Zone/CellCenterFields/OriginalIds
  Base_2_2/Zone/CellCenterFields/test_elem_field_1

#---# Summarize CGNS Tree without additional Field Information
Number of Bases: 1
Number of Zones: 1
Number of Nodes: 5
Number of Elements: 3
Number of Fields: 6

Initialize a new empty Sample and print it

# Initialize an empty Sample
print("#---# Empty Sample")
sample = Sample()

print(sample, end="\n\n")
print(sample.summarize())
#---# Empty Sample
Sample(0 globals, 0 timestamps, 0 fields)

Sample Summary:
==================================================
Meshes (0 timestamps):

Add a scalars to a Sample

# Add a rotation scalar to this Sample
sample.add_global("rotation", np.random.randn())

print(sample.summarize())
Sample Summary:
==================================================
Scalars (1):
  - rotation: -0.8595603514974286

Meshes (1 timestamps):
    Time: 0.0
        Base: Global
# Add a more scalars to this Sample
sample.add_global("speed", np.random.randn())
sample.add_global("other", np.random.randn())

print(sample.summarize())
Sample Summary:
==================================================
Scalars (3):
  - rotation: -0.8595603514974286
  - speed: -0.4207686397533967
  - other: -0.9008198766721682

Meshes (1 timestamps):
    Time: 0.0
        Base: Global

Add a CGNS Tree to a Sample and display it

# Add the previously created CGNS tree to the sample
sample.add_tree(tree)

# Display the Sample CGNS tree
sample.show_tree()
 CGNSLibraryVersion : (1,) [4.] float32 CGNSLibraryVersion_t
 Global : (2,) [1 1] int32 CGNSBase_t
|_  Time : (1,) [1] int32 BaseIterativeData_t
   |_  IterationValues : (1,) [1] int32 DataArray_t
   |_  TimeValues : (1,) [0.] float64 DataArray_t
|_  rotation : (1,) [-0.85956035] float64 DataArray_t
|_  speed : (1,) [-0.42076864] float64 DataArray_t
|_  other : (1,) [-0.90081988] float64 DataArray_t
 Base_2_2 : (2,) [2 2] int32 CGNSBase_t
|_  2D : None Family_t
|_  Zone : (1, 3) [[5 3 0]] int64 Zone_t
   |_  ZoneType : (12,) Unstructured |S1 ZoneType_t
   |_  GridCoordinates : None GridCoordinates_t
      |_  CoordinateX : (5,) [0.  1.  1.  0.  0.5] float64 DataArray_t
      |_  CoordinateY : (5,) [0.  0.  1.  1.  1.5] float64 DataArray_t
   |_  Elements_TRI_3 : (2,) [5 0] int32 Elements_t
      |_  ElementRange : (2,) [1 3] int64 IndexRange_t
      |_  ElementConnectivity : (9,) [1 ... 4] int64 DataArray_t
   |_  VertexFields : None FlowSolution_t
      |_  GridLocation : (6,) Vertex |S1 GridLocation_t
      |_  OriginalIds : (5,) [1 2 3 4 5] int64 DataArray_t
      |_  test_node_field_1 : (5,) [ 1.57228445 -0.79812797 -0.74235336 -0.28304151 -1.1860188 ] float64 DataArray_t
   |_  CellCenterFields : None FlowSolution_t
      |_  GridLocation : (10,) CellCenter |S1 GridLocation_t
      |_  OriginalIds : (3,) [1 2 3] int64 DataArray_t
      |_  test_elem_field_1 : (3,) [-1.10621078  0.29662337 -1.18089037] float64 DataArray_t
   |_  FamilyName : (2,) 2D |S1 FamilyName_t
|_  Time : (1,) [1] int32 BaseIterativeData_t
   |_  IterationValues : (1,) [1] int32 DataArray_t
   |_  TimeValues : (1,) [0.] float64 DataArray_t

Set all meshes with their corresponding time step

# Init an empty Sample
new_sample_mult_mesh = Sample()

# All meshes with their corresponding time step
meshes_dict = {0.0: tree, 0.5: tree, 1.0: tree}

# Set meshes in the Sample
new_sample_mult_mesh.set_trees(meshes_dict)

print(f"{new_sample_mult_mesh.get_all_time_values() = }")
new_sample_mult_mesh.get_all_time_values() = [0.0, 0.5, 1.0]

Section 2.2: Accessing and Modifying Sample Data

This section demonstrates how to access and modify base, zone, node, scalar and field data within the Sample.

Initialize CGNS tree base

# Initialize an new empty Sample
print("#---# Empty Sample")
sample = Sample()
print(sample, end="\n\n")

# Init CGNS tree base at time 0.
sample.init_base(2, 3, "SurfaceMesh", time=0.0)

print(sample.summarize())
#---# Empty Sample
Sample(0 globals, 0 timestamps, 0 fields)

Sample Summary:
==================================================
Meshes (1 timestamps):
    Time: 0.0
        Base: SurfaceMesh

Initialize CGNS tree zone

# Init CGNS tree zone to a base at time 0.
shape = np.array([[len(points), len(triangles), 0]])
sample.init_zone(shape, zone="TestZoneName", base="SurfaceMesh", time=0.0)

print(sample.summarize())
Sample Summary:
==================================================
Meshes (1 timestamps):
    Time: 0.0
        Base: SurfaceMesh
            Zone: TestZoneName
                Elements (0)

Set the coordinates of nodes for a specified base and zone

points = np.array(
    [
        [0.0, 0.0],
        [1.0, 0.0],
        [1.0, 1.0],
        [0.0, 1.0],
        [0.5, 1.5],
    ]
)

# Set the coordinates of nodes for a specified base and zone at a given time.
# set node coordinates
sample.set_nodes(points, base="SurfaceMesh", zone="TestZoneName", time=0.0)

print(sample.summarize())
Sample Summary:
==================================================
Meshes (1 timestamps):
    Time: 0.0
        Base: SurfaceMesh
            Zone: TestZoneName
                Nodes (5)
                Elements (0)

Add a field to a specified zone in the grid

# Add a field to a specified zone
sample.add_field(
    "Pressure",
    np.random.randn(len(points)),
    base="SurfaceMesh",
    zone="TestZoneName",
    time=0.0,
)

print(sample.summarize())
Sample Summary:
==================================================
Meshes (1 timestamps):
    Time: 0.0
        Base: SurfaceMesh
            Zone: TestZoneName
                Nodes (5)
                Location: Vertex
                    Fields (1): Pressure
                Elements (0)
# Add another field
sample.add_field(
    "Temperature",
    np.random.randn(len(points)),
    base="SurfaceMesh",
    zone="TestZoneName",
    time=0.0,
)

print(sample.summarize())
Sample Summary:
==================================================
Meshes (1 timestamps):
    Time: 0.0
        Base: SurfaceMesh
            Zone: TestZoneName
                Nodes (5)
                Location: Vertex
                    Fields (2): Pressure, Temperature
                Elements (0)

Access scalars data in Sample

# It will look for a default base if no base and zone are given
print(f"{sample.get_global_names() = }")
print(f"{sample.get_global('omega') = }")
print(f"{sample.get_global('rotation') = }")
sample.get_global_names() = []
sample.get_global('omega') = None
sample.get_global('rotation') = None

Access fields data in Sample

# It will look for a default base if no base and zone are given
print(f"{sample.get_field_names() = }")
print(f"{sample.get_field('T') = }")
print(f"{sample.get_field('Temperature') = }")
sample.get_field_names() = ['Pressure', 'Temperature']
sample.get_field('T') = None
sample.get_field('Temperature') = array([ 0.58177437,  0.29638562, -0.17345189,  0.27042681,  1.23031369])

Access to points coordinates

# It will look for a default base if no base and zone are given
print(f"{sample.get_nodes() = }")
print(f"{sample.get_nodes() = }")
sample.get_nodes() = array([[0. , 0. ],
       [1. , 0. ],
       [1. , 1. ],
       [0. , 1. ],
       [0.5, 1.5]])
sample.get_nodes() = array([[0. , 0. ],
       [1. , 0. ],
       [1. , 1. ],
       [0. , 1. ],
       [0.5, 1.5]])

Retrieve element connectivity data

# Create an empty Sample
tmp_sample = Sample()

# Add the previously created CGNS tree in the Sample
tmp_sample.add_tree(tree)

print("element connectivity = \n", f"{tmp_sample.get_elements()}")
element connectivity = 
 {'TRI_3': array([[0, 1, 2],
       [0, 2, 3],
       [2, 4, 3]])}

Access the available base of the CGNS tree

# Get base names
bases_names = sample.get_base_names()
# Get full base path
full_bases_names = sample.get_base_names(full_path=True)

print(f"{bases_names=}")
print(f"{full_bases_names=}")
bases_names=['SurfaceMesh']
full_bases_names=['/SurfaceMesh']
# Get the first base name
base_name = sample.get_base_names()[0]
# Get base node
base_node_content = sample.get_base(base_name)

print(f"{base_node_content = }")
base_node_content = ['SurfaceMesh', array([2, 3], dtype=int32), [['Time', array([1], dtype=int32), [['IterationValues', array([1], dtype=int32), [], 'DataArray_t'], ['TimeValues', array([0.]), [], 'DataArray_t']], 'BaseIterativeData_t'], ['TestZoneName', array([[5, 3, 0]]), [['ZoneType', array([b'U', b'n', b's', b't', b'r', b'u', b'c', b't', b'u', b'r', b'e',
       b'd'], dtype='|S1'), [], 'ZoneType_t'], ['GridCoordinates', None, [['CoordinateX', array([0. , 1. , 1. , 0. , 0.5]), [], 'DataArray_t'], ['CoordinateY', array([0. , 0. , 1. , 1. , 1.5]), [], 'DataArray_t']], 'GridCoordinates_t'], ['VertexFields', None, [['GridLocation', array([b'V', b'e', b'r', b't', b'e', b'x'], dtype='|S1'), [], 'GridLocation_t'], ['Pressure', array([-1.54146993,  2.65935437,  1.09965842, -0.06759072, -0.3208659 ]), [], 'DataArray_t'], ['Temperature', array([ 0.58177437,  0.29638562, -0.17345189,  0.27042681,  1.23031369]), [], 'DataArray_t']], 'FlowSolution_t']], 'Zone_t']], 'CGNSBase_t']

Check if a base exists in a Sample

# Get the first base name
base_name = sample.get_base_names()[0]

print(f"{sample.has_base(base_name) = }")
print(f"{sample.has_base('unknown_base_name') = }")
sample.has_base(base_name) = True
sample.has_base('unknown_base_name') = False

Access the available zone from a CGNS tree base

# Get the first base name
base_name = sample.get_base_names()[0]

# Get zones associated with the first base
zones_names = sample.get_zone_names(base_name)
# Get full path of zones associated with the first base
full_zones_names = sample.get_zone_names(base_name, full_path=True)

print(f" - Base : {base_name}")
print(f"    - Zone(s): {zones_names}")
print(f"    - Zone(s) full path: {full_zones_names}")
 - Base : SurfaceMesh
    - Zone(s): ['TestZoneName']
    - Zone(s) full path: ['SurfaceMesh/TestZoneName']
# Get the first zone name from a base name
zone_name = zones_names[0]
# Get base node
zone_node_content = sample.get_zone(zone_name, base_name)

print(f"{zone_node_content = }")
zone_node_content = ['TestZoneName', array([[5, 3, 0]]), [['ZoneType', array([b'U', b'n', b's', b't', b'r', b'u', b'c', b't', b'u', b'r', b'e',
       b'd'], dtype='|S1'), [], 'ZoneType_t'], ['GridCoordinates', None, [['CoordinateX', array([0. , 1. , 1. , 0. , 0.5]), [], 'DataArray_t'], ['CoordinateY', array([0. , 0. , 1. , 1. , 1.5]), [], 'DataArray_t']], 'GridCoordinates_t'], ['VertexFields', None, [['GridLocation', array([b'V', b'e', b'r', b't', b'e', b'x'], dtype='|S1'), [], 'GridLocation_t'], ['Pressure', array([-1.54146993,  2.65935437,  1.09965842, -0.06759072, -0.3208659 ]), [], 'DataArray_t'], ['Temperature', array([ 0.58177437,  0.29638562, -0.17345189,  0.27042681,  1.23031369]), [], 'DataArray_t']], 'FlowSolution_t']], 'Zone_t']

Get the zone type

# Get the first zone name from a base name
zone_name = zones_names[0]
z_type = sample.get_zone_type(zone_name, base_name)

print(f"zone type = {z_type}")
zone type = Unstructured

Check if a zone exists in a Sample

# Get the first zone name from a base name
zone_name = zones_names[0]

print(f"{sample.has_zone(zone_name, base_name) = }")
print(f"{sample.has_zone('unknown_zone_name', base_name) = }")
sample.has_zone(zone_name, base_name) = True
sample.has_zone('unknown_zone_name', base_name) = False

Get mesh from sample

sample_mesh = sample.get_tree()
print(sample_mesh)
['CGNSTree', None, [['CGNSLibraryVersion', array([4.], dtype=float32), [], 'CGNSLibraryVersion_t'], ['SurfaceMesh', array([2, 3], dtype=int32), [['Time', array([1], dtype=int32), [['IterationValues', array([1], dtype=int32), [], 'DataArray_t'], ['TimeValues', array([0.]), [], 'DataArray_t']], 'BaseIterativeData_t'], ['TestZoneName', array([[5, 3, 0]]), [['ZoneType', array([b'U', b'n', b's', b't', b'r', b'u', b'c', b't', b'u', b'r', b'e',
       b'd'], dtype='|S1'), [], 'ZoneType_t'], ['GridCoordinates', None, [['CoordinateX', array([0. , 1. , 1. , 0. , 0.5]), [], 'DataArray_t'], ['CoordinateY', array([0. , 0. , 1. , 1. , 1.5]), [], 'DataArray_t']], 'GridCoordinates_t'], ['VertexFields', None, [['GridLocation', array([b'V', b'e', b'r', b't', b'e', b'x'], dtype='|S1'), [], 'GridLocation_t'], ['Pressure', array([-1.54146993,  2.65935437,  1.09965842, -0.06759072, -0.3208659 ]), [], 'DataArray_t'], ['Temperature', array([ 0.58177437,  0.29638562, -0.17345189,  0.27042681,  1.23031369]), [], 'DataArray_t']], 'FlowSolution_t']], 'Zone_t']], 'CGNSBase_t']], 'CGNSTree_t']

Get all mesh time available in Sample

# Before adding new tree
print(f"{sample.get_all_time_values() = }")

# Add one CGNS tree at time 1.
sample.add_tree(tree, 1.0)

# After adding new tree
print(f"{sample.get_all_time_values() = }")
sample.get_all_time_values() = [0.0]
sample.get_all_time_values() = [0.0, 1.0]

Creating a Sample Hierarchy with bases, zones, and associated data.

bases_names = sample.get_base_names()
full_bases_names = sample.get_base_names(full_path=True)
print(f"{bases_names = }")
print(f"{full_bases_names = }", end="\n\n")

for b_name in bases_names:
    zones_names = sample.get_zone_names(b_name)
    full_zones_names = sample.get_zone_names(b_name, full_path=True)
    print(f" - Base : {b_name}")
    for z_name, f_z_name in zip(zones_names, full_zones_names):
        print(
            f"    - {z_name} -> type: {sample.get_zone_type(z_name, b_name)} | full: {f_z_name}"
        )
bases_names = ['SurfaceMesh']
full_bases_names = ['/SurfaceMesh']

 - Base : SurfaceMesh
    - TestZoneName -> type: Unstructured | full: SurfaceMesh/TestZoneName

Section 2.3: Set and Get default values

This section demonstrates how to use default CGNS values in a Sample.

Set and use default time in a Sample

# Without a provided default time, it searches the first time available in all mesh times
print(f"{sample.get_all_time_values() = }")
print(f"{sample.resolve_time() = }", end="\n\n")

# Set default time
sample.set_default_time(1.0)
# Now that default time has been assigned, there's no need to specify it in function calls.
print(f"{sample.resolve_time() = }", end="\n\n")

# Print the tree at time 1.0
sample.show_tree()  # == sample.show_tree(1.0)
sample.get_all_time_values() = [0.0, 1.0]
sample.resolve_time() = 0.0

sample.resolve_time() = 1.0

 CGNSLibraryVersion : (1,) [4.] float32 CGNSLibraryVersion_t
 Base_2_2 : (2,) [2 2] int32 CGNSBase_t
|_  2D : None Family_t
|_  Zone : (1, 3) [[5 3 0]] int64 Zone_t
   |_  ZoneType : (12,) Unstructured |S1 ZoneType_t
   |_  GridCoordinates : None GridCoordinates_t
      |_  CoordinateX : (5,) [0.  1.  1.  0.  0.5] float64 DataArray_t
      |_  CoordinateY : (5,) [0.  0.  1.  1.  1.5] float64 DataArray_t
   |_  Elements_TRI_3 : (2,) [5 0] int32 Elements_t
      |_  ElementRange : (2,) [1 3] int64 IndexRange_t
      |_  ElementConnectivity : (9,) [1 ... 4] int64 DataArray_t
   |_  VertexFields : None FlowSolution_t
      |_  GridLocation : (6,) Vertex |S1 GridLocation_t
      |_  OriginalIds : (5,) [1 2 3 4 5] int64 DataArray_t
      |_  test_node_field_1 : (5,) [ 1.57228445 -0.79812797 -0.74235336 -0.28304151 -1.1860188 ] float64 DataArray_t
   |_  CellCenterFields : None FlowSolution_t
      |_  GridLocation : (10,) CellCenter |S1 GridLocation_t
      |_  OriginalIds : (3,) [1 2 3] int64 DataArray_t
      |_  test_elem_field_1 : (3,) [-1.10621078  0.29662337 -1.18089037] float64 DataArray_t
   |_  FamilyName : (2,) 2D |S1 FamilyName_t
|_  Time : (1,) [1] int32 BaseIterativeData_t
   |_  IterationValues : (1,) [1] int32 DataArray_t
   |_  TimeValues : (1,) [0.] float64 DataArray_t
# If time is specified as an argument in a function, it takes precedence over the default time.
sample.show_tree(0.0)  # Print the tree at time 0.0 even if default time is 1.0
 CGNSLibraryVersion : (1,) [4.] float32 CGNSLibraryVersion_t
 SurfaceMesh : (2,) [2 3] int32 CGNSBase_t
|_  Time : (1,) [1] int32 BaseIterativeData_t
   |_  IterationValues : (1,) [1] int32 DataArray_t
   |_  TimeValues : (1,) [0.] float64 DataArray_t
|_  TestZoneName : (1, 3) [[5 3 0]] int64 Zone_t
   |_  ZoneType : (12,) Unstructured |S1 ZoneType_t
   |_  GridCoordinates : None GridCoordinates_t
      |_  CoordinateX : (5,) [0.  1.  1.  0.  0.5] float64 DataArray_t
      |_  CoordinateY : (5,) [0.  0.  1.  1.  1.5] float64 DataArray_t
   |_  VertexFields : None FlowSolution_t
      |_  GridLocation : (6,) Vertex |S1 GridLocation_t
      |_  Pressure : (5,) [-1.54146993  2.65935437  1.09965842 -0.06759072 -0.3208659 ] float64 DataArray_t
      |_  Temperature : (5,) [ 0.58177437  0.29638562 -0.17345189  0.27042681  1.23031369] float64 DataArray_t

Set and use default base and time in a Sample

# Reset default time
sample._default_active_time = None

# Without a provided default time, it searches the first time available in all mesh times
print(f"{sample.resolve_time() = }", end="\n\n")

# Create new bases
sample.init_base(1, 1, "new_base", 0.0)
print(f"{sample.get_topological_dim('new_base', 0.0) = }")
print(f"{sample.get_physical_dim('new_base', 0.0) = }")
sample.resolve_time() = 1.0

sample.get_topological_dim('new_base', 0.0) = 1
sample.get_physical_dim('new_base', 0.0) = 1
# Attempting to get a base when the default base is not set, and there are multiple bases available.
print(f"{sample.get_base_names() = }", end="\n\n")
try:
    sample.resolve_base()
except KeyError as e:
    print(str(e))
sample.get_base_names() = ['Base_2_2']
# Set default base and time
sample.set_default_base("SurfaceMesh", 0.0)

# Now that default base and time have been assigned, it is no longer necessary to specify them in function calls.
print(f"{sample.resolve_time() = }")
print(f"{sample.resolve_base() = }", end="\n\n")

# Print the topological and physical dim for the default base == 'SurfaceMesh'
print(f"{sample.get_topological_dim() = }")
print(f"{sample.get_physical_dim() = }")
sample.resolve_time() = 0.0
sample.resolve_base() = 'SurfaceMesh'

sample.get_topological_dim() = 2
sample.get_physical_dim() = 3
# If base is specified as an argument in a function, it takes precedence over the default base.
print(
    f"{sample.get_physical_dim('new_base') = }"
)  # Print the 'new_base' physical dim instead of the default base physical dim
sample.get_physical_dim('new_base') = 1

Set and use default base, zone and time in a Sample

import CGNS.PAT.cgnskeywords as CGK

# Reset default base and time
sample._default_active_time = None
sample._default_active_base = None

# Without a provided default time, it searches the first time available in all mesh times
print(f"{sample.resolve_time() = }", end="\n\n")

# Create a new zone in 'SurfaceMesh' base
sample.init_zone(
    zone_shape=np.array([[5, 3, 0]]),
    zone_type=CGK.Structured_s,
    zone="new_zone",
    base="SurfaceMesh",
)
print(f"{sample.get_zone_type('TestZoneName', 'SurfaceMesh') = }")
print(f"{sample.get_zone_type('new_zone', 'SurfaceMesh') = }")
sample.resolve_time() = 0.0

sample.get_zone_type('TestZoneName', 'SurfaceMesh') = 'Unstructured'
sample.get_zone_type('new_zone', 'SurfaceMesh') = 'Structured'
# Set default base
sample.set_default_base("SurfaceMesh")

# Attempting to get a zone when the default zone is not set, and there are multiple zones available in the default base.
print(f"{sample.get_zone_names() = }", end="\n\n")
try:
    sample.resolve_zone()
except KeyError as e:
    print(str(e))
sample.get_zone_names() = ['TestZoneName', 'new_zone']

"No default zone provided among ['TestZoneName', 'new_zone'] in the default base: SurfaceMesh"
# Reset default base and time
sample._default_active_time = None
sample._default_active_base = None

# Set default base, zone and time
sample.set_default_zone_base("TestZoneName", "SurfaceMesh", 0.0)

# Now that default base, zone and time have been assigned, it is no longer necessary to specify them in function calls.
print(f"{sample.resolve_time() = }")
print(f"{sample.resolve_base() = }")
print(f"{sample.resolve_zone() = }", end="\n\n")

# Print the type of the default zone (from the default base)
print(f"{sample.get_zone_type() = }")

# Print the default zone content (from the default base)
print(f"{sample.get_zone() = }")
sample.resolve_time() = 0.0
sample.resolve_base() = 'SurfaceMesh'
sample.resolve_zone() = 'TestZoneName'

sample.get_zone_type() = 'Unstructured'
sample.get_zone() = ['TestZoneName', array([[5, 3, 0]]), [['ZoneType', array([b'U', b'n', b's', b't', b'r', b'u', b'c', b't', b'u', b'r', b'e',
       b'd'], dtype='|S1'), [], 'ZoneType_t'], ['GridCoordinates', None, [['CoordinateX', array([0. , 1. , 1. , 0. , 0.5]), [], 'DataArray_t'], ['CoordinateY', array([0. , 0. , 1. , 1. , 1.5]), [], 'DataArray_t']], 'GridCoordinates_t'], ['VertexFields', None, [['GridLocation', array([b'V', b'e', b'r', b't', b'e', b'x'], dtype='|S1'), [], 'GridLocation_t'], ['Pressure', array([-1.54146993,  2.65935437,  1.09965842, -0.06759072, -0.3208659 ]), [], 'DataArray_t'], ['Temperature', array([ 0.58177437,  0.29638562, -0.17345189,  0.27042681,  1.23031369]), [], 'DataArray_t']], 'FlowSolution_t']], 'Zone_t']
# If zone is specified as an argument in a function, it takes precedence over the default zone.
print(
    f"{sample.get_zone_type('new_zone') = }"
)  # Print the 'new_zone' type instead of the default zone type
sample.get_zone_type('new_zone') = 'Structured'

More information on how default values work

from IPython.display import Image

try:
    filename = (
        Path(__file__).parent.parent.parent
        / "docs"
        / "source"
        / "images"
        / "default_value_selection.png"
    )
except NameError:
    filename = Path("..") / ".." / "images" / "default_value_selection.png"
Image(filename=filename)

png

Section 2.4: Saving and Loading Sample

This section demonstrates how to save and load a Sample from a directory.

Save Sample to as a file tree

test_pth = Path(
    f"/tmp/test_safe_to_delete_{np.random.randint(low=1, high=2_000_000_000)}"
)
test_pth.mkdir(parents=True, exist_ok=True)

sample_save_fname = test_pth / "test"
print(f"saving path: {sample_save_fname}")

sample.save_to_dir(sample_save_fname)
saving path: /tmp/test_safe_to_delete_861754285/test

Load a Sample from a directory via the Sample class

new_sample_from_dir = Sample.load_from_dir(sample_save_fname)

print(new_sample_from_dir.summarize())
Sample Summary:
==================================================
Meshes (1 timestamps):
    Time: 0.0
        Base: Base_2_2
            Zone: Zone
                Nodes (5)
                Location: Vertex
                    Fields (2): OriginalIds, test_node_field_1
                Location: CellCenter
                    Fields (2): OriginalIds, test_elem_field_1
                Elements (3)
                    TRI_3 (3)

Load the Sample from a directory via a Sample instance

new_sample_via_instance = Sample()
new_sample_via_instance.load(sample_save_fname)
print(new_sample_via_instance.summarize())
Sample Summary:
==================================================
Meshes (1 timestamps):
    Time: 0.0
        Base: Base_2_2
            Zone: Zone
                Nodes (5)
                Location: Vertex
                    Fields (2): OriginalIds, test_node_field_1
                Location: CellCenter
                    Fields (2): OriginalIds, test_elem_field_1
                Elements (3)
                    TRI_3 (3)