Coverage for fiqus/pre_processors/PreProcessCCT.py: 93%

136 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-12-25 02:54 +0100

1import math 

2import os 

3import timeit 

4import json 

5import gmsh 

6import numpy as np 

7from fiqus.utils.Utils import FilesAndFolders as uff 

8from fiqus.utils.Utils import GmshUtils 

9from fiqus.data.DataWindingsCCT import WindingsInformation # for volume information 

10 

11 

12class Pre_Process: 

13 def __init__(self, fdm, verbose=True): 

14 """ 

15 Class to preparing brep files by adding terminals. 

16 :param fdm: FiQuS data model 

17 :param verbose: If True more information is printed in python console. 

18 """ 

19 self.cctdm = fdm.magnet 

20 self.model_folder = os.path.join(os.getcwd()) 

21 self.magnet_name = fdm.general.magnet_name 

22 winding_info_file = os.path.join(self.model_folder, f'{self.magnet_name}.wi') 

23 self.cctwi = uff.read_data_from_yaml(winding_info_file, WindingsInformation) 

24 self.verbose = verbose 

25 self.gu = GmshUtils(self.model_folder, self.verbose) 

26 self.gu.initialize() 

27 

28 def calculate_normals(self, gui=False): 

29 """ 

30 Calculates normals for the cct channel directions, i.e. along winding direction, along radial direction of the former (height) and along axial direction (width). Normals are saved to a .json 

31 file and used later for post-processing of magnetic field into components along channel length, height and width. Note that this function does not give the correct 'sign of normals', i.e. 

32 normals facing inwards or outwards of the surface are not properly distinguished. The normals along the length are not reliable and only along the height and width are used for calculations and 

33 the field along the length is taken as a remaining field. 

34 This function needs full geom_generators .brep file and volume information files (.vi) for each individual powered volume. 

35 :param gui: if True, the gmsh graphical user interface is shown at the end and normals are displayed as a view 

36 :return: Nothing, a file for each powered geom_generators brep is 

37 """ 

38 if self.verbose: 

39 print('Calculating Normals Started') 

40 start_time = timeit.default_timer() 

41 gmsh.open(os.path.join(self.model_folder, f'{self.magnet_name}.brep')) 

42 

43 def _calc_normals_dir(tags_for_normals_in, surfs_idx, surfs_scale): 

44 v_to_suf = [[0, 1, 2, 3], [0, 1, 5, 4], [4, 5, 6, 7], [3, 7, 6, 2], [0, 3, 7, 4], [1, 5, 6, 2]] 

45 norm_e_x = [] # normal along x of volume 

46 norm_e_y = [] 

47 norm_e_z = [] 

48 norm_dict = {} 

49 normals_view = [] # this remains an empty list if view is False 

50 coor_e_x = [] # coordinate x of the center of volume 

51 coor_e_y = [] 

52 coor_e_z = [] 

53 for vol_tag in tags_for_normals_in: 

54 all_surf_tags = gmsh.model.getAdjacencies(3, vol_tag)[1] 

55 surf_tags = [all_surf_tags[index] for index in surfs_idx] 

56 norm = [] 

57 node_coord = [] 

58 vol_line_tags = [] 

59 for surf_tag in all_surf_tags: 

60 line_tags_new = gmsh.model.getAdjacencies(2, surf_tag)[1] 

61 for line_tag in line_tags_new: 

62 if line_tag not in vol_line_tags: 

63 vol_line_tags.append(line_tag) 

64 point_tags = [] 

65 for line_tag in vol_line_tags: 

66 point_tags_new = gmsh.model.getAdjacencies(1, line_tag)[1] 

67 for point_tag in point_tags_new: 

68 if point_tag not in point_tags: 

69 point_tags.append(int(point_tag)) 

70 for surf_i, surf_tag, scale in zip(surfs_idx, surf_tags, surfs_scale): 

71 p_idx = v_to_suf[surf_i] 

72 s_node_coord = [] 

73 for p_i in p_idx: 

74 xmin, ymin, zmin, xmax, ymax, zmax = gmsh.model.occ.getBoundingBox(0, point_tags[p_i]) 

75 s_node_coord.append((xmin + xmax) / 2) 

76 s_node_coord.append((ymin + ymax) / 2) 

77 s_node_coord.append((zmin + zmax) / 2) 

78 parametricCoord = gmsh.model.getParametrization(2, surf_tag, s_node_coord) 

79 s_norm = gmsh.model.getNormal(surf_tag, parametricCoord) 

80 norm.extend(scale*s_norm) 

81 node_coord.extend(s_node_coord) 

82 coor_s_x = [] # coordinates surface x 

83 coor_s_y = [] 

84 coor_s_z = [] 

85 norm_s_x = [] # normals surface x 

86 norm_s_y = [] 

87 norm_s_z = [] 

88 

89 for i in range(0, len(node_coord), 3): 

90 coor_s_x.append(node_coord[i]) 

91 coor_s_y.append(node_coord[i+1]) 

