beamme.four_c.model_importer

This module contains functions to load and parse existing 4C input files.

  1# The MIT License (MIT)
  2#
  3# Copyright (c) 2018-2025 BeamMe Authors
  4#
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11#
 12# The above copyright notice and this permission notice shall be included in
 13# all copies or substantial portions of the Software.
 14#
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21# THE SOFTWARE.
 22"""This module contains functions to load and parse existing 4C input files."""
 23
 24from pathlib import Path as _Path
 25from typing import Dict as _Dict
 26from typing import List as _List
 27from typing import Tuple as _Tuple
 28from typing import Union as _Union
 29
 30import beamme.core.conf as _conf
 31from beamme.core.boundary_condition import BoundaryCondition as _BoundaryCondition
 32from beamme.core.boundary_condition import (
 33    BoundaryConditionBase as _BoundaryConditionBase,
 34)
 35from beamme.core.conf import bme as _bme
 36from beamme.core.coupling import Coupling as _Coupling
 37from beamme.core.geometry_set import GeometrySetNodes as _GeometrySetNodes
 38from beamme.core.mesh import Mesh as _Mesh
 39from beamme.core.node import Node as _Node
 40from beamme.four_c.input_file import InputFile as _InputFile
 41from beamme.four_c.input_file import (
 42    get_geometry_set_indices_from_section as _get_geometry_set_indices_from_section,
 43)
 44from beamme.four_c.input_file_mappings import (
 45    INPUT_FILE_MAPPINGS as _INPUT_FILE_MAPPINGS,
 46)
 47from beamme.four_c.material import MaterialSolid as _MaterialSolid
 48from beamme.utils.environment import cubitpy_is_available as _cubitpy_is_available
 49
 50if _cubitpy_is_available():
 51    from cubitpy.cubit_to_fourc_input import (
 52        get_input_file_with_mesh as _get_input_file_with_mesh,
 53    )
 54
 55
 56def import_cubitpy_model(
 57    cubit, convert_input_to_mesh: bool = False
 58) -> _Tuple[_InputFile, _Mesh]:
 59    """Convert a CubitPy instance to a BeamMe InputFile.
 60
 61    Args:
 62        cubit (CubitPy): An instance of a cubit model.
 63        convert_input_to_mesh: If this is false, the cubit model will be
 64            converted to plain FourCIPP input data. If this is true, an input
 65            file with all the parameters will be returned and a mesh which
 66            contains the mesh information from cubit converted to BeamMe
 67            objects.
 68
 69    Returns:
 70        A tuple with the input file and the mesh. If convert_input_to_mesh is
 71        False, the mesh will be empty. Note that the input sections which are
 72        converted to a BeamMe mesh are removed from the input file object.
 73    """
 74
 75    input_file = _InputFile(sections=_get_input_file_with_mesh(cubit).sections)
 76
 77    if convert_input_to_mesh:
 78        return _extract_mesh_sections(input_file)
 79    else:
 80        return input_file, _Mesh()
 81
 82
 83def import_four_c_model(
 84    input_file_path: _Path, convert_input_to_mesh: bool = False
 85) -> _Tuple[_InputFile, _Mesh]:
 86    """Import an existing 4C input file and optionally convert it into a BeamMe
 87    mesh.
 88
 89    Args:
 90        input_file_path: A file path to an existing 4C input file that will be
 91            imported.
 92        convert_input_to_mesh: If True, the input file will be converted to a
 93            BeamMe mesh.
 94
 95    Returns:
 96        A tuple with the input file and the mesh. If convert_input_to_mesh is
 97        False, the mesh will be empty. Note that the input sections which are
 98        converted to a BeamMe mesh are removed from the input file object.
 99    """
