beamme.four_c.input_file

This module defines the classes that are used to create an input file for 4C.

  1# The MIT License (MIT)
  2#
  3# Copyright (c) 2018-2026 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 defines the classes that are used to create an input file for 4C."""
 23
 24import os as _os
 25from collections import defaultdict as _defaultdict
 26from collections.abc import Callable as _Callable
 27from datetime import datetime as _datetime
 28from pathlib import Path as _Path
 29from typing import Any as _Any
 30from typing import Self as _Self
 31
 32from fourcipp.fourc_input import FourCInput as _FourCInput
 33from fourcipp.fourc_input import sort_by_section_names as _sort_by_section_names
 34from fourcipp.utils.not_set import NOT_SET as _NOT_SET
 35
 36from beamme.core.conf import INPUT_FILE_HEADER as _INPUT_FILE_HEADER
 37from beamme.core.mesh import Mesh as _Mesh
 38from beamme.core.mesh_representation import MeshRepresentation as _MeshRepresentation
 39from beamme.four_c.boundary_condition_data import (
 40    FourCBoundaryConditionData as _FourCBoundaryConditionData,
 41)
 42from beamme.four_c.element_data import FourCElementData as _FourCElementData
 43from beamme.four_c.input_file_dump_functions import (
 44    dump_mesh_representation_to_input_file_vtu as _dump_mesh_representation_to_input_file_vtu,
 45)
 46from beamme.four_c.input_file_dump_functions import (
 47    dump_mesh_representation_to_input_file_yaml as _dump_mesh_representation_to_input_file_yaml,
 48)
 49from beamme.four_c.input_file_dump_functions import (
 50    dump_mesh_to_input_file as _dump_mesh_to_input_file,
 51)
 52from beamme.utils.environment import cubitpy_is_available as _cubitpy_is_available
 53from beamme.utils.environment import get_application_path as _get_application_path
 54from beamme.utils.environment import get_git_data as _get_git_data
 55
 56if _cubitpy_is_available():
 57    import cubitpy as _cubitpy
 58
 59
 60class InputFile:
 61    """An item that represents a complete 4C input file."""
 62
 63    def __init__(self) -> None:
 64        """Initialize the input file."""
 65        self.fourc_input = _FourCInput()
 66
 67        # Register converters to directly convert non-primitive types
 68        # to native Python types via the FourCIPP type converter.
 69        self.fourc_input.type_converter.register_numpy_types()
 70
 71        # Contents of NOX xml file.
 72        self.nox_xml_contents = ""
 73
 74        # Mesh representation for this input file.
 75        self.mesh_representation = _MeshRepresentation()
 76        self.element_type_id_to_data: dict[_Any, _FourCElementData] = {}
 77
 78        # Boundary conditions
 79        self.boundary_conditions: dict[str, list[_FourCBoundaryConditionData]] = (
 80            _defaultdict(list)
 81        )
 82
 83    def __contains__(self, key: str) -> bool:
 84        """Contains function.
 85
 86        Allows to use the `in` operator.
 87
 88        Args:
 89            key: Section name to check if it is set
 90
 91        Returns:
 92            True if section is set
 93        """
 94        return key in self.fourc_input
 95
 96    def __setitem__(self, key: str, value: _Any) -> None:
 97        """Set section.
 98
 99        Args:
100            key: Section name
101            value: Section entry
102        """
103        self.fourc_input[key] = value
104
105    def __getitem__(self, key: str) -> _Any:
106        """Get section of input file.
107
108        Allows to use the indexing operator.
109
110        Args:
111            key: Section name to get
112
113        Returns:
114            The section content
115        """
116        return self.fourc_input[key]
117
118    @classmethod
119    def from_4C_yaml(
120        cls, input_file_path: str | _Path, header_only: bool = False
121    ) -> _Self:
122        """Load 4C yaml file.
123
124        Args:
125            input_file_path: Path to yaml file
126            header_only: Only extract header, i.e., all sections except the legacy ones
127
128        Returns:
129            Initialised object
130        """
131        obj = cls()
132        obj.fourc_input = _FourCInput.from_4C_yaml(input_file_path, header_only)
133        return obj
134
135    @property
136    def sections(self) -> dict:
137        """All the set sections.
138
139        Returns:
140            dict: Set sections
141        """
142        return self.fourc_input.sections
143
144    def pop(self, key: str, default_value: _Any = _NOT_SET) -> _Any:
145        """Pop section of input file.
146
147        Args:
148            key: Section name to pop
149
150        Returns:
151            The section content
152        """
153        return self.fourc_input.pop(key, default_value)
154
155    def add(self, object_to_add, **kwargs):
156        """Add a mesh or a dictionary to the input file.
157
158        Args:
159            object: The object to be added. This can be a mesh or a dictionary.
160            **kwargs: Additional arguments to be passed to the add method.
161        """
162        if isinstance(object_to_add, _Mesh):
163            _dump_mesh_to_input_file(self, mesh=object_to_add, **kwargs)
164
165        else:
166            self.fourc_input.combine_sections(object_to_add)
167
168    def get_fourcipp_input_with_mesh(self) -> _FourCInput:
169        """Return a copy of the FourCIPP input file with the contents of the mesh
170        representation dumped to the yaml sections."""
171        fourc_input = self.fourc_input.copy()
172        _dump_mesh_representation_to_input_file_yaml(
173            fourc_input,
174            self.mesh_representation,
175            self.element_type_id_to_data,
176            self.boundary_conditions,
177        )
178        return fourc_input
179
180    def dump(
181        self,
182        input_file_path: str | _Path,
183        *,
184        mesh_format: str = "vtu",
185        vtu_binary: bool = True,
186        nox_xml_file: str | None = None,
187        add_header_default: bool = True,
188        add_header_information: bool = True,
189        add_footer_application_script: bool = True,
190        validate=True,
191        validate_sections_only: bool = False,
192        sort_function: _Callable[[dict], dict] | None = _sort_by_section_names,
193        fourcipp_yaml_style: bool = True,
194    ):
195        """Write the input file to disk.
196
197        Args:
198            input_file_path:
199                Path to the input file that should be created.
200            mesh_format:
201                The format in which the mesh information should be written.
202                Currently, "vtu" and "yaml" are supported.
203            vtu_binary:
204                Only relevant if mesh_format is "vtu". If True, the vtu file will
205                be written in binary format. Otherwise, it will be written in ascii format.
206            nox_xml_file:
207                If this is a string, the NOX xml file will be created with this
208                name. If this is None, the NOX xml file will be created with the
209                name of the input file with the extension ".nox.xml".
210            add_header_default:
211                Prepend the default header comment to the input file.
212            add_header_information:
213                If the information header should be exported to the input file
214                Contains creation date, git details of BeamMe, CubitPy and
215                original application which created the input file if available.
216            add_footer_application_script:
217                Append the application script which creates the input files as a
218                comment at the end of the input file.
219            validate:
220                Validate if the created input file is compatible with 4C with FourCIPP.
221            validate_sections_only:
222                Validate each section independently. Required sections are no longer
223                required, but the sections must be valid.
224            sort_function:
225                A function which sorts the sections of the input file.
226            fourcipp_yaml_style:
227                If True, the input file is written in the fourcipp yaml style.
228        """
229        # Make sure the given input file is a Path instance.
230        input_file_path = _Path(input_file_path)
231
232        # Base name of the input file without extension.
233        if not input_file_path.name.endswith(".4C.yaml"):
234            raise ValueError(
235                "Input file must have a .4C.yaml extension, but got the "
236                f"path {input_file_path}"
237            )
238        input_file_base_name = input_file_path.name.removesuffix(".4C.yaml")
239
240        # Create a deep copy of the existing input sections - this function should not alter
241        # the present instance of InputFile
242        fourc_input = self.fourc_input.copy()
243
244        # Add the mesh representation, either directly to the yaml input file or via an
245        # external mesh format.
246        if mesh_format == "vtu":
247            vtu_file_path = input_file_path.parent / (
248                input_file_base_name + ".mesh.vtu"
249            )
250            vtu_grid = _dump_mesh_representation_to_input_file_vtu(
251                fourc_input,
252                self.mesh_representation,
253                self.element_type_id_to_data,
254                self.boundary_conditions,
255            )
256            # Save the grid and add the file name to the input file
257            vtu_grid.save(vtu_file_path, binary=vtu_binary)
258            fourc_input["STRUCTURE GEOMETRY"]["FILE"] = vtu_file_path.name
259        elif mesh_format == "yaml":
260            _dump_mesh_representation_to_input_file_yaml(
261                fourc_input,
262                self.mesh_representation,
263                self.element_type_id_to_data,
264                self.boundary_conditions,
265            )
266        else:
267            raise ValueError(f"Unsupported mesh format: {mesh_format}.")
268
269        if self.nox_xml_contents:
270            if nox_xml_file is None:
271                nox_xml_file = input_file_base_name + ".nox.xml"
272
273            fourc_input["STRUCT NOX/Status Test"] = {"XML File": nox_xml_file}
274
275            # Write the xml file to the disc.
276            with open(input_file_path.parent / nox_xml_file, "w") as xml_file:
277                xml_file.write(self.nox_xml_contents)
278
279        # Add information header to the input file
280        if add_header_information:
281            fourc_input.combine_sections({"TITLE": self._get_header()})
282
283        fourc_input.dump(
284            input_file_path=input_file_path,
285            validate=validate,
286            validate_sections_only=validate_sections_only,
287            convert_to_native_types=False,  # conversion already happens during add()
288            sort_function=sort_function,
289            use_fourcipp_yaml_style=fourcipp_yaml_style,
290        )
291
292        if add_header_default or add_footer_application_script:
293            with open(input_file_path, "r") as input_file:
294                lines = input_file.readlines()
295
296                if add_header_default:
297                    lines = ["# " + line + "\n" for line in _INPUT_FILE_HEADER] + lines
298
299                if add_footer_application_script:
300                    application_path = _get_application_path()
301                    if application_path is not None:
302                        lines += self._get_application_script(application_path)
303
304                with open(input_file_path, "w") as input_file:
305                    input_file.writelines(lines)
306
307    def _get_header(self) -> dict:
308        """Return the information header for the current BeamMe run.
309
310        Returns:
311            A dictionary with the header information.
312        """
313        header: dict = {"BeamMe": {}}
314
315        header["BeamMe"]["creation_date"] = _datetime.now().isoformat(
316            sep=" ", timespec="seconds"
317        )
318
319        # application which created the input file
320        application_path = _get_application_path()
321        if application_path is not None:
322            header["BeamMe"]["Application"] = {"path": str(application_path)}
323
324            application_git_sha, application_git_date = _get_git_data(
325                application_path.parent
326            )
327            if application_git_sha is not None and application_git_date is not None:
328                header["BeamMe"]["Application"].update(
329                    {
330                        "git_sha": application_git_sha,
331                        "git_date": application_git_date,
332                    }
333                )
334
335        # BeamMe information
336        beamme_git_sha, beamme_git_date = _get_git_data(
337            _Path(__file__).resolve().parent
338        )
339        if beamme_git_sha is not None and beamme_git_date is not None:
340            header["BeamMe"]["BeamMe"] = {
341                "git_SHA": beamme_git_sha,
342                "git_date": beamme_git_date,
343            }
344
345        # CubitPy information
346        if _cubitpy_is_available():
347            cubitpy_git_sha, cubitpy_git_date = _get_git_data(
348                _os.path.dirname(_cubitpy.__file__)
349            )
350
351            if cubitpy_git_sha is not None and cubitpy_git_date is not None:
352                header["BeamMe"]["CubitPy"] = {
353                    "git_SHA": cubitpy_git_sha,
354                    "git_date": cubitpy_git_date,
355                }
356
357        return header
358
359    def _get_application_script(self, application_path: _Path) -> list[str]:
360        """Get the script that created this input file.
361
362        Args:
363            application_path: Path to the script that created this input file.
364        Returns:
365            A list of strings with the script that created this input file.
366        """
367        application_script_lines = [
368            "# Application script which created this input file:\n"
369        ]
370
371        with open(application_path) as script_file:
372            application_script_lines.extend("# " + line for line in script_file)
373
374        return application_script_lines
375
376    def _contains_mesh_based_geometry(self, allowed_extensions: list[str]) -> bool:
377        """Check if the input file contains mesh-based geometry.
378
379        Args:
380            allowed_extensions: List of allowed file extensions for mesh files.
381
382        Returns:
383            True if the input file contains mesh-based geometry of given type,
384            False otherwise.
385        """
386        structure_geometry_section = self.fourc_input.sections.get(
387            "STRUCTURE GEOMETRY", None
388        )
389        if structure_geometry_section is not None:
390            file_name = structure_geometry_section.get("FILE", None)
391            if file_name is not None:
392                mesh_file_name = _Path(file_name)
393                if mesh_file_name.suffix.lower() in allowed_extensions:
394                    return True
395        return False
396
397    def contains_mesh_based_geometry_exodus(self) -> bool:
398        """Check if the input file contains exodus mesh-based geometry.
399
400        Returns:
401            True if the input file contains exodus mesh-based geometry, False otherwise.
402        """
403        return self._contains_mesh_based_geometry([".exo", ".e"])
404
405    def contains_mesh_based_geometry_vtu(self) -> bool:
406        """Check if the input file contains VTU mesh-based geometry.
407
408        Returns:
409            True if the input file contains VTU mesh-based geometry, False otherwise.
410        """
411        return self._contains_mesh_based_geometry([".vtu"])
class InputFile:
 61class InputFile:
 62    """An item that represents a complete 4C input file."""
 63
 64    def __init__(self) -> None:
 65        """Initialize the input file."""
 66        self.fourc_input = _FourCInput()
 67
 68        # Register converters to directly convert non-primitive types
 69        # to native Python types via the FourCIPP type converter.
 70        self.fourc_input.type_converter.register_numpy_types()
 71
 72        # Contents of NOX xml file.
 73        self.nox_xml_contents = ""
 74
 75        # Mesh representation for this input file.
 76        self.mesh_representation = _MeshRepresentation()
 77        self.element_type_id_to_data: dict[_Any, _FourCElementData] = {}
 78
 79        # Boundary conditions
 80        self.boundary_conditions: dict[str, list[_FourCBoundaryConditionData]] = (
 81            _defaultdict(list)
 82        )
 83
 84    def __contains__(self, key: str) -> bool:
 85        """Contains function.
 86
 87        Allows to use the `in` operator.
 88
 89        Args:
 90            key: Section name to check if it is set
 91
 92        Returns:
 93            True if section is set
 94        """
 95        return key in self.fourc_input
 96
 97    def __setitem__(self, key: str, value: _Any) -> None:
 98        """Set section.
 99
100        Args:
101            key: Section name
102            value: Section entry
103        """
104        self.fourc_input[key] = value
105
106    def __getitem__(self, key: str) -> _Any:
107        """Get section of input file.
108
109        Allows to use the indexing operator.
110
111        Args:
112            key: Section name to get
113
114        Returns:
115            The section content
116        """
117        return self.fourc_input[key]
118
119    @classmethod
120    def from_4C_yaml(
121        cls, input_file_path: str | _Path, header_only: bool = False
122    ) -> _Self:
123        """Load 4C yaml file.
124
125        Args:
126            input_file_path: Path to yaml file
127            header_only: Only extract header, i.e., all sections except the legacy ones
128
129        Returns:
130            Initialised object
131        """
132        obj = cls()
133        obj.fourc_input = _FourCInput.from_4C_yaml(input_file_path, header_only)
134        return obj
135
136    @property
137    def sections(self) -> dict:
138        """All the set sections.
139
140        Returns:
141            dict: Set sections
142        """
143        return self.fourc_input.sections
144
145    def pop(self, key: str, default_value: _Any = _NOT_SET) -> _Any:
146        """Pop section of input file.
147
148        Args:
149            key: Section name to pop
150
151        Returns:
152            The section content
153        """
154        return self.fourc_input.pop(key, default_value)
155
156    def add(self, object_to_add, **kwargs):
157        """Add a mesh or a dictionary to the input file.
158
159        Args:
160            object: The object to be added. This can be a mesh or a dictionary.
161            **kwargs: Additional arguments to be passed to the add method.
162        """
163        if isinstance(object_to_add, _Mesh):
164            _dump_mesh_to_input_file(self, mesh=object_to_add, **kwargs)
165
166        else:
167            self.fourc_input.combine_sections(object_to_add)
168
169    def get_fourcipp_input_with_mesh(self) -> _FourCInput:
170        """Return a copy of the FourCIPP input file with the contents of the mesh
171        representation dumped to the yaml sections."""
172        fourc_input = self.fourc_input.copy()
173        _dump_mesh_representation_to_input_file_yaml(
174            fourc_input,
175            self.mesh_representation,
176            self.element_type_id_to_data,
177            self.boundary_conditions,
178        )
179        return fourc_input
180
181    def dump(
182        self,
183        input_file_path: str | _Path,
184        *,
185        mesh_format: str = "vtu",
186        vtu_binary: bool = True,
187        nox_xml_file: str | None = None,
188        add_header_default: bool = True,
189        add_header_information: bool = True,
190        add_footer_application_script: bool = True,
191        validate=True,
192        validate_sections_only: bool = False,
193        sort_function: _Callable[[dict], dict] | None = _sort_by_section_names,
194        fourcipp_yaml_style: bool = True,
195    ):
196        """Write the input file to disk.
197
198        Args:
199            input_file_path:
200                Path to the input file that should be created.
201            mesh_format:
202                The format in which the mesh information should be written.
203                Currently, "vtu" and "yaml" are supported.
204            vtu_binary:
205                Only relevant if mesh_format is "vtu". If True, the vtu file will
206                be written in binary format. Otherwise, it will be written in ascii format.
207            nox_xml_file:
208                If this is a string, the NOX xml file will be created with this
209                name. If this is None, the NOX xml file will be created with the
210                name of the input file with the extension ".nox.xml".
211            add_header_default:
212                Prepend the default header comment to the input file.
213            add_header_information:
214                If the information header should be exported to the input file
215                Contains creation date, git details of BeamMe, CubitPy and
216                original application which created the input file if available.
217            add_footer_application_script:
218                Append the application script which creates the input files as a
219                comment at the end of the input file.
220            validate:
221                Validate if the created input file is compatible with 4C with FourCIPP.
222            validate_sections_only:
223                Validate each section independently. Required sections are no longer
224                required, but the sections must be valid.
225            sort_function:
226                A function which sorts the sections of the input file.
227            fourcipp_yaml_style:
228                If True, the input file is written in the fourcipp yaml style.
229        """
230        # Make sure the given input file is a Path instance.
231        input_file_path = _Path(input_file_path)
232
233        # Base name of the input file without extension.
234        if not input_file_path.name.endswith(".4C.yaml"):
235            raise ValueError(
236                "Input file must have a .4C.yaml extension, but got the "
237                f"path {input_file_path}"
238            )
239        input_file_base_name = input_file_path.name.removesuffix(".4C.yaml")
240
241        # Create a deep copy of the existing input sections - this function should not alter
242        # the present instance of InputFile
243        fourc_input = self.fourc_input.copy()
244
245        # Add the mesh representation, either directly to the yaml input file or via an
246        # external mesh format.
247        if mesh_format == "vtu":
248            vtu_file_path = input_file_path.parent / (
249                input_file_base_name + ".mesh.vtu"
250            )
251            vtu_grid = _dump_mesh_representation_to_input_file_vtu(
252                fourc_input,
253                self.mesh_representation,
254                self.element_type_id_to_data,
255                self.boundary_conditions,
256            )
257            # Save the grid and add the file name to the input file
258            vtu_grid.save(vtu_file_path, binary=vtu_binary)
259            fourc_input["STRUCTURE GEOMETRY"]["FILE"] = vtu_file_path.name
260        elif mesh_format == "yaml":
261            _dump_mesh_representation_to_input_file_yaml(
262                fourc_input,
263                self.mesh_representation,
264                self.element_type_id_to_data,
265                self.boundary_conditions,
266            )
267        else:
268            raise ValueError(f"Unsupported mesh format: {mesh_format}.")
269
270        if self.nox_xml_contents:
271            if nox_xml_file is None:
272                nox_xml_file = input_file_base_name + ".nox.xml"
273
274            fourc_input["STRUCT NOX/Status Test"] = {"XML File": nox_xml_file}
275
276            # Write the xml file to the disc.
277            with open(input_file_path.parent / nox_xml_file, "w") as xml_file:
278                xml_file.write(self.nox_xml_contents)
279
280        # Add information header to the input file
281        if add_header_information:
282            fourc_input.combine_sections({"TITLE": self._get_header()})
283
284        fourc_input.dump(
285            input_file_path=input_file_path,
286            validate=validate,
287            validate_sections_only=validate_sections_only,
288            convert_to_native_types=False,  # conversion already happens during add()
289            sort_function=sort_function,
290            use_fourcipp_yaml_style=fourcipp_yaml_style,
291        )
292
293        if add_header_default or add_footer_application_script:
294            with open(input_file_path, "r") as input_file:
295                lines = input_file.readlines()
296
297                if add_header_default:
298                    lines = ["# " + line + "\n" for line in _INPUT_FILE_HEADER] + lines
299
300                if add_footer_application_script:
301                    application_path = _get_application_path()
302                    if application_path is not None:
303                        lines += self._get_application_script(application_path)
304
305                with open(input_file_path, "w") as input_file:
306                    input_file.writelines(lines)
307
308    def _get_header(self) -> dict:
309        """Return the information header for the current BeamMe run.
310
311        Returns:
312            A dictionary with the header information.
313        """
314        header: dict = {"BeamMe": {}}
315
316        header["BeamMe"]["creation_date"] = _datetime.now().isoformat(
317            sep=" ", timespec="seconds"
318        )
319
320        # application which created the input file
321        application_path = _get_application_path()
322        if application_path is not None:
323            header["BeamMe"]["Application"] = {"path": str(application_path)}
324
325            application_git_sha, application_git_date = _get_git_data(
326                application_path.parent
327            )
328            if application_git_sha is not None and application_git_date is not None:
329                header["BeamMe"]["Application"].update(
330                    {
331                        "git_sha": application_git_sha,
332                        "git_date": application_git_date,
333                    }
334                )
335
336        # BeamMe information
337        beamme_git_sha, beamme_git_date = _get_git_data(
338            _Path(__file__).resolve().parent
339        )
340        if beamme_git_sha is not None and beamme_git_date is not None:
341            header["BeamMe"]["BeamMe"] = {
342                "git_SHA": beamme_git_sha,
343                "git_date": beamme_git_date,
344            }
345
346        # CubitPy information
347        if _cubitpy_is_available():
348            cubitpy_git_sha, cubitpy_git_date = _get_git_data(
349                _os.path.dirname(_cubitpy.__file__)
350            )
351
352            if cubitpy_git_sha is not None and cubitpy_git_date is not None:
353                header["BeamMe"]["CubitPy"] = {
354                    "git_SHA": cubitpy_git_sha,
355                    "git_date": cubitpy_git_date,
356                }
357
358        return header
359
360    def _get_application_script(self, application_path: _Path) -> list[str]:
361        """Get the script that created this input file.
362
363        Args:
364            application_path: Path to the script that created this input file.
365        Returns:
366            A list of strings with the script that created this input file.
367        """
368        application_script_lines = [
369            "# Application script which created this input file:\n"
370        ]
371
372        with open(application_path) as script_file:
373            application_script_lines.extend("# " + line for line in script_file)
374
375        return application_script_lines
376
377    def _contains_mesh_based_geometry(self, allowed_extensions: list[str]) -> bool:
378        """Check if the input file contains mesh-based geometry.
379
380        Args:
381            allowed_extensions: List of allowed file extensions for mesh files.
382
383        Returns:
384            True if the input file contains mesh-based geometry of given type,
385            False otherwise.
386        """
387        structure_geometry_section = self.fourc_input.sections.get(
388            "STRUCTURE GEOMETRY", None
389        )
390        if structure_geometry_section is not None:
391            file_name = structure_geometry_section.get("FILE", None)
392            if file_name is not None:
393                mesh_file_name = _Path(file_name)
394                if mesh_file_name.suffix.lower() in allowed_extensions:
395                    return True
396        return False
397
398    def contains_mesh_based_geometry_exodus(self) -> bool:
399        """Check if the input file contains exodus mesh-based geometry.
400
401        Returns:
402            True if the input file contains exodus mesh-based geometry, False otherwise.
403        """
404        return self._contains_mesh_based_geometry([".exo", ".e"])
405
406    def contains_mesh_based_geometry_vtu(self) -> bool:
407        """Check if the input file contains VTU mesh-based geometry.
408
409        Returns:
410            True if the input file contains VTU mesh-based geometry, False otherwise.
411        """
412        return self._contains_mesh_based_geometry([".vtu"])

