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.
element_type_id_to_data: dict[typing.Any, beamme.four_c.element_data.FourCElementData]
boundary_conditions: dict[str, list[beamme.four_c.boundary_condition_data.FourCBoundaryConditionData]]
@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.