100
101    input_file = _InputFile().from_4C_yaml(input_file_path=input_file_path)
102
103    if convert_input_to_mesh:
104        return _extract_mesh_sections(input_file)
105    else:
106        return input_file, _Mesh()
107
108
109def _element_from_dict(nodes: _List[_Node], element: dict, material_id_map: dict):
110    """Create a solid element from a dictionary from a 4C input file.
111
112    Args:
113        nodes: A list of nodes that are part of the element.
114        element: A dictionary with the element data.
115        material_id_map: A map between the global material ID and the BeamMe material object.
116    Returns:
117        A solid element object.
118    """
119
120    # Check which element to create.
121    if (
122        element["cell"]["type"]
123        not in _INPUT_FILE_MAPPINGS["element_four_c_string_to_type"]
124    ):
125        raise TypeError(
126            f"Could not create a BeamMe element for {element['data']['type']} {element['cell']['type']}!"
127        )
128    created_element = _INPUT_FILE_MAPPINGS["element_four_c_string_to_type"][
129        element["cell"]["type"]
130    ](nodes=nodes, data=element["data"])
131
132    # Check if we have to link this element to a material object (rigid spheres do not
133    # have a material).
134    if "MAT" in created_element.data:
135        created_element.data["MAT"] = material_id_map[created_element.data["MAT"]]
136    return created_element
137
138
139def _boundary_condition_from_dict(
140    geometry_set: _GeometrySetNodes,
141    bc_key: _Union[_conf.BoundaryCondition, str],
142    data: _Dict,
143) -> _BoundaryConditionBase:
144    """This function acts as a factory and creates the correct boundary
145    condition object from a dictionary parsed from an input file."""
146
147    del data["E"]
148
149    if bc_key in (
150        _bme.bc.dirichlet,
151        _bme.bc.neumann,
152        _bme.bc.locsys,
153        _bme.bc.beam_to_solid_surface_meshtying,
154        _bme.bc.beam_to_solid_surface_contact,
155        _bme.bc.beam_to_solid_volume_meshtying,
156    ) or isinstance(bc_key, str):
157        return _BoundaryCondition(geometry_set, data, bc_type=bc_key)
158    elif bc_key is _bme.bc.point_coupling:
159        return _Coupling(geometry_set, bc_key, data, check_overlapping_nodes=False)
160    else:
161        raise ValueError("Got unexpected boundary condition!")
162
163
164def _get_yaml_geometry_sets(
165    nodes: _List[_Node], geometry_key: _conf.Geometry, section_list: _List
166) -> _Dict[int, _GeometrySetNodes]:
167    """Add sets of points, lines, surfaces or volumes to the object."""
168
169    # Create the individual geometry sets
170    geometry_set_dict = _get_geometry_set_indices_from_section(section_list)
171    geometry_sets_in_this_section = {}
172    for geometry_set_id, node_ids in geometry_set_dict.items():
173        geometry_sets_in_this_section[geometry_set_id] = _GeometrySetNodes(
174            geometry_key, nodes=[nodes[node_id] for node_id in node_ids]
175        )
176    return geometry_sets_in_this_section
177
178
179def _extract_mesh_sections(input_file: _InputFile) -> _Tuple[_InputFile, _Mesh]:
180    """Convert an existing input file to a BeamMe mesh with mesh items, e.g.,
181    nodes, elements, element sets, node sets, boundary conditions, materials.
182
183    Args:
184        input_file: The input file object that contains the sections to be
185            converted to a BeamMe mesh.
186    Returns:
187        A tuple with the input file and the mesh. The input file will be
188            modified to remove the sections that have been converted to a
189            BeamMe mesh.
190    """
191
192    def _get_section_items(section_name):
193        """Return the items in a given section.
194
195        Since we will add the created BeamMe objects to the mesh, we
196        delete them from the plain data storage to avoid having
197        duplicate entries.
198        """
199
200        if section_name in input_file:
201            return input_file.pop(section_name)
202        else:
203            return []
204
205    # Go through all sections that have to be converted to full BeamMe objects
206    mesh = _Mesh()
207
208    # Add materials
209    material_id_map = {}
210    if "MATERIALS" in input_file:
211        for material_in_input_file in input_file.pop("MATERIALS"):
212            material_id = material_in_input_file.pop("MAT")
213            if not len(material_in_input_file) == 1:
214                raise ValueError(
215                    f"Could not convert the material data {material_in_input_file} to a BeamMe material."
216                )
217            material_string = list(material_in_input_file.keys())[0]
218            material_data = list(material_in_input_file.values())[0]
219            material = _MaterialSolid(
220                material_string=material_string, data=material_data
221            )
222            material_id_map[material_id] = material
223            mesh.add(material)
224
225    # Add nodes
226    if "NODE COORDS" in input_file:
227        mesh.nodes = [_Node(node["COORD"]) for node in input_file.pop("NODE COORDS")]
228
229    # Add elements
230    if "STRUCTURE ELEMENTS" in input_file:
231        for element_in_input_file in input_file.pop("STRUCTURE ELEMENTS"):
232            nodes = [
233                mesh.nodes[node_id - 1]
234                for node_id in element_in_input_file["cell"]["connectivity"]
235            ]
236            mesh.elements.append(
237                _element_from_dict(
238                    nodes=nodes,
239                    element=element_in_input_file,
240                    material_id_map=material_id_map,
241                )
242            )
243
244    # Add geometry sets
245    geometry_sets_in_sections: dict[str, dict[int, _GeometrySetNodes]] = {
246        key: {} for key in _bme.geo
247    }
248    for section_name in input_file.sections.keys():
249        if section_name.endswith("TOPOLOGY"):
250            section_items = _get_section_items(section_name)
251            if len(section_items) > 0:
252                # Get the geometry key for this set
253                for key, value in _INPUT_FILE_MAPPINGS[
254                    "geometry_sets_geometry_to_condition_name"
255                ].items():
256                    if value == section_name:
257                        geometry_key = key
258                        break
259                else:
260                    raise ValueError(f"Could not find the set {section_name}")
261                geometry_sets_in_section = _get_yaml_geometry_sets(
262                    mesh.nodes, geometry_key, section_items
263                )
264                geometry_sets_in_sections[geometry_key] = geometry_sets_in_section
265                mesh.geometry_sets[geometry_key] = list(
266                    geometry_sets_in_section.values()
267                )
268
269    # Add boundary conditions
270    for (
271        bc_key,
272        geometry_key,
273    ), section_name in _INPUT_FILE_MAPPINGS["boundary_conditions"].items():
274        for item in _get_section_items(section_name):
275            geometry_set_id = item["E"]
276            geometry_set = geometry_sets_in_sections[geometry_key][geometry_set_id]
277            mesh.boundary_conditions.append(
278                (bc_key, geometry_key),
279                _boundary_condition_from_dict(geometry_set, bc_key, item),
280            )
281
282    return input_file, mesh
def import_cubitpy_model( cubit, convert_input_to_mesh: bool = False) -> Tuple[beamme.four_c.input_file.InputFile, beamme.core.mesh.Mesh]:
57def import_cubitpy_model(
58    cubit, convert_input_to_mesh: bool = False
59) -> _Tuple[_InputFile, _Mesh]:
60    """Convert a CubitPy instance to a BeamMe InputFile.
61
62    Args:
63        cubit (CubitPy): An instance of a cubit model.
64        convert_input_to_mesh: If this is false, the cubit model will be
65            converted to plain FourCIPP input data. If this is true, an input
66            file with all the parameters will be returned and a mesh which
67            contains the mesh information from cubit converted to BeamMe
68            objects.
69
70    Returns:
71        A tuple with the input file and the mesh. If convert_input_to_mesh is
72        False, the mesh will be empty. Note that the input sections which are
73        converted to a BeamMe mesh are removed from the input file object.
74    """
75
76    input_file = _InputFile(sections=_get_input_file_with_mesh(cubit).sections)
77
78    if convert_input_to_mesh:
79        return _extract_mesh_sections(input_file)
80    else:
81        return input_file, _Mesh()