An item that represents a complete 4C input file.

InputFile()
64    def __init__(self) -> None:
65        """Initialize the input file."""
66        self.fourc_input = _FourCInput()
67
68        # Register converters to directly convert non-primitive types
69        # to native Python types via the FourCIPP type converter.
70        self.fourc_input.type_converter.register_numpy_types()
71
72        # Contents of NOX xml file.
73        self.nox_xml_contents = ""
74
75        # Mesh representation for this input file.
76        self.mesh_representation = _MeshRepresentation()
77        self.element_type_id_to_data: dict[_Any, _FourCElementData] = {}
78
79        # Boundary conditions
80        self.boundary_conditions: dict[str, list[_FourCBoundaryConditionData]] = (
81            _defaultdict(list)
82        )

Initialize the input file.

fourc_input
nox_xml_contents
mesh_representation
element_type_id_to_data: dict[typing.Any, beamme.four_c.element_data.FourCElementData]
@classmethod
def from_4C_yaml( cls, input_file_path: str | pathlib.Path, header_only: bool = False) -> Self:
119    @classmethod
120    def from_4C_yaml(
121        cls, input_file_path: str | _Path, header_only: bool = False
122    ) -> _Self:
123        """Load 4C yaml file.
124
125        Args:
126            input_file_path: Path to yaml file
127            header_only: Only extract header, i.e., all sections except the legacy ones
128
129        Returns:
130            Initialised object
131        """
132        obj = cls()
133        obj.fourc_input = _FourCInput.from_4C_yaml(input_file_path, header_only)
134        return obj

