Coverage for mpcforces_extractor\reader\modelreaders.py: 96%
161 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-01 11:08 +0100
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-01 11:08 +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
7class FemFileReader:
8 """
9 This class is used to read the .fem file and extract the nodes and the rigid elements
10 """
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 ]
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
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
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 []
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
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+"))
90 return float(coord_str)
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 ]
101 if "\n" in line_content:
102 line_content.remove("\n")
104 line_content = [line.strip() for line in line_content]
105 return line_content
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]
116 if line.strip().startswith("+") and elements_found:
117 continue
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]
125 if element_keyword not in self.element_keywords:
126 continue
128 elements_found = True
130 property_id = int(line_content[2])
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]
145 else:
146 node_ids = line_content[3:]
147 element_id = int(line_content[1])
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]
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))
161 for node in nodes:
162 self.node2property[node.id] = property_id
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 """
170 element_keywords = ["RBE2", "RBE3"]
172 for i, _ in enumerate(self.file_content[self.endGridLine :]):
173 line = self.file_content[i]
175 if line.split(" ")[0] not in element_keywords:
176 continue
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
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:]
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])
211 i += 1
212 if i == len(self.file_content) - 1:
213 break
214 line2 = self.file_content[i]
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 )
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]
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]
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
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]
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