@@ -2738,7 +2738,8 @@ class UnstructuredMesh(MeshBase):
27382738 _UNSUPPORTED_ELEM = - 1
27392739 _LINEAR_TET = 0
27402740 _LINEAR_HEX = 1
2741- _VTK_TETRA = 10
2741+ _VTK_TET = 10
2742+ _VTK_HEX = 12
27422743
27432744 def __init__ (self , filename : PathLike , library : str , mesh_id : int | None = None ,
27442745 name : str = '' , length_multiplier : float = 1.0 ,
@@ -3049,7 +3050,9 @@ def _write_data_to_vtk_ascii_format(
30493050 n_skipped += 1
30503051 continue
30513052 else :
3052- raise RuntimeError (f"Invalid element type { elem_type } found" )
3053+ raise RuntimeError (
3054+ f"Invalid element type { elem_type } found in mesh { self .id } "
3055+ )
30533056
30543057 for i , c in enumerate (conn ):
30553058 if c == - 1 :
@@ -3106,35 +3109,42 @@ def _write_data_to_vtk_hdf5_format(
31063109 datasets : dict | None = None ,
31073110 volume_normalization : bool = True ,
31083111 ):
3109- def append_dataset (dset , array ):
3110- """Convenience function to append data to an HDF5 dataset"""
3111- origLen = dset .shape [0 ]
3112- dset .resize (origLen + array .shape [0 ], axis = 0 )
3113- dset [origLen :] = array
3114-
3115- if self .library != "moab" :
3116- raise NotImplementedError ("VTKHDF output is only supported for MOAB meshes" )
3117-
3118- # the self.connectivity contains arrays of length 8 to support hex
3119- # elements as well, in the case of tetrahedra mesh elements, the
3120- # last 4 values are -1 and are removed
3121- trimmed_connectivity = []
3122- for cell in self .connectivity :
3123- # Find the index of the first -1 value, if any
3124- first_negative_index = np .where (cell == - 1 )[0 ]
3125- if first_negative_index .size > 0 :
3126- # Slice the array up to the first -1 value
3127- trimmed_connectivity .append (cell [: first_negative_index [0 ]])
3112+ # This writer supports linear tetrahedra and linear hexahedra elements
3113+ conn_list = [] # flattened connectivity ids
3114+ cell_sizes = [] # number of points per cell
3115+ vtk_types = [] # VTK cell types per cell (uint8)
3116+ n_skipped = 0
3117+
3118+ for conn , etype in zip (self .connectivity , self .element_types ):
3119+ if etype == self ._LINEAR_TET :
3120+ ids = conn [:4 ]
3121+ vtk_types .append (self ._VTK_TET )
3122+ elif etype == self ._LINEAR_HEX :
3123+ ids = conn [:8 ]
3124+ vtk_types .append (self ._VTK_HEX )
3125+ elif etype == self ._UNSUPPORTED_ELEM :
3126+ n_skipped += 1
3127+ continue
31283128 else :
3129- # No -1 values, append the whole cell
3130- trimmed_connectivity .append (cell )
3131- trimmed_connectivity = np .array (trimmed_connectivity , dtype = "int32" ).flatten ()
3129+ raise RuntimeError (
3130+ f"Invalid element type { etype } found in mesh { self .id } "
3131+ )
3132+ conn_list .extend (ids .tolist ())
3133+ cell_sizes .append (len (ids ))
31323134
3133- # MOAB meshes supports tet elements only so we know it has 4 points per cell
3134- points_per_cell = 4
3135+ if n_skipped > 0 :
3136+ warnings .warn (
3137+ f"{ n_skipped } elements were not written because "
3138+ "they are not of type linear tet/hex"
3139+ )
31353140
3136- # offsets are the indices of the first point of each cell in the array of points
3137- offsets = np .arange (0 , self .n_elements * points_per_cell + 1 , points_per_cell )
3141+ connectivity = np .asarray (conn_list , dtype = np .int64 )
3142+
3143+ # Offsets must be length (numCells + 1) with a leading 0 and
3144+ # cumulative end-indices thereafter, per VTK's layout
3145+ cell_sizes_arr = np .asarray (cell_sizes , dtype = np .int64 )
3146+ offsets = np .zeros (cell_sizes_arr .size + 1 , dtype = np .int64 )
3147+ np .cumsum (cell_sizes_arr , out = offsets [1 :])
31383148
31393149 for name , data in datasets .items ():
31403150 if data .shape != self .dimension :
@@ -3156,42 +3166,27 @@ def append_dataset(dset, array):
31563166 dtype = h5py .string_dtype ("ascii" , len (ascii_type )),
31573167 )
31583168
3159- # create hdf5 file structure
3160- root .create_dataset ("NumberOfPoints" , (0 ,), maxshape = (None ,), dtype = "i8" )
3161- root .create_dataset ("Types" , (0 ,), maxshape = (None ,), dtype = "uint8" )
3162- root .create_dataset ("Points" , (0 , 3 ), maxshape = (None , 3 ), dtype = "f" )
3163- root .create_dataset (
3164- "NumberOfConnectivityIds" , (0 ,), maxshape = (None ,), dtype = "i8"
3165- )
3166- root .create_dataset ("NumberOfCells" , (0 ,), maxshape = (None ,), dtype = "i8" )
3167- root .create_dataset ("Offsets" , (0 ,), maxshape = (None ,), dtype = "i8" )
3168- root .create_dataset ("Connectivity" , (0 ,), maxshape = (None ,), dtype = "i8" )
3169-
3170- append_dataset (root ["NumberOfPoints" ], np .array ([len (self .vertices )]))
3171- append_dataset (root ["Points" ], self .vertices )
3172- append_dataset (
3173- root ["NumberOfConnectivityIds" ],
3174- np .array ([len (trimmed_connectivity )]),
3175- )
3176- append_dataset (root ["Connectivity" ], trimmed_connectivity )
3177- append_dataset (root ["NumberOfCells" ], np .array ([self .n_elements ]))
3178- append_dataset (root ["Offsets" ], offsets )
3169+ # Create HDF5 file structure compliant with VTKHDF UnstructuredGrid
3170+ n_points = int (len (self .vertices ))
3171+ n_cells = int (len (cell_sizes ))
3172+ n_conn_ids = int (len (connectivity ))
31793173
3180- append_dataset (
3181- root ["Types" ], np .full (self .n_elements , self ._VTK_TETRA , dtype = "uint8" )
3182- )
3174+ root .create_dataset ("NumberOfPoints" , data = (n_points ,), dtype = "i8" )
3175+ root .create_dataset ("NumberOfCells" , data = (n_cells ,), dtype = "i8" )
3176+ root .create_dataset ("NumberOfConnectivityIds" , data = (n_conn_ids ,), dtype = "i8" )
3177+ root .create_dataset ("Points" , data = self .vertices .astype (np .float64 , copy = False ), dtype = "f8" )
3178+ root .create_dataset ("Types" , data = np .asarray (vtk_types , dtype = np .uint8 ), dtype = "uint8" )
3179+ root .create_dataset ("Offsets" , data = offsets .astype ("i8" ), dtype = "i8" )
3180+ root .create_dataset ("Connectivity" , data = connectivity .astype ("i8" ), dtype = "i8" )
31833181
31843182 cell_data_group = root .create_group ("CellData" )
31853183
31863184 for name , data in datasets .items ():
3187-
3188- cell_data_group .create_dataset (
3189- name , (0 ,), maxshape = (None ,), dtype = "float64" , chunks = True
3190- )
3191-
31923185 if volume_normalization :
31933186 data /= self .volumes
3194- append_dataset (cell_data_group [name ], data )
3187+ cell_data_group .create_dataset (
3188+ name , data = data , dtype = "float64" , chunks = True
3189+ )
31953190
31963191 @classmethod
31973192 def from_hdf5 (cls , group : h5py .Group , mesh_id : int , name : str ):
0 commit comments