Convert a CubitPy instance to a BeamMe InputFile.

Arguments:
  • cubit (CubitPy): An instance of a cubit model.
  • convert_input_to_mesh: If this is false, the cubit model will be converted to plain FourCIPP input data. If this is true, an input file with all the parameters will be returned and a mesh which contains the mesh information from cubit converted to BeamMe objects.
Returns:

A tuple with the input file and the mesh. If convert_input_to_mesh is False, the mesh will be empty. Note that the input sections which are converted to a BeamMe mesh are removed from the input file object.

def import_four_c_model( input_file_path: pathlib.Path, convert_input_to_mesh: bool = False) -> Tuple[beamme.four_c.input_file.InputFile, beamme.core.mesh.Mesh]:
 84def import_four_c_model(
 85    input_file_path: _Path, convert_input_to_mesh: bool = False
 86) -> _Tuple[_InputFile, _Mesh]:
 87    """Import an existing 4C input file and optionally convert it into a BeamMe
 88    mesh.
 89
 90    Args:
 91        input_file_path: A file path to an existing 4C input file that will be
 92            imported.
 93        convert_input_to_mesh: If True, the input file will be converted to a
 94            BeamMe mesh.
 95
 96    Returns:
 97        A tuple with the input file and the mesh. If convert_input_to_mesh is
 98        False, the mesh will be empty. Note that the input sections which are
 99        converted to a BeamMe mesh are removed from the input file object.
100    """
101
102    input_file = _InputFile().from_4C_yaml(input_file_path=input_file_path)
103
104    if convert_input_to_mesh:
105        return _extract_mesh_sections(input_file)
106    else:
107        return input_file, _Mesh()

Import an existing 4C input file and optionally convert it into a BeamMe mesh.

Arguments:
  • input_file_path: A file path to an existing 4C input file that will be imported.
  • convert_input_to_mesh: If True, the input file will be converted to a BeamMe mesh.
Returns:

A tuple with the input file and the mesh. If convert_input_to_mesh is False, the mesh will be empty. Note that the input sections which are converted to a BeamMe mesh are removed from the input file object.