Load 4C yaml file.

Arguments:
  • input_file_path: Path to yaml file
  • header_only: Only extract header, i.e., all sections except the legacy ones
Returns:

Initialised object

sections: dict
136    @property
137    def sections(self) -> dict:
138        """All the set sections.
139
140        Returns:
141            dict: Set sections
142        """
143        return self.fourc_input.sections

All the set sections.

Returns:

dict: Set sections

def pop(self, key: str, default_value: Any = NotSet(<class 'object'>)) -> Any:
145    def pop(self, key: str, default_value: _Any = _NOT_SET) -> _Any:
146        """Pop section of input file.
147
148        Args:
149            key: Section name to pop
150
151        Returns:
152            The section content
153        """
154        return self.fourc_input.pop(key, default_value)

Pop section of input file.

Arguments:
  • key: Section name to pop
Returns:

The section content

def add(self, object_to_add, **kwargs):
156    def add(self, object_to_add, **kwargs):
157        """Add a mesh or a dictionary to the input file.
158
159        Args:
160            object: The object to be added. This can be a mesh or a dictionary.
161            **kwargs: Additional arguments to be passed to the add method.
162        """
163        if isinstance(object_to_add, _Mesh):
164            _dump_mesh_to_input_file(self, mesh=object_to_add, **kwargs)
165
166        else:
167            self.fourc_input.combine_sections(object_to_add)

