Module i2pp.core.discretization_readers.lnmmeshio_reader
Import discretizations readable by lnmmeshio (e.g. .4C.yaml, .vtu).
Classes
class LnmmeshioReader-
Expand source code
class LnmmeshioReader(DiscretizationReader): """Class for reading and processing finite element models with lnmmeshio. This class extends `DiscretizationReader` to handle all formats readable by `lnmmeshio`, e.g. `.4C.yaml` input files or `.vtu` meshes (such as line element airway trees). It provides functionality to import the Discretization, filter elements by an element field, and structure the data into a `Discretization` object. """ def _is_selected(self, ele, element_filter: dict) -> bool: """Checks whether an element passes the element filter. The filter field is looked up in the element data (e.g. VTU cell data such as `block_id`) and then in the element options (e.g. the material `MAT`). Arguments: ele: The lnmmeshio element. element_filter (dict): Dictionary with the keys `field` (name of the element data field or option) and `values` (list of values to keep). Returns: bool: True if the value of the field is one of the filter values. Raises: ValueError: If the element has no data field or option with the given name. """ field = element_filter["field"] if field in ele.data: value = ele.data[field] elif field in ele.options: value = ele.options[field] else: available = ", ".join([*ele.data, *ele.options]) raise ValueError( f"Element filter field '{field}' not found in the element " f"data or options. Available fields: {available}." ) # options such as the material may be stored as strings if isinstance(value, str): try: value = int(value) except ValueError: pass return value in element_filter["values"] def _filter_discretization( self, dis: LnmmeshioDiscretization, element_filter: dict, ) -> LnmmeshioDiscretization: """Filters the discretization to include only elements whose filter field has one of the specified values. This function iterates through the elements in the discretization and selects only those whose element data field or option (e.g. `block_id` or `MAT`) matches one of the values of `element_filter`. The corresponding nodes of these elements are also retained. After filtering, the nodes are sorted based on their IDs. Arguments: dis (LnmmeshioDiscretization): The discretization. element_filter (dict): Dictionary with the keys `field` and `values` to filter elements by. Returns: LnmmeshioDiscretization: The filtered discretization containing only the selected elements and nodes. """ dis.compute_ids(zero_based=True) filtered_nodes = set() filtered_elements = [] for ele in tqdm(dis.elements.structure, desc="Filtering elements"): if self._is_selected(ele, element_filter): for n in ele.nodes: filtered_nodes.add(n) filtered_elements.append(ele) sorted_nodes = sorted(filtered_nodes, key=lambda x: x.id) dis.elements.structure = filtered_elements dis.nodes = list(sorted_nodes) dis.compute_ids(zero_based=True) return dis def load_discretization( self, file_path: Path, options: dict, processing: Processing, ) -> Discretization: """Loads and processes a finite element discretization with lnmmeshio. This function imports nodes and elements from a file readable by `lnmmeshio` (e.g. .4C.yaml or .vtu), applies optional element filtering, and organizes the data into a `Discretization` object. Arguments: file_path (Path): Path to the discretization file. options (dict): Options for loading the discretization. Filtering of elements (e.g. by the VTU cell data `block_id` or the material `MAT`) can be enabled by specifying `element_filter` with the keys `field` and `values`. processing (I2PPConfig.processing): Processing configuration object. Returns: Discretization: The finite element discretization including nodes and elements. """ logging.info("Importing discretization data") if processing is None: raise ValueError( "Processing configuration is required" "for loading the discretization." ) raw_dis = lnmmeshio.read(str(file_path)) raw_dis.compute_ids(zero_based=True) # remember the position of each element in the input file, since # filtering renumbers the element ids source_ids = {ele: ele.id for ele in raw_dis.elements.structure} element_filter = (options or {}).get("element_filter") if element_filter is not None: if "field" not in element_filter or "values" not in element_filter: raise ValueError( "The discretization option 'element_filter' requires the " "keys 'field' and 'values'." ) raw_dis = self._filter_discretization(raw_dis, element_filter) if not raw_dis.elements.structure: raise RuntimeError( "No elements left in the discretization after filtering." ) scaling_factors = processing.interpolation.node_scaling_factors interior_node_scaling = scaling_factors.interior_node_scaling surface_node_scaling = scaling_factors.surface_node_scaling nodes_coords = [] node_ids = [] nodes_scaling = [] for node in raw_dis.nodes: nodes_coords.append(node.coords) node_ids.append(node.id) nodes_scaling.append(interior_node_scaling) elements = [] for ele in raw_dis.elements.structure: ele_node_ids = [] for node in ele.nodes: ele_node_ids.append(node.id) elements.append( Element( node_ids=np.array(ele_node_ids), id=ele.id, source_id=source_ids[ele], fields=dict(ele.data), ) ) surfaces = [] node_id_to_idx = {nid: i for i, nid in enumerate(node_ids)} for surf in raw_dis.surfacenodesets: surf_node_ids = [] for node in surf.nodes: surf_node_ids.append(node.id) position = node_id_to_idx[node.id] nodes_scaling[position] = surface_node_scaling surfaces.append( Surface(node_ids=np.array(surf_node_ids), id=surf.id) ) dis = Discretization( nodes=Nodes( coords=np.array(nodes_coords), ids=np.array(node_ids), scaling_factors=np.array(nodes_scaling), ), elements=elements, surfaces=surfaces, ) return disClass for reading and processing finite element models with lnmmeshio.
This class extends
DiscretizationReaderto handle all formats readable bylnmmeshio, e.g..4C.yamlinput files or.vtumeshes (such as line element airway trees). It provides functionality to import the Discretization, filter elements by an element field, and structure the data into aDiscretizationobject.Init DiscretizationReader.
Ancestors
- DiscretizationReader
- abc.ABC
Methods
def load_discretization(self, file_path: Path, options: dict, processing: Processing) ‑> Discretization-
Expand source code
def load_discretization( self, file_path: Path, options: dict, processing: Processing, ) -> Discretization: """Loads and processes a finite element discretization with lnmmeshio. This function imports nodes and elements from a file readable by `lnmmeshio` (e.g. .4C.yaml or .vtu), applies optional element filtering, and organizes the data into a `Discretization` object. Arguments: file_path (Path): Path to the discretization file. options (dict): Options for loading the discretization. Filtering of elements (e.g. by the VTU cell data `block_id` or the material `MAT`) can be enabled by specifying `element_filter` with the keys `field` and `values`. processing (I2PPConfig.processing): Processing configuration object. Returns: Discretization: The finite element discretization including nodes and elements. """ logging.info("Importing discretization data") if processing is None: raise ValueError( "Processing configuration is required" "for loading the discretization." ) raw_dis = lnmmeshio.read(str(file_path)) raw_dis.compute_ids(zero_based=True) # remember the position of each element in the input file, since # filtering renumbers the element ids source_ids = {ele: ele.id for ele in raw_dis.elements.structure} element_filter = (options or {}).get("element_filter") if element_filter is not None: if "field" not in element_filter or "values" not in element_filter: raise ValueError( "The discretization option 'element_filter' requires the " "keys 'field' and 'values'." ) raw_dis = self._filter_discretization(raw_dis, element_filter) if not raw_dis.elements.structure: raise RuntimeError( "No elements left in the discretization after filtering." ) scaling_factors = processing.interpolation.node_scaling_factors interior_node_scaling = scaling_factors.interior_node_scaling surface_node_scaling = scaling_factors.surface_node_scaling nodes_coords = [] node_ids = [] nodes_scaling = [] for node in raw_dis.nodes: nodes_coords.append(node.coords) node_ids.append(node.id) nodes_scaling.append(interior_node_scaling) elements = [] for ele in raw_dis.elements.structure: ele_node_ids = [] for node in ele.nodes: ele_node_ids.append(node.id) elements.append( Element( node_ids=np.array(ele_node_ids), id=ele.id, source_id=source_ids[ele], fields=dict(ele.data), ) ) surfaces = [] node_id_to_idx = {nid: i for i, nid in enumerate(node_ids)} for surf in raw_dis.surfacenodesets: surf_node_ids = [] for node in surf.nodes: surf_node_ids.append(node.id) position = node_id_to_idx[node.id] nodes_scaling[position] = surface_node_scaling surfaces.append( Surface(node_ids=np.array(surf_node_ids), id=surf.id) ) dis = Discretization( nodes=Nodes( coords=np.array(nodes_coords), ids=np.array(node_ids), scaling_factors=np.array(nodes_scaling), ), elements=elements, surfaces=surfaces, ) return disLoads and processes a finite element discretization with lnmmeshio.
This function imports nodes and elements from a file readable by
lnmmeshio(e.g. .4C.yaml or .vtu), applies optional element filtering, and organizes the data into aDiscretizationobject.Arguments
file_path (Path): Path to the discretization file. options (dict): Options for loading the discretization. Filtering of elements (e.g. by the VTU cell data
block_idor the materialMAT) can be enabled by specifyingelement_filterwith the keysfieldandvalues. processing (I2PPConfig.processing): Processing configuration object.Returns
Discretization- The finite element discretization including nodes
and elements.