Coverage for mpcforces_extractor\reader\modelreaders.py: 96%

161 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-06 21:34 +0100

1from typing import List, Dict 

2from mpcforces_extractor.datastructure.rigids import MPC, MPC_CONFIG 

3from mpcforces_extractor.datastructure.entities import Element1D, Element, Node 

4from mpcforces_extractor.datastructure.loads import Moment, Force 

5 

6 

7class FemFileReader: 

8 """ 

9 This class is used to read the .fem file and extract the nodes and the rigid elements 

10 """ 

11 

12 element_keywords: List = [ 

13 "CTRIA3", 

14 "CQUAD4", 

15 "CTRIA6", 

16 "CQUAD8", 

17 "CHEXA", 

18 "CPENTA", 

19 "CTETRA", 

20 "CROD", 

21 "CTUBE", 

22 "CBEAM", 

23 "CBAR", 

24 ] 

25 

26 file_path: str = None 

27 file_content: str = None 

28 nodes_id2node: Dict = {} 

29 rigid_elements: List[MPC] = [] 

30 node2property = {} 

31 load_id2load: Dict = {} 

32 blocksize: int = None 

33 

34 def __init__(self, file_path, block_size: int): 

35 self.file_path = file_path 

36 self.blocksize = block_size 

37 self.nodes_id2node = {} 

38 self.rigid_elements = [] 

39 self.node2property = {} 

40 self.file_content = self.__read_lines() 

41 self.__read_nodes() 

42 self.elements_1D = [] 

43 self.elements_3D = [] 

44 self.endGridLine = None 

45 self.endElementLine = None 

46 

47 def __read_lines(self) -> List: 

48 """ 

49 This method reads the lines of the .fem file 

50 """ 

51 # check if the file exists 

52 try: 

53 with open(self.file_path, "r", encoding="utf-8") as file: 

54 return file.readlines() 

55 except FileNotFoundError: 

56 print(f"File {self.file_path} not found") 

57 return [] 

58 

59 def __read_nodes(self): 

60 """ 

61 This method is used to read the nodes from the .fem file 

62 """ 

63 grids_found = False 

64 for i, line in enumerate(self.file_content): 

65 if line.startswith("GRID"): 

66 grids_found = True 

67 line_content = self.split_line(line) 

68 node_id = int(line_content[1]) 

69 x = self.__node_coord_parser(line_content[3]) 

70 y = self.__node_coord_parser(line_content[4]) 

71 z = self.__node_coord_parser(line_content[5]) 

72 node = Node(node_id, [x, y, z]) 

73 self.nodes_id2node[node.id] = node 

74 if grids_found and not line.startswith("GRID"): 

75 self.endGridLine = i 

76 break 

77 

78 def __node_coord_parser(self, coord_str: str) -> float: 

79 """ 

80 This method is used to parse the node coordinates 

81 """ 

82 # Problem is the expenential notation without the E in it 

83 before_dot = coord_str.split(".")[0] 

84 after_dot = coord_str.split(".")[1] 

85 if "-" in after_dot: 

86 return float(before_dot + "." + after_dot.replace("-", "e-")) 

87 if "+" in after_dot: 

88 return float(before_dot + "." + after_dot.replace("+", "e+")) 

89 

90 return float(coord_str) 

91 

92 def split_line(self, line: str) -> List: 

93 """ 

94 This method is used to split a line into blocks of blocksize, and 

95 remove the newline character and strip the content of the block 

96 """ 

97 line_content = [ 

98 line[j : j + self.blocksize] for j in range(0, len(line), self.blocksize) 

99 ] 

100 

101 if "\n" in line_content: 

102 line_content.remove("\n") 

103 

104 line_content = [line.strip() for line in line_content] 

105 return line_content 

106 

107 def create_entities(self): 

108 """ 

109 This method is used to build the node2property dictionary. 

110 Its the main info needed for getting the forces by property 

111 """ 

112 elements_found = False 

113 for i, _ in enumerate(self.file_content[self.endGridLine :]): 

114 line = self.file_content[i] 

115 

116 if line.strip().startswith("+") and elements_found: 

117 continue 

118 

119 line_content = self.split_line(line) 

120 if len(line_content) < 2: 

121 continue 

122 line_content = self.split_line(line) 

123 element_keyword = line_content[0] 

124 

125 if element_keyword not in self.element_keywords: 

126 continue 

127 

128 elements_found = True 

129 

130 property_id = int(line_content[2]) 

131 

132 if element_keyword in ["CBEAM", "CBAR", "CTUBE", "CROD"]: 

133 element_id = int(line_content[1]) 

134 node1 = Node.node_id2node[int(line_content[3])] 

