fiqus 2025.2.0__py3-none-any.whl → 2025.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fiqus/MainFiQuS.py +24 -28
- fiqus/data/DataConductor.py +350 -301
- fiqus/data/DataFiQuS.py +42 -115
- fiqus/data/DataFiQuSCCT.py +150 -150
- fiqus/data/DataFiQuSConductor.py +97 -84
- fiqus/data/DataFiQuSConductorAC_Strand.py +701 -565
- fiqus/data/DataModelCommon.py +439 -0
- fiqus/data/DataMultipole.py +0 -13
- fiqus/data/DataRoxieParser.py +7 -0
- fiqus/data/DataWindingsCCT.py +37 -37
- fiqus/data/RegionsModelFiQuS.py +61 -104
- fiqus/geom_generators/GeometryCCT.py +904 -905
- fiqus/geom_generators/GeometryConductorAC_Strand.py +1863 -1391
- fiqus/geom_generators/GeometryMultipole.py +5 -4
- fiqus/geom_generators/GeometryPancake3D.py +1 -1
- fiqus/getdp_runners/RunGetdpCCT.py +13 -4
- fiqus/getdp_runners/RunGetdpConductorAC_Strand.py +341 -201
- fiqus/getdp_runners/RunGetdpPancake3D.py +2 -2
- fiqus/mains/MainConductorAC_Strand.py +141 -133
- fiqus/mains/MainMultipole.py +6 -5
- fiqus/mains/MainPancake3D.py +3 -4
- fiqus/mesh_generators/MeshCCT.py +209 -209
- fiqus/mesh_generators/MeshConductorAC_Strand.py +709 -656
- fiqus/mesh_generators/MeshMultipole.py +43 -46
- fiqus/parsers/ParserDAT.py +16 -16
- fiqus/parsers/ParserGetDPOnSection.py +212 -212
- fiqus/parsers/ParserGetDPTimeTable.py +134 -134
- fiqus/parsers/ParserMSH.py +53 -53
- fiqus/parsers/ParserPOS.py +214 -214
- fiqus/parsers/ParserRES.py +142 -142
- fiqus/plotters/PlotPythonCCT.py +133 -133
- fiqus/plotters/PlotPythonConductorAC.py +1079 -855
- fiqus/plotters/PlotPythonMultipole.py +18 -18
- fiqus/post_processors/PostProcessCCT.py +444 -440
- fiqus/post_processors/PostProcessConductorAC.py +997 -49
- fiqus/post_processors/PostProcessMultipole.py +19 -19
- fiqus/pre_processors/PreProcessCCT.py +175 -175
- fiqus/pro_material_functions/ironBHcurves.pro +246 -246
- fiqus/pro_templates/combined/CCT_template.pro +275 -274
- fiqus/pro_templates/combined/ConductorAC_template.pro +1474 -1025
- fiqus/pro_templates/combined/Multipole_template.pro +5 -5
- fiqus/utils/Utils.py +12 -7
- {fiqus-2025.2.0.dist-info → fiqus-2025.11.0.dist-info}/METADATA +65 -63
- fiqus-2025.11.0.dist-info/RECORD +86 -0
- {fiqus-2025.2.0.dist-info → fiqus-2025.11.0.dist-info}/WHEEL +1 -1
- tests/test_geometry_generators.py +4 -0
- tests/test_mesh_generators.py +5 -0
- tests/test_solvers.py +41 -4
- tests/utils/fiqus_test_classes.py +15 -6
- tests/utils/generate_reference_files_ConductorAC.py +57 -57
- tests/utils/helpers.py +97 -97
- fiqus-2025.2.0.dist-info/RECORD +0 -85
- {fiqus-2025.2.0.dist-info → fiqus-2025.11.0.dist-info}/LICENSE.txt +0 -0
- {fiqus-2025.2.0.dist-info → fiqus-2025.11.0.dist-info}/top_level.txt +0 -0
|
@@ -65,7 +65,7 @@ class PostProcess:
|
|
|
65
65
|
self.postproc_settings = pd.DataFrame({
|
|
66
66
|
'variables': settings.variables,
|
|
67
67
|
'volumes': settings.volumes})
|
|
68
|
-
if 'compare_to_ROXIE' in settings.
|
|
68
|
+
if 'compare_to_ROXIE' in settings.model_dump():
|
|
69
69
|
self.physical_quantity = 'magnetic_flux_density'
|
|
70
70
|
else:
|
|
71
71
|
self.physical_quantity = 'temperature'
|
|
@@ -83,8 +83,8 @@ class PostProcess:
|
|
|
83
83
|
# self.ax.set_xlim(0, 0.09) # todo
|
|
84
84
|
# self.ax.set_ylim(0, 0.09)
|
|
85
85
|
|
|
86
|
-
if not settings.
|
|
87
|
-
if settings.
|
|
86
|
+
if not settings.model_dump().get('take_average_conductor_temperature', False):
|
|
87
|
+
if settings.model_dump().get('compare_to_ROXIE', False):
|
|
88
88
|
fig2 = plt.figure(2)
|
|
89
89
|
self.ax2 = fig2.add_subplot(projection='3d')
|
|
90
90
|
self.ax2.set_xlabel('x [m]')
|
|
@@ -99,9 +99,9 @@ class PostProcess:
|
|
|
99
99
|
self.ax3 = fig3.add_subplot(projection='3d')
|
|
100
100
|
self.ax3.set_xlabel('x [m]')
|
|
101
101
|
self.ax3.set_ylabel('y [m]')
|
|
102
|
-
self.ax3.set_zlabel('norm(B) [T]' if 'compare_to_ROXIE' in settings.
|
|
102
|
+
self.ax3.set_zlabel('norm(B) [T]' if 'compare_to_ROXIE' in settings.model_dump() else '')
|
|
103
103
|
|
|
104
|
-
if 'compare_to_ROXIE' in settings.
|
|
104
|
+
if 'compare_to_ROXIE' in settings.model_dump() and 'b' in settings.variables:
|
|
105
105
|
b_index = settings.variables.index('b')
|
|
106
106
|
file_to_open = os.path.join(self.solution_folder, f"b_{settings.volumes[b_index]}.pos")
|
|
107
107
|
elif 'T' in settings.variables:
|
|
@@ -178,9 +178,9 @@ class PostProcess:
|
|
|
178
178
|
|
|
179
179
|
def postProcess(self, postproc):
|
|
180
180
|
df_ref = pd.DataFrame()
|
|
181
|
-
model_file_extension = 'EM' if 'compare_to_ROXIE' in postproc.
|
|
181
|
+
model_file_extension = 'EM' if 'compare_to_ROXIE' in postproc.model_dump() else 'TH'
|
|
182
182
|
|
|
183
|
-
if postproc.
|
|
183
|
+
if postproc.model_dump().get('compare_to_ROXIE', False):
|
|
184
184
|
# flag_self_field = False
|
|
185
185
|
# path_map2d = Path(postproc.compare_to_ROXIE, "MQXA_All_" +
|
|
186
186
|
# f"{'WithIron_' if self.data.magnet.geometry.with_iron_yoke else 'NoIron_'}" +
|
|
@@ -196,13 +196,13 @@ class PostProcess:
|
|
|
196
196
|
conductorPositionsList = RoxieParsers.parseCond2d(path_cond2d)
|
|
197
197
|
|
|
198
198
|
# Collect strands coordinates
|
|
199
|
-
strands_x = df_ref['X-POS/MM'] / 1e3 if postproc.
|
|
200
|
-
strands_y = df_ref['Y-POS/MM'] / 1e3 if postproc.
|
|
199
|
+
strands_x = df_ref['X-POS/MM'] / 1e3 if postproc.model_dump().get('compare_to_ROXIE', False) else self.strands['x']
|
|
200
|
+
strands_y = df_ref['Y-POS/MM'] / 1e3 if postproc.model_dump().get('compare_to_ROXIE', False) else self.strands['y']
|
|
201
201
|
|
|
202
202
|
# Probe physical quantity values from view and region areas
|
|
203
203
|
physical_quantity_values = {'x': [], 'y': []}
|
|
204
204
|
cond_areas, current_signs = [], []
|
|
205
|
-
if postproc.
|
|
205
|
+
if postproc.model_dump().get('take_average_conductor_temperature', False):
|
|
206
206
|
half_turns = {name[:-3]: str(values.vol.numbers[i])
|
|
207
207
|
for group, values in self.rm.powered.items() for i, name in enumerate(values.vol.names)}
|
|
208
208
|
self.avg_temperatures = pd.concat([pd.read_csv(os.path.join(self.solution_folder, 'T_avg', 'T_avg_0.txt'),
|
|
@@ -217,7 +217,7 @@ class PostProcess:
|
|
|
217
217
|
self.avg_temperatures.to_csv(os.path.join(self.solution_folder, 'half_turn_temperatures_over_time.csv'), index=False)
|
|
218
218
|
else:
|
|
219
219
|
print(f"Info : {self.data.general.magnet_name} - I n t e r p o l a t i n g . . .")
|
|
220
|
-
print(f"Info : Interpolating {'magnetic flux density' if 'compare_to_ROXIE' in postproc.
|
|
220
|
+
print(f"Info : Interpolating {'magnetic flux density' if 'compare_to_ROXIE' in postproc.model_dump() else 'temperature'} ...")
|
|
221
221
|
|
|
222
222
|
# view = gmsh.view.getTags()[0] if len(postproc.variables) == 1 else self.postproc_settings[
|
|
223
223
|
# (self.postproc_settings['variables'] == self.supported_variables[self.physical_quantity]) &
|
|
@@ -235,7 +235,7 @@ class PostProcess:
|
|
|
235
235
|
|
|
236
236
|
# Probe
|
|
237
237
|
probe_data = gmsh.view.probe(view, strands_x[i], strands_y[i], 0)[0]
|
|
238
|
-
if 'compare_to_ROXIE' in postproc.
|
|
238
|
+
if 'compare_to_ROXIE' in postproc.model_dump():
|
|
239
239
|
physical_quantity_values['x'].append(probe_data[0])
|
|
240
240
|
physical_quantity_values['y'].append(probe_data[1])
|
|
241
241
|
else:
|
|
@@ -243,7 +243,7 @@ class PostProcess:
|
|
|
243
243
|
physical_quantity_values['y'].append(0)
|
|
244
244
|
|
|
245
245
|
# Plot conductor and block identifiers
|
|
246
|
-
if postproc.
|
|
246
|
+
if postproc.model_dump().get('compare_to_ROXIE', False) and postproc.plot_all != 'false':
|
|
247
247
|
if is_new_conductor:
|
|
248
248
|
self.ax.text(df_ref['X-POS/MM'][i] / 1e3, df_ref['Y-POS/MM'][i] / 1e3, str(self.strands['ht'][i]),
|
|
249
249
|
style='italic', bbox={'facecolor': 'blue', 'pad': 3})
|
|
@@ -283,7 +283,7 @@ class PostProcess:
|
|
|
283
283
|
f"{physical_quantity_values['y'][i]:.4f}", # pq_y
|
|
284
284
|
f"{cond_areas[i] / strands_nr * 1e6:.4f}", # area
|
|
285
285
|
f"{current_signs[i] * self.data.power_supply.I_initial / strands_nr:.2f}", # curr
|
|
286
|
-
f"{df_ref['FILL FAC.'][i] if postproc.
|
|
286
|
+
f"{df_ref['FILL FAC.'][i] if postproc.model_dump().get('compare_to_ROXIE', False) else 0:.4f}")) # fill_fac
|
|
287
287
|
|
|
288
288
|
# Save map2d file
|
|
289
289
|
with open(f"{self.model_file}_{model_file_extension}.map2d", 'w') as file:
|
|
@@ -291,19 +291,19 @@ class PostProcess:
|
|
|
291
291
|
file.writelines(content)
|
|
292
292
|
print(f"Info : Map2D file saved.")
|
|
293
293
|
print(f"WARNING : [Map2D] All strand surface areas are equal within a conductor. Refer to the ROXIE map2d file for actual values")
|
|
294
|
-
if not postproc.
|
|
294
|
+
if not postproc.model_dump().get('compare_to_ROXIE', True):
|
|
295
295
|
print(f"WARNING : [Map2D] No data is available for Filling Factor. Refer to the ROXIE map2d file for correct values")
|
|
296
296
|
|
|
297
297
|
# Compute errors
|
|
298
298
|
pq = np.linalg.norm(np.column_stack((np.array(physical_quantity_values['x']), np.array(physical_quantity_values['y']))), axis=1)
|
|
299
|
-
if postproc.
|
|
299
|
+
if postproc.model_dump().get('compare_to_ROXIE', False):
|
|
300
300
|
BB_err = pq - BB_roxie
|
|
301
301
|
self.postprocess_parameters['overall_error'] = np.mean(abs(BB_err))
|
|
302
302
|
self.postprocess_parameters['minimum_diff'] = np.min(BB_err)
|
|
303
303
|
self.postprocess_parameters['maximum_diff'] = np.max(BB_err)
|
|
304
304
|
|
|
305
305
|
if postproc.plot_all != 'false':
|
|
306
|
-
if postproc.
|
|
306
|
+
if postproc.model_dump().get('take_average_conductor_temperature', False):
|
|
307
307
|
min_value = self.avg_temperatures.iloc[:, 1:].min().min()
|
|
308
308
|
max_value = self.avg_temperatures.iloc[:, 1:].max().max()
|
|
309
309
|
ht_polygons = [patches.Polygon(np.array([(self.crns['iHr'][i][0], self.crns['iHr'][i][1]),
|
|
@@ -324,12 +324,12 @@ class PostProcess:
|
|
|
324
324
|
plt.pause(0.05)
|
|
325
325
|
|
|
326
326
|
else:
|
|
327
|
-
self.plotHalfTurnGeometry(postproc.
|
|
327
|
+
self.plotHalfTurnGeometry(postproc.model_dump().get('compare_to_ROXIE', False))
|
|
328
328
|
map2d_strands = self.ax.scatter(strands_x, strands_y, edgecolor='black', facecolor='black', s=10)
|
|
329
329
|
|
|
330
330
|
scatter3D_pos = self.ax3.scatter3D(strands_x, strands_y, pq, c=pq, cmap='Greens', vmin=0, vmax=10)
|
|
331
331
|
|
|
332
|
-
if postproc.
|
|
332
|
+
if postproc.model_dump().get('compare_to_ROXIE', False):
|
|
333
333
|
if os.path.isfile(path_cond2d):
|
|
334
334
|
conductors_corners = [condPos.xyCorner for condPos in conductorPositionsList]
|
|
335
335
|
for corners in conductors_corners:
|
|
@@ -1,175 +1,175 @@
|
|
|
1
|
-
import math
|
|
2
|
-
import os
|
|
3
|
-
import timeit
|
|
4
|
-
import json
|
|
5
|
-
import gmsh
|
|
6
|
-
import numpy as np
|
|
7
|
-
from fiqus.utils.Utils import FilesAndFolders as uff
|
|
8
|
-
from fiqus.utils.Utils import GmshUtils
|
|
9
|
-
from fiqus.data.DataWindingsCCT import WindingsInformation # for volume information
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class 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()
|
|
1
|
+
import math
|
|
2
|
+
import os
|
|
3
|
+
import timeit
|
|
4
|
+
import json
|
|
5
|
+
import gmsh
|
|
6
|
+
import numpy as np
|
|
7
|
+
from fiqus.utils.Utils import FilesAndFolders as uff
|
|
8
|
+
from fiqus.utils.Utils import GmshUtils
|
|
9
|
+
from fiqus.data.DataWindingsCCT import WindingsInformation # for volume information
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class 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()
|