Coverage for fiqus/parsers/ParserPOS.py: 98%
129 statements
« prev ^ index » next coverage.py v7.4.4, created at 2025-01-14 02:37 +0100
« prev ^ index » next coverage.py v7.4.4, created at 2025-01-14 02:37 +0100
1import re
2import inspect
3import pandas as pd
6class ParserPOS:
8 def __init__(self, pos_file_path):
9 """
10 Read pos file and returns its content as object attribute .data_dict that is a dictionary.
11 :param pos_file_path: Full path to .pos file, including file name and extension.
12 """
13 self._mesh_format_markers = {'s': '$MeshFormat', 'e': '$EndMeshFormat'}
14 self._nodes_markers = {'s': '$Nodes', 'e': '$EndNodes'}
15 self._elements_markers = {'s': '$Elements', 'e': '$EndElements'}
16 self._elements_node_data_markers = {'s': '$ElementNodeData', 'e': '$EndElementNodeData'}
17 self._physical_name_markers = {'s': 'PhysicalNames', 'e': '$EndPhysicalNames'}
19 with open(pos_file_path) as f:
20 self._contents = f.read()
21 # node properteis
22 self._node_numbers = []
23 self._node_coordinates = []
24 # element properties
25 self._element_numbers = []
26 self._element_types = []
27 self._element_number_of_tags = []
28 self._element_physical_tags = []
29 self._element_elementary_tags = []
30 self._element_node_numbers = []
31 # elements_node_data properties
32 self._data_element_tags = []
33 self._data_num_nodes_per_element = []
34 self._values = []
35 # parse the content (output is in this class attributes)
36 self._parse()
38 def __get_content(self, markers_dict):
39 """
40 Gets text string between two markers specified in markers_dict
41 """
42 return self._contents[self._contents.find(markers_dict['s']) + len(markers_dict['s']):self._contents.find(markers_dict['e'])]
44 @staticmethod
45 def __get_lines(data_str, empty_lines=[0, -1]):
46 """
47 Converts text string into a list of lines
48 """
49 data_str = re.sub('\n', "'", data_str)
50 data_str = re.sub('"', '', data_str)
51 str_list = re.split("'", data_str)
52 for empty_line in empty_lines:
53 if not str_list.pop(empty_line) == '':
54 raise ValueError('Error in parsing lines')
55 return str_list
57 @staticmethod
58 def __list_from_list_at_pos(dest_list, source_list, d_type, pos):
59 """
60 This one is a bit complex. It converts a supplied in source list and appends it destination list.
61 It uses data type and position to append to the right type to the destination list from the right place in the source list.
62 Position could be an integer, a list of integers or a tuple with a reference variables to figure out positions from them.
63 """
64 if isinstance(pos, int):
65 dest_list.append(d_type(source_list[pos]))
66 elif isinstance(pos, list):
67 dest_list.append([d_type(source_list[p]) for p in pos])
68 elif isinstance(pos, tuple):
69 offset = pos[0]
70 dimension = pos[1]
71 ref_list = pos[2]
72 current_num_elem = ref_list[len(dest_list)] # get current element
73 pos = list(range(offset, dimension * current_num_elem + offset))
74 dest_list.append([d_type(source_list[p]) for p in pos])
76 def __parse_lines(self, lines, list_of_outputs_lists, list_of_data_types, list_of_positions, len_check, ):
77 """
78 Simply loop through lines and append to class attributes lists. Basic error check if number of lines in the list matches that declared in the file.
79 """
80 for line in lines:
81 values = re.split(' ', line)
82 for dest_list, d_type, pos in zip(list_of_outputs_lists, list_of_data_types, list_of_positions):
83 self.__list_from_list_at_pos(dest_list, values, d_type, pos)
84 for out_list in list_of_outputs_lists:
85 if not len_check == len(out_list):
86 raise ValueError(f'Error in parsing {inspect.stack()[1].function}')
88 def _mesh_format(self):
89 """
90 Parse mesh_generators field and assign it to the class attribute
91 """
92 self.mesh_format = self.__get_content(self._mesh_format_markers)
94 def _physical_names(self):
95 """
96 Parse physical_names field and assign it to the class attribute
97 """
98 self.physical_names = self.__get_content(self._physical_name_markers)
100 def _nodes(self):
101 """
102 Parse nodes and assign it to the class attributes
103 """
104 data_str = self.__get_content(self._nodes_markers)
105 lines = self.__get_lines(data_str)
106 self._number_of_nodes = int(lines.pop(0))
107 list_of_outputs_lists = [self._node_numbers, self._node_coordinates]
108 list_of_data_types = [int, float]
109 list_of_positions = [0, [1, 2, 3]]
110 self.__parse_lines(lines, list_of_outputs_lists, list_of_data_types, list_of_positions, self._number_of_nodes)
112 def _elements(self):
113 """
114 Parse elements and assign it to the class attributes
115 """
116 data_str = self.__get_content(self._elements_markers)
117 lines = self.__get_lines(data_str)
118 self._number_of_elements = int(lines.pop(0))
119 list_of_outputs_lists = [self._element_numbers, self._element_types, self._element_number_of_tags, self._element_physical_tags, self._element_elementary_tags, self._element_node_numbers]
120 list_of_data_types = [int, int, int, int, int, int]
121 list_of_positions = [0, 1, 2, 3, 4, (5, 1, self._data_num_nodes_per_element)]
122 self.__parse_lines(lines, list_of_outputs_lists, list_of_data_types, list_of_positions, self._number_of_elements)
124 def _elements_node_data(self):
125 """
126 Parse elements data and assign it to the class attributes
127 """
128 data_str = self.__get_content(self._elements_node_data_markers)
129 lines = self.__get_lines(data_str)
130 self._numStringTags = int(lines.pop(0))
131 self._stringTags = str(lines.pop(0))
132 self._numRealTags = int(lines.pop(0))
133 self._realTags = float(lines.pop(0))
134 self._numIntegerTags = int(lines.pop(0))
135 self._integerTags = int(lines.pop(0))
136 self._entityDim = int(lines.pop(0))
137 self._number_of_elements_data = int(lines.pop(0))
138 self._not_sure = int(lines.pop(0)) # not sure what this number is
139 list_of_outputs_lists = [self._data_element_tags, self._data_num_nodes_per_element, self._values]
140 list_of_data_types = [int, int, float]
141 list_of_positions = [0, 1, (2, self._entityDim, self._data_num_nodes_per_element)]
142 self.__parse_lines(lines, list_of_outputs_lists, list_of_data_types, list_of_positions, self._number_of_elements_data)
144 def __element_for_node(self):
145 self._elem_list_for_nodes = []
146 self._index_elem = []
147 element_node_numbers = pd.DataFrame(self._element_node_numbers)
148 for node in self._node_numbers:
149 index = pd.Index
150 i = -1
151 while index.empty:
152 i += 1
153 index = element_node_numbers[element_node_numbers[i] == node].index
154 self._elem_list_for_nodes.append(index[0])
155 self._index_elem.append(i)
157 # element_node_numbers = pd.DataFrame(self._element_node_numbers)
158 # for node in self._node_numbers:
159 # column = []
160 # for i in range(len(element_node_numbers.loc[0, :])):
161 # index = element_node_numbers[element_node_numbers[i] == node].index
162 # column.append(index[0] if not index.empty else float('nan'))
163 # first_elem = np.nanmin(column)
164 # self._elem_list_for_nodes.append(int(first_elem))
165 # self._index_elem.append(column.index(first_elem))
167 # for node in self._node_numbers:
168 # self._elem_list_for_nodes.append([i for i in range(len(self._element_node_numbers))
169 # if node in self._element_node_numbers[i]][0])
170 # self._index_elem.append(self._element_node_numbers[self._elem_list_for_nodes[-1]].index(node))
172 # for node in self._node_numbers:
173 # n_idx = -1
174 # e_idx = 0
175 # while n_idx == -1:
176 # try:
177 # n_idx = self._element_node_numbers[e_idx].index(node)
178 # self._elem_list_for_nodes.append(e_idx)
179 # self._index_elem.append(n_idx)
180 # except ValueError:
181 # e_idx += 1
183 def _node_values_dict(self):
184 self.regions_dict = {}
185 for reg_tag, elem_of_reg in zip(self._element_physical_tags, self._data_element_tags):
186 if reg_tag not in self.regions_dict:
187 self.regions_dict[reg_tag] = []
188 self.regions_dict[reg_tag].append(elem_of_reg)
190 self.elements_dict = {}
191 for elem_tag, nodes_of_elem in zip(self._data_element_tags, self._element_node_numbers):
192 self.elements_dict[elem_tag] = nodes_of_elem
194 self.nodes_dict = {}
195 for n_num, coors, elem_tag, idx in zip(self._node_numbers, self._node_coordinates,
196 self._elem_list_for_nodes, self._index_elem):
197 self.nodes_dict[n_num] = {}
198 for i, key in zip(range(self._entityDim), ['x', 'y', 'z']):
199 self.nodes_dict[n_num][key] = coors[i]
200 for i, key in zip(range(self._entityDim), ['Vx', 'Vy', 'Vz']):
201 self.nodes_dict[n_num][key] = self._values[elem_tag][idx * self._entityDim + i]
202 self.nodes_dict[n_num]['e'] = self._data_element_tags[elem_tag]
204 def _parse(self):
205 """
206 Call all parsing functions in the right sequence.
207 """
208 self._mesh_format()
209 self._physical_names()
210 self._nodes()
211 self._elements_node_data() # this one needs to be called before _elements method.
212 self._elements()
213 self.__element_for_node()
214 self._node_values_dict()