135 node2 = Node.node_id2node[int(line_content[4])] 

136 element = Element1D( 

137 element_id, 

138 property_id, 

139 node1, 

140 node2, 

141 ) 

142 self.elements_1D.append(element) 

143 nodes = [node1, node2] 

144 

145 else: 

146 node_ids = line_content[3:] 

147 element_id = int(line_content[1]) 

148 

149 if i < len(self.file_content) - 1: 

150 i += 1 

151 line2 = self.file_content[i] 

152 while line2.startswith("+"): 

153 line_content = self.split_line(line2) 

154 node_ids += self.split_line(line2)[1:] 

155 i += 1 

156 line2 = self.file_content[i] 

157 

158 nodes = [self.nodes_id2node[int(node_id)] for node_id in node_ids] 

159 self.elements_3D.append(Element(element_id, property_id, nodes)) 

160 

161 for node in nodes: 

162 self.node2property[node.id] = property_id 

163 

164 def get_rigid_elements(self): 

165 """ 

166 This method is used to extract the rigid elements from the .fem file 

167 Currently: only RBE2 / RBE3 is supported 

168 """ 

169 

170 element_keywords = ["RBE2", "RBE3"] 

171 

172 for i, _ in enumerate(self.file_content[self.endGridLine :]): 

173 line = self.file_content[i] 

174 

175 if line.split(" ")[0] not in element_keywords: 

176 continue 

177 

178 line_content = self.split_line(line) 

179 element_id: int = int(line_content[1]) 

180 dofs: int = None 

181 node_ids: List = [] 

182 master_node = None 

183 mpc_config = None 

184 

185 if line.startswith("RBE3"): 

186 mpc_config = MPC_CONFIG.RBE3 

187 master_node_id = int(line_content[3]) 

188 master_node = self.nodes_id2node[master_node_id] 

189 dofs = int(line_content[4]) 

190 node_ids = line_content[7:] 

191 

192 elif line.startswith("RBE2"): 

193 mpc_config = MPC_CONFIG.RBE2 

194 master_node_id = int(line_content[2]) 

195 master_node = self.nodes_id2node[master_node_id] 

196 dofs = int(line_content[3]) 

197 node_ids = line_content[4:] 

198 if i < len(self.file_content) - 1: 

199 i += 1 

200 line2 = self.file_content[i] 

201 while line2.startswith("+"): 

202 line_content = self.split_line(line2) 

203 for j, _ in enumerate(line_content): 

204 if j == 0: 

205 continue 

206 if "." in line_content[j]: 

207 j += 1 

208 continue 

209 node_ids.append(line_content[j]) 

210 

211 i += 1 

212 if i == len(self.file_content) - 1: 

213 break 

214 line2 = self.file_content[i] 

215 

216 # remove anything with a . in nodes, those are the weights 

217 node_ids = [ 

218 int(node) for node in node_ids if "." not in node and node != "" 

219 ] 

220 # cast to int 

221 nodes = [self.nodes_id2node[id] for id in node_ids] 

222 self.rigid_elements.append( 

223 MPC( 

224 element_id=element_id, 

225 mpc_config=mpc_config, 

226 master_node=master_node, 

227 nodes=nodes, 

228 dofs=dofs, 

229 ) 

230 ) 

231 

232 def get_loads(self): 

233 """ 

234 This method is used to extract the loads from the .fem file (currently forces and moments) 

235 """ 

236 for i, _ in enumerate(self.file_content[self.endElementLine :]): 

237 line = self.file_content[i] 

238 

239 if line.startswith("FORCE"): 

240 line_content = self.split_line(line) 

241 force_id = int(line_content[1]) 

242 node_id = int(line_content[2]) 

243 system_id = int(line_content[3]) 

244 scale_factor = float(line_content[4]) 

245 components_from_file = line_content[5:8] 

246 

247 force = Force( 

248 force_id=force_id, 

249 node_id=node_id, 

250 system_id=system_id, 

251 scale_factor=scale_factor, 

252 compenents_from_file=components_from_file, 

253 ) 

254 FemFileReader.load_id2load[force_id] = force 

255 

256 if line.startswith("MOMENT"): 

257 line_content = self.split_line(line) 

258 moment_id = int(line_content[1]) 

259 node_id = int(line_content[2]) 

260 system_id = int(line_content[3]) 

261 scale_factor = float(line_content[4]) 

262 components_from_file = line_content[5:8] 

263 

264 moment = Moment( 

265 moment_id=moment_id, 

266 node_id=node_id, 

267 system_id=system_id, 

268 scale_factor=scale_factor, 

269 compenents_from_file=components_from_file, 

270 ) 

271 FemFileReader.load_id2load[moment_id] = moment