92 coor_s_z.append(node_coord[i+2]) 

93 norm_s_x.append(norm[i]) 

94 norm_s_y.append(norm[i+1]) 

95 norm_s_z.append(norm[i+2]) 

96 

97 coor_e_x.append(np.mean(coor_s_x)) 

98 coor_e_y.append(np.mean(coor_s_y)) 

99 coor_e_z.append(np.mean(coor_s_z)) 

100 # norm_e_x.append(np.mean(norm_s_x)) 

101 # norm_e_y.append(np.mean(norm_s_y)) 

102 # norm_e_z.append(np.mean(norm_s_z)) 

103 

104 # norm_e_x.append(np.sqrt(np.sum(np.square(norm_s_x)))/(2*np.sqrt(2))) 

105 # norm_e_y.append(np.sqrt(np.sum(np.square(norm_s_y)))/(2*np.sqrt(2))) 

106 # norm_e_z.append(np.sqrt(np.sum(np.square(norm_s_z)))/(2*np.sqrt(2))) 

107 v_x = np.sum(norm_s_x) 

108 v_y = np.sum(norm_s_y) 

109 v_z = np.sum(norm_s_z) 

110 ampl = math.sqrt(v_x**2 + v_y**2 + v_z**2) 

111 

112 norm_e_x.append(v_x/ampl) 

113 norm_e_y.append(v_y/ampl) 

114 norm_e_z.append(v_z/ampl) 

115 

116 for i in range(len(coor_e_x)): 

117 normals_view.append(coor_e_x[i]) 

118 normals_view.append(coor_e_y[i]) 

119 normals_view.append(coor_e_z[i]) 

120 normals_view.append(norm_e_x[i]) 

121 normals_view.append(norm_e_y[i]) 

122 normals_view.append(norm_e_z[i]) 

123 norm_dict['x'] = coor_e_x 

124 norm_dict['y'] = coor_e_y 

125 norm_dict['z'] = coor_e_z 

126 norm_dict['n_x'] = norm_e_x 

127 norm_dict['n_y'] = norm_e_y 

128 norm_dict['n_z'] = norm_e_z 

129 return norm_dict, normals_view 

130 """ 

131 This is helper function called in a loop below. 

132 """ 

133 max_tag = 0 

134 for f_name in self.cctwi.w_names+self.cctwi.f_names: 

135 vol_tags = json.load(open(os.path.join(self.model_folder, f'{f_name}.vi'))) 

136 export_tags = vol_tags['export'] 

137 tags_for_normals = [e + max_tag for e in export_tags] 

138 max_tag = np.max(vol_tags['all']) + max_tag 

139 surfs_idx_l = [0, 2] # along length of the former groove 

140 if f_name in self.cctwi.w_names: 

141 surfs_scale_l = [1, 1] 

142 elif f_name in self.cctwi.f_names: 

143 surfs_scale_l = [1, -1] # change direction for fqpl 

144 norm_l, norm_view_l = _calc_normals_dir(tags_for_normals, surfs_idx_l, surfs_scale_l) 

145 surfs_idx_h = [1, 3] # along height of the former groove 

146 surfs_scale_h = [1, -1] 

147 norm_h, norm_view_h = _calc_normals_dir(tags_for_normals, surfs_idx_h, surfs_scale_h) 

148 surfs_idx_w = [4, 5] # along width of the former groove 

149 surfs_scale_w = [1, -1] 

150 norm_w, norm_view_w = _calc_normals_dir(tags_for_normals, surfs_idx_w, surfs_scale_w) 

151 normals_dict = {'normals_l': norm_l, 'normals_h': norm_h, 'normals_w': norm_w} 

152 json.dump(normals_dict, open(f"{os.path.join(self.model_folder, f_name)}.normals", 'w')) 

153 if gui: 

154 normals_all = [norm_view_l, norm_view_h, norm_view_w] 

155 self.__add_normals_view(f_name, normals_all) 

156 if self.verbose: 

157 print(f'Calculating Normals Took {timeit.default_timer() - start_time:.2f} s') 

158 if gui: 

159 self.gu.launch_interactive_GUI() 

160 

161 @staticmethod 

162 def __add_normals_view(name, normals_all, norm_list=[0, 1, 2]): 

163 """ 

164 THis adds new view in gmsh. 

165 :param name: name of view 

166 :param normals_all: dictionary with normals 

167 :param norm_list: which normals to plot. Default is: [0, 1, 2] corresponds to [n_l, n_h, n_w]. If this array is shorter the corresponding normals are skipped in views. 

168 :return: 

169 """ 

170 norm_names_all = [f"{name}_n_l", f"{name}_n_h", f"{name}_n_w"] 

171 norm_names = [norm_names_all[index] for index in norm_list] 

172 normals = [normals_all[index] for index in norm_list] 

173 for view_name, view_data in zip(norm_names, normals): 

174 gmsh.view.addListData(gmsh.view.add(view_name), "VP", len(view_data) // 6, view_data) 

175 gmsh.model.occ.synchronize()