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

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.""" 

23 

24from dataclasses import dataclass as _dataclass 

25from dataclasses import field as _field 

26from typing import Any as _Any 

27 

28import numpy as _np 

29from numpy.typing import NDArray as _NDArray 

30 

31from beamme.utils.data_structures import ( 

32 compare_nested_dicts_or_lists as _compare_nested_dicts_or_lists, 

33) 

34 

35 

36@_dataclass(eq=False) 

37class FourCElementData: 

38 """Class that contains the data for a 4C element block.""" 

39 

40 four_c_type: str 

41 four_c_cell: str 

42 element_technology: dict[str, _Any] = _field(default_factory=dict) 

43 

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. 

48 

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. 

54 

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 } 

68 

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 } 

91 

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 

105 

106 

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. 

111 

112 Args: 

113 yaml_dict: The yaml element definition in the input file, will be modified in place. 

114 

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"] 

121 

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") 

127 

128 data = FourCElementData( 

129 four_c_type=four_c_type, 

130 four_c_cell=four_c_cell, 

131 element_technology=yaml_dict["data"], 

132 ) 

133 

134 return data, element_id, connectivity, material_id 

135 

136 

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. 

139 

140 Args: 

141 exo_dict: The exodus element definition in the input file, will be modified in 

142 place. 

143 

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") 

149 

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 

154 

155 data = FourCElementData( 

156 four_c_type=four_c_type, 

157 four_c_cell=four_c_cell, 

158 element_technology=element_technology, 

159 ) 

160 

161 return data, material_id