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
« 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
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()
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'))
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 = []
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])
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))
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)
112 norm_e_x.append(v_x/ampl)
113 norm_e_y.append(v_y/ampl)
114 norm_e_z.append(v_z/ampl)
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()
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()