def save(cls, network, phases=[], filename='', delim=' | ', fill_nans=None, fill_infs=None): r""" Save network and phase data to a single vtp file for visualizing in Paraview Parameters ---------- network : OpenPNM Network Object The Network containing the data to be written phases : list, optional A list containing OpenPNM Phase object(s) containing data to be written filename : string, optional Filename to write data. If no name is given the file is named after the network delim : string Specify which character is used to delimit the data names. The default is ' | ' which creates a nice clean output in the Paraview pipeline viewer (e.g. net | property | pore | diameter) fill_nans : scalar The value to use to replace NaNs with. The VTK file format does not work with NaNs, so they must be dealt with. The default is `None` which means property arrays with NaNs are not written to the file. Other useful options might be 0 or -1, but the user must be aware that these are not real values, only place holders. fill_infs : scalar The value to use to replace infs with. The default is ``None`` which means that property arrays containing ``None`` will *not* be written to the file, and a warning will be issued. A useful value is """ project, network, phases = cls._parse_args(network=network, phases=phases) # Check if any of the phases has time series transient = GenericIO.is_transient(phases=phases) if transient: logger.warning('vtp format does not support transient data, ' + 'use xdmf instead') if filename == '': filename = project.name filename = cls._parse_filename(filename=filename, ext='vtp') am = Dict.to_dict(network=network, phases=phases, interleave=True, categorize_by=['object', 'data']) am = FlatDict(am, delimiter=delim) key_list = list(sorted(am.keys())) network = network[0] points = network['pore.coords'] pairs = network['throat.conns'] num_points = np.shape(points)[0] num_throats = np.shape(pairs)[0] root = ET.fromstring(VTK._TEMPLATE) piece_node = root.find('PolyData').find('Piece') piece_node.set("NumberOfPoints", str(num_points)) piece_node.set("NumberOfLines", str(num_throats)) points_node = piece_node.find('Points') coords = VTK._array_to_element("coords", points.T.ravel('F'), n=3) points_node.append(coords) lines_node = piece_node.find('Lines') connectivity = VTK._array_to_element("connectivity", pairs) lines_node.append(connectivity) offsets = VTK._array_to_element("offsets", 2*np.arange(len(pairs))+2) lines_node.append(offsets) point_data_node = piece_node.find('PointData') cell_data_node = piece_node.find('CellData') for key in key_list: array = am[key] if array.dtype == 'O': logger.warning(key + ' has dtype object,' + ' will not write to file') else: if array.dtype == np.bool: array = array.astype(int) if np.any(np.isnan(array)): if fill_nans is None: logger.warning(key + ' has nans,' + ' will not write to file') continue else: array[np.isnan(array)] = fill_nans if np.any(np.isinf(array)): if fill_infs is None: logger.warning(key + ' has infs,' + ' will not write to file') continue else: array[np.isinf(array)] = fill_infs element = VTK._array_to_element(key, array) if (array.size == num_points): point_data_node.append(element) elif (array.size == num_throats): cell_data_node.append(element) tree = ET.ElementTree(root) tree.write(filename) with open(filename, 'r+') as f: string = f.read() string = string.replace('</DataArray>', '</DataArray>\n\t\t\t') f.seek(0) # consider adding header: '<?xml version="1.0"?>\n'+ f.write(string)
def save(cls, network, phases=[], filename=''): r""" Saves (transient/steady-state) data from the given objects into the specified file. Parameters ---------- network : OpenPNM Network Object The network containing the desired data phases : list of OpenPNM Phase Objects (optional, default is none) A list of phase objects whose data are to be included Notes ----- This method only saves the data, not any of the pore-scale models or other attributes. To save an actual OpenPNM Project use the ``Workspace`` object. """ project, network, phases = cls._parse_args(network=network, phases=phases) network = network[0] # Check if any of the phases has time series transient = GenericIO.is_transient(phases=phases) if filename == '': filename = project.name path = cls._parse_filename(filename=filename, ext='xmf') # Path is a pathlib object, so slice it up as needed fname_xdf = path.name d = Dict.to_dict(network, phases=phases, interleave=True, flatten=False, categorize_by=['element', 'data']) D = FlatDict(d, delimiter='/') # Identify time steps t_steps = [] if transient: for key in D.keys(): if '@' in key: t_steps.append(key.split('@')[1]) t_grid = create_grid(Name="TimeSeries", GridType="Collection", CollectionType="Temporal") # If steady-state, define '0' time step if not transient: t_steps.append('0') # Setup xdmf file root = create_root('Xdmf') domain = create_domain() # Iterate over time steps present for t in range(len(t_steps)): # Define the hdf file if not transient: fname_hdf = path.stem+".hdf" else: fname_hdf = path.stem+'@'+t_steps[t]+".hdf" path_p = path.parent f = h5py.File(path_p.joinpath(fname_hdf), "w") # Add coordinate and connection information to top of HDF5 file f["coordinates"] = network["pore.coords"] f["connections"] = network["throat.conns"] # geometry coordinates row, col = f["coordinates"].shape dims = ' '.join((str(row), str(col))) hdf_loc = fname_hdf + ":coordinates" geo_data = create_data_item(value=hdf_loc, Dimensions=dims, Format='HDF', DataType="Float") geo = create_geometry(GeometryType="XYZ") geo.append(geo_data) # topolgy connections row, col = f["connections"].shape # col first then row dims = ' '.join((str(row), str(col))) hdf_loc = fname_hdf + ":connections" topo_data = create_data_item(value=hdf_loc, Dimensions=dims, Format="HDF", NumberType="Int") topo = create_topology(TopologyType="Polyline", NodesPerElement=str(2), NumberOfElements=str(row)) topo.append(topo_data) # Make HDF5 file with all datasets, and no groups for item in D.keys(): if D[item].dtype == 'O': logger.warning(item + ' has dtype object,' + ' will not write to file') del D[item] elif 'U' in str(D[item][0].dtype): pass elif ('@' in item and t_steps[t] == item.split('@')[1]): f.create_dataset(name='/'+item.split('@')[0]+'@t', shape=D[item].shape, dtype=D[item].dtype, data=D[item]) elif ('@' not in item and t == 0): f.create_dataset(name='/'+item, shape=D[item].shape, dtype=D[item].dtype, data=D[item]) # Create a grid grid = create_grid(Name=t_steps[t], GridType="Uniform") time = create_time(type='Single', Value=t_steps[t]) grid.append(time) # Add pore and throat properties for item in D.keys(): if item not in ['coordinates', 'connections']: if (('@' in item and t_steps[t] == item.split('@')[1]) or ('@' not in item)): attr_type = 'Scalar' shape = D[item].shape dims = (''.join([str(i) + ' ' for i in list(shape)[::-1]])) if '@' in item: item = item.split('@')[0]+'@t' hdf_loc = fname_hdf + ":" + item elif ('@' not in item and t == 0): hdf_loc = fname_hdf + ":" + item elif ('@' not in item and t > 0): hdf_loc = (path.stem+'@'+t_steps[0]+".hdf" + ":" + item) attr = create_data_item(value=hdf_loc, Dimensions=dims, Format='HDF', Precision='8', DataType='Float') name = item.replace('/', ' | ') if 'throat' in item: Center = "Cell" else: Center = "Node" el_attr = create_attribute(Name=name, Center=Center, AttributeType=attr_type) el_attr.append(attr) grid.append(el_attr) else: pass grid.append(topo) grid.append(geo) t_grid.append(grid) # CLose the HDF5 file f.close() domain.append(t_grid) root.append(domain) with open(path_p.joinpath(fname_xdf), 'w') as file: file.write(cls._header) file.write(ET.tostring(root).decode("utf-8"))
def save(cls, network, phases=[], filename='', delim=' | ', fill_nans=None, fill_infs=None): r""" Save network and phase data to a single vtp file for visualizing in Paraview Parameters ---------- network : OpenPNM Network Object The Network containing the data to be written phases : list, optional A list containing OpenPNM Phase object(s) containing data to be written filename : string, optional Filename to write data. If no name is given the file is named after the network delim : string Specify which character is used to delimit the data names. The default is ' | ' which creates a nice clean output in the Paraview pipeline viewer (e.g. net | property | pore | diameter) fill_nans : scalar The value to use to replace NaNs with. The VTK file format does not work with NaNs, so they must be dealt with. The default is `None` which means property arrays with NaNs are not written to the file. Other useful options might be 0 or -1, but the user must be aware that these are not real values, only place holders. fill_infs : scalar The value to use to replace infs with. The default is ``None`` which means that property arrays containing ``None`` will *not* be written to the file, and a warning will be issued. A useful value is """ project, network, phases = cls._parse_args(network=network, phases=phases) # Check if any of the phases has time series transient = GenericIO.is_transient(phases=phases) if transient: logger.warning('vtp format does not support transient data, ' + 'use xdmf instead') if filename == '': filename = project.name filename = cls._parse_filename(filename=filename, ext='vtp') am = Dict.to_dict(network=network, phases=phases, interleave=True, categorize_by=['object', 'data']) am = FlatDict(am, delimiter=delim) key_list = list(sorted(am.keys())) network = network[0] points = network['pore.coords'] pairs = network['throat.conns'] num_points = np.shape(points)[0] num_throats = np.shape(pairs)[0] root = ET.fromstring(VTK._TEMPLATE) piece_node = root.find('PolyData').find('Piece') piece_node.set("NumberOfPoints", str(num_points)) piece_node.set("NumberOfLines", str(num_throats)) points_node = piece_node.find('Points') coords = VTK._array_to_element("coords", points.T.ravel('F'), n=3) points_node.append(coords) lines_node = piece_node.find('Lines') connectivity = VTK._array_to_element("connectivity", pairs) lines_node.append(connectivity) offsets = VTK._array_to_element("offsets", 2 * np.arange(len(pairs)) + 2) lines_node.append(offsets) point_data_node = piece_node.find('PointData') cell_data_node = piece_node.find('CellData') for key in key_list: array = am[key] if array.dtype == 'O': logger.warning(key + ' has dtype object,' + ' will not write to file') else: if array.dtype == np.bool: array = array.astype(int) if np.any(np.isnan(array)): if fill_nans is None: logger.warning(key + ' has nans,' + ' will not write to file') continue else: array[np.isnan(array)] = fill_nans if np.any(np.isinf(array)): if fill_infs is None: logger.warning(key + ' has infs,' + ' will not write to file') continue else: array[np.isinf(array)] = fill_infs element = VTK._array_to_element(key, array) if (array.size == num_points): point_data_node.append(element) elif (array.size == num_throats): cell_data_node.append(element) tree = ET.ElementTree(root) tree.write(filename) with open(filename, 'r+') as f: string = f.read() string = string.replace('</DataArray>', '</DataArray>\n\t\t\t') f.seek(0) # consider adding header: '<?xml version="1.0"?>\n'+ f.write(string)