Add a mesh or a dictionary to the input file.

Arguments:
  • object: The object to be added. This can be a mesh or a dictionary.
  • **kwargs: Additional arguments to be passed to the add method.
def get_fourcipp_input_with_mesh(self) -> fourcipp.fourc_input.FourCInput:
169    def get_fourcipp_input_with_mesh(self) -> _FourCInput:
170        """Return a copy of the FourCIPP input file with the contents of the mesh
171        representation dumped to the yaml sections."""
172        fourc_input = self.fourc_input.copy()
173        _dump_mesh_representation_to_input_file_yaml(
174            fourc_input,
175            self.mesh_representation,
176            self.element_type_id_to_data,
177            self.boundary_conditions,
178        )
179        return fourc_input

Return a copy of the FourCIPP input file with the contents of the mesh representation dumped to the yaml sections.

def dump( self, input_file_path: str | pathlib.Path, *, mesh_format: str = 'vtu', vtu_binary: bool = True, nox_xml_file: str | None = None, add_header_default: bool = True, add_header_information: bool = True, add_footer_application_script: bool = True, validate=True, validate_sections_only: bool = False, sort_function: Callable[[dict], dict] | None = <function sort_by_section_names>, fourcipp_yaml_style: bool = True):
181    def dump(
182        self,
183        input_file_path: str | _Path,
184        *,
185        mesh_format: str = "vtu",
186        vtu_binary: bool = True,
187        nox_xml_file: str | None = None,
188        add_header_default: bool = True,
189        add_header_information: bool = True,
190        add_footer_application_script: bool = True,
191        validate=True,
192        validate_sections_only: bool = False,
193        sort_function: _Callable[[dict], dict] | None = _sort_by_section_names,
194        fourcipp_yaml_style: bool = True,
195    ):
196        """Write the input file to disk.
197
198        Args:
199            input_file_path:
200                Path to the input file that should be created.
201            mesh_format:
202                The format in which the mesh information should be written.
203                Currently, "vtu" and "yaml" are supported.
204            vtu_binary:
205                Only relevant if mesh_format is "vtu". If True, the vtu file will
206                be written in binary format. Otherwise, it will be written in ascii format.
207            nox_xml_file:
208                If this is a string, the NOX xml file will be created with this
209                name. If this is None, the NOX xml file will be created with the
210                name of the input file with the extension ".nox.xml".
211            add_header_default:
212                Prepend the default header comment to the input file.
213            add_header_information:
214                If the information header should be exported to the input file
215                Contains creation date, git details of BeamMe, CubitPy and
216                original application which created the input file if available.
217            add_footer_application_script:
218                Append the application script which creates the input files as a
219                comment at the end of the input file.
220            validate:
221                Validate if the created input file is compatible with 4C with FourCIPP.
222            validate_sections_only:
223                Validate each section independently. Required sections are no longer
224                required, but the sections must be valid.
225            sort_function:
226                A function which sorts the sections of the input file.
227            fourcipp_yaml_style:
228                If True, the input file is written in the fourcipp yaml style.
229        """
230        # Make sure the given input file is a Path instance.
231        input_file_path = _Path(input_file_path)
232
233        # Base name of the input file without extension.
234        if not input_file_path.name.endswith(".4C.yaml"):
235            raise ValueError(
236                "Input file must have a .4C.yaml extension, but got the "
237                f"path {input_file_path}"
238            )
239        input_file_base_name = input_file_path.name.removesuffix(".4C.yaml")
240
241        # Create a deep copy of the existing input sections - this function should not alter
242        # the present instance of InputFile
243        fourc_input = self.fourc_input.copy()
244
245        # Add the mesh representation, either directly to the yaml input file or via an
246        # external mesh format.
247        if mesh_format == "vtu":
248            vtu_file_path = input_file_path.parent / (
249                input_file_base_name + ".mesh.vtu"
250            )
251            vtu_grid = _dump_mesh_representation_to_input_file_vtu(
252                fourc_input,
253                self.mesh_representation,
254                self.element_type_id_to_data,
255                self.boundary_conditions,
256            )
257            # Save the grid and add the file name to the input file
258            vtu_grid.save(vtu_file_path, binary=vtu_binary)
259            fourc_input["STRUCTURE GEOMETRY"]["FILE"] = vtu_file_path.name
260        elif mesh_format == "yaml":
261            _dump_mesh_representation_to_input_file_yaml(
262                fourc_input,
263                self.mesh_representation,
264                self.element_type_id_to_data,
265                self.boundary_conditions,
266            )
267        else:
268            raise ValueError(f"Unsupported mesh format: {mesh_format}.")
269
270        if self.nox_xml_contents:
271            if nox_xml_file is None:
272                nox_xml_file = input_file_base_name + ".nox.xml"
273
274            fourc_input["STRUCT NOX/Status Test"] = {"XML File": nox_xml_file}
275
276            # Write the xml file to the disc.
277            with open(input_file_path.parent / nox_xml_file, "w") as xml_file:
278                xml_file.write(self.nox_xml_contents)
279
280        # Add information header to the input file
281        if add_header_information:
282            fourc_input.combine_sections({"TITLE": self._get_header()})
283
284        fourc_input.dump(
285            input_file_path=input_file_path,
286            validate=validate,
287            validate_sections_only=validate_sections_only,
288            convert_to_native_types=False,  # conversion already happens during add()
289            sort_function=sort_function,
290            use_fourcipp_yaml_style=fourcipp_yaml_style,
291        )
292
293        if add_header_default or add_footer_application_script:
294            with open(input_file_path, "r") as input_file:
295                lines = input_file.readlines()
296
297                if add_header_default:
298                    lines = ["# " + line + "\n" for line in _INPUT_FILE_HEADER] + lines
299
300                if add_footer_application_script:
301                    application_path = _get_application_path()
302                    if application_path is not None:
303                        lines += self._get_application_script(application_path)
304
305                with open(input_file_path, "w") as input_file:
306                    input_file.writelines(lines)

Write the input file to disk.

Arguments:
  • input_file_path: Path to the input file that should be created.
  • mesh_format: The format in which the mesh information should be written. Currently, "vtu" and "yaml" are supported.
  • vtu_binary: Only relevant if mesh_format is "vtu". If True, the vtu file will be written in binary format. Otherwise, it will be written in ascii format.
  • nox_xml_file: If this is a string, the NOX xml file will be created with this name. If this is None, the NOX xml file will be created with the name of the input file with the extension ".nox.xml".
  • add_header_default: Prepend the default header comment to the input file.
  • add_header_information: If the information header should be exported to the input file Contains creation date, git details of BeamMe, CubitPy and original application which created the input file if available.
  • add_footer_application_script: Append the application script which creates the input files as a comment at the end of the input file.
  • validate: Validate if the created input file is compatible with 4C with FourCIPP.
  • validate_sections_only: Validate each section independently. Required sections are no longer required, but the sections must be valid.
  • sort_function: A function which sorts the sections of the input file.
  • fourcipp_yaml_style: If True, the input file is written in the fourcipp yaml style.
def contains_mesh_based_geometry_exodus(self) -> bool:
398    def contains_mesh_based_geometry_exodus(self) -> bool:
399        """Check if the input file contains exodus mesh-based geometry.
400
401        Returns:
402            True if the input file contains exodus mesh-based geometry, False otherwise.
403        """
404        return self._contains_mesh_based_geometry([".exo", ".e"])

Check if the input file contains exodus mesh-based geometry.

Returns:

True if the input file contains exodus mesh-based geometry, False otherwise.

def contains_mesh_based_geometry_vtu(self) -> bool:
406    def contains_mesh_based_geometry_vtu(self) -> bool:
407        """Check if the input file contains VTU mesh-based geometry.
408
409        Returns:
410            True if the input file contains VTU mesh-based geometry, False otherwise.
411        """
412        return self._contains_mesh_based_geometry([".vtu"])

Check if the input file contains VTU mesh-based geometry.

Returns:

True if the input file contains VTU mesh-based geometry, False otherwise.