Coverage for fiqus/parsers/ParserPOS.py: 98%

129 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2024-05-20 03:24 +0200

1import re 

2import inspect 

3import pandas as pd 

4 

5 

6class ParserPOS: 

7 

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'} 

18 

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

37 

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'])] 

43 

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 

56 

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

75 

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}') 

87 

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) 

93 

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) 

99 

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) 

111 

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) 

123 

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) 

143 

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) 

156 

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

166 

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

171 

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 

182 

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) 

189 

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 

193 

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] 

203 

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