Coverage for src/beamme/four_c/element_data.py: 95%
39 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-28 15:20 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-28 15:20 +0000
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"""Define a data class for 4C element data."""
24from dataclasses import dataclass as _dataclass
25from dataclasses import field as _field
26from typing import Any as _Any
28import numpy as _np
29from numpy.typing import NDArray as _NDArray
31from beamme.utils.data_structures import (
32 compare_nested_dicts_or_lists as _compare_nested_dicts_or_lists,
33)
36@_dataclass(eq=False)
37class FourCElementData:
38 """Class that contains the data for a 4C element block."""
40 four_c_type: str
41 four_c_cell: str
42 element_technology: dict[str, _Any] = _field(default_factory=dict)
44 def get_block_dict(
45 self, i_block: int, i_material: int, additional_data: dict | None = None
46 ) -> dict:
47 """Return the dictionary containing the data for this element block.
49 Args:
50 i_block: The index of the element block.
51 i_material: The index of the material. If -1, no material will be assigned
52 to this element block.
53 additional_data: Additional data to add to the element block dictionary.
55 Returns: The dictionary to write this element block data to the input file for
56 mesh based output.
57 """
58 return {
59 "ID": i_block,
60 self.four_c_type: {
61 self.four_c_cell: {
62 **({"MAT": i_material + 1} if i_material != -1 else {}),
63 **self.element_technology,
64 **(additional_data or {}),
65 }
66 },
67 }
69 def get_yaml_dict(
70 self, element_id, connectivity, element_material_id, additional_element_data
71 ) -> dict:
72 """Return the dictionary to write this element data to a yaml element definition
73 in the input file."""
74 return {
75 "id": element_id + 1,
76 "cell": {
77 "type": self.four_c_cell,
78 "connectivity": connectivity + 1,
79 },
80 "data": {
81 "type": self.four_c_type,
82 **(
83 {"MAT": element_material_id + 1}
84 if element_material_id != -1
85 else {}
86 ),
87 **self.element_technology,
88 **(additional_element_data or {}),
89 },
90 }
92 def __eq__(self, other) -> bool:
93 """Check if two 4C element data objects are equal."""
94 if not isinstance(other, FourCElementData):
95 return False
96 if self.four_c_cell != other.four_c_cell:
97 return False
98 if self.four_c_type != other.four_c_type:
99 return False
100 if not _compare_nested_dicts_or_lists(
101 self.element_technology, other.element_technology
102 ):
103 return False
104 return True
107def four_c_element_data_from_yaml_dict(
108 yaml_dict: dict,
109) -> tuple[FourCElementData, int, _NDArray, int]:
110 """Extract the 4C element data from a yaml element definition in the input file.
112 Args:
113 yaml_dict: The yaml element definition in the input file, will be modified in place.
115 Returns:
116 A tuple containing the 4C element data, the element ID, the connectivity, and the material ID.
117 """
118 element_id = yaml_dict["id"]
119 connectivity = _np.array(yaml_dict["cell"]["connectivity"], dtype=int) - 1
120 four_c_cell = yaml_dict["cell"]["type"]
122 # Since we directly store `yaml_dict["data"]` as the element technology data,
123 # the material ID and four_c_type have to be removed from the `yaml_dict["data"]`
124 # dictionary, thus the `pop`.
125 material_id = yaml_dict["data"].pop("MAT", 0) - 1
126 four_c_type = yaml_dict["data"].pop("type")
128 data = FourCElementData(
129 four_c_type=four_c_type,
130 four_c_cell=four_c_cell,
131 element_technology=yaml_dict["data"],
132 )
134 return data, element_id, connectivity, material_id
137def four_c_element_data_from_exo_dict(exo_dict: dict) -> tuple[FourCElementData, int]:
138 """Extract the 4C element data from an exodus element definition in the input file.
140 Args:
141 exo_dict: The exodus element definition in the input file, will be modified in
142 place.
144 Returns:
145 A tuple containing the 4C element data and the material ID.
146 """
147 # First, we have to remove the ID entry
148 exo_dict.pop("ID")
150 four_c_type = list(exo_dict.keys())[0]
151 four_c_cell = list(exo_dict[four_c_type].keys())[0]
152 element_technology = exo_dict[four_c_type][four_c_cell]
153 material_id = element_technology.pop("MAT", 0) - 1
155 data = FourCElementData(
156 four_c_type=four_c_type,
157 four_c_cell=four_c_cell,
158 element_technology=element_technology,
159 )
161 return data, material_id