Coverage for src/beamme/four_c/element_data.py: 94%
31 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-08 11:03 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-08 11:03 +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_legacy_dict(
45 self, element_id, connectivity, element_material_id, additional_element_data
46 ) -> dict:
47 """Return the dictionary to write this element data to a legacy element
48 definition in the input file."""
50 return {
51 "id": element_id + 1,
52 "cell": {
53 "type": self.four_c_cell,
54 "connectivity": connectivity + 1,
55 },
56 "data": {
57 "type": self.four_c_type,
58 **(
59 {"MAT": element_material_id + 1}
60 if element_material_id != -1
61 else {}
62 ),
63 **self.element_technology,
64 **(additional_element_data or {}),
65 },
66 }
68 def __eq__(self, other) -> bool:
69 """Check if two 4C element data objects are equal."""
70 if not isinstance(other, FourCElementData):
71 return False
72 if self.four_c_cell != other.four_c_cell:
73 return False
74 if self.four_c_type != other.four_c_type:
75 return False
76 if not _compare_nested_dicts_or_lists(
77 self.element_technology, other.element_technology
78 ):
79 return False
80 return True
83def four_c_element_data_from_legacy_dict(
84 legacy_dict: dict,
85) -> tuple[FourCElementData, int, _NDArray, int]:
86 """Extract the 4C element data from a legacy element definition in the
87 input file.
89 Args:
90 legacy_dict: The legacy element definition in the input file, will be modified in place.
92 Returns:
93 A tuple containing the 4C element data, the element ID, the connectivity, and the material ID.
94 """
96 element_id = legacy_dict["id"]
97 connectivity = _np.array(legacy_dict["cell"]["connectivity"], dtype=int) - 1
98 four_c_cell = legacy_dict["cell"]["type"]
100 # Since we directly store `legacy_dict["data"]` as the element technology data,
101 # the material ID and four_c_type have to be removed from the `legacy_dict["data"]`
102 # dictionary, thus the `pop`.
103 material_id = legacy_dict["data"].pop("MAT", 0) - 1
104 four_c_type = legacy_dict["data"].pop("type")
106 data = FourCElementData(
107 four_c_type=four_c_type,
108 four_c_cell=four_c_cell,
109 element_technology=legacy_dict["data"],
110 )
112 return data, element_id, connectivity, material_id