fiqus 2026.1.0__py3-none-any.whl → 2026.1.2__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.
Files changed (42) hide show
  1. fiqus/MainFiQuS.py +1 -8
  2. fiqus/data/DataConductor.py +4 -8
  3. fiqus/data/DataFiQuSMultipole.py +358 -167
  4. fiqus/data/DataModelCommon.py +30 -15
  5. fiqus/data/DataMultipole.py +33 -10
  6. fiqus/data/DataWindingsCCT.py +37 -37
  7. fiqus/data/RegionsModelFiQuS.py +1 -1
  8. fiqus/geom_generators/GeometryMultipole.py +751 -54
  9. fiqus/getdp_runners/RunGetdpMultipole.py +181 -31
  10. fiqus/mains/MainMultipole.py +109 -17
  11. fiqus/mesh_generators/MeshCCT.py +209 -209
  12. fiqus/mesh_generators/MeshMultipole.py +938 -263
  13. fiqus/parsers/ParserCOND.py +2 -1
  14. fiqus/parsers/ParserDAT.py +16 -16
  15. fiqus/parsers/ParserGetDPOnSection.py +212 -212
  16. fiqus/parsers/ParserGetDPTimeTable.py +134 -134
  17. fiqus/parsers/ParserMSH.py +53 -53
  18. fiqus/parsers/ParserRES.py +142 -142
  19. fiqus/plotters/PlotPythonCCT.py +133 -133
  20. fiqus/plotters/PlotPythonMultipole.py +18 -18
  21. fiqus/post_processors/PostProcessMultipole.py +16 -6
  22. fiqus/pre_processors/PreProcessCCT.py +175 -175
  23. fiqus/pro_assemblers/ProAssembler.py +3 -3
  24. fiqus/pro_material_functions/ironBHcurves.pro +246 -246
  25. fiqus/pro_templates/combined/CC_Module.pro +1213 -0
  26. fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -0
  27. fiqus/pro_templates/combined/Multipole_template.pro +2738 -1338
  28. fiqus/pro_templates/combined/TSA_materials.pro +102 -2
  29. fiqus/pro_templates/combined/materials.pro +54 -3
  30. fiqus/utils/Utils.py +18 -25
  31. fiqus/utils/update_data_settings.py +1 -1
  32. {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/METADATA +64 -68
  33. {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/RECORD +42 -40
  34. {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/WHEEL +1 -1
  35. tests/test_geometry_generators.py +29 -32
  36. tests/test_mesh_generators.py +35 -34
  37. tests/test_solvers.py +32 -31
  38. tests/utils/fiqus_test_classes.py +396 -147
  39. tests/utils/generate_reference_files_ConductorAC.py +57 -57
  40. tests/utils/helpers.py +76 -1
  41. {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/LICENSE.txt +0 -0
  42. {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/top_level.txt +0 -0
@@ -1,134 +1,134 @@
1
- import os
2
- from operator import le, ge
3
-
4
- import numpy as np
5
- import pandas as pd
6
- import matplotlib.pyplot as plt
7
- from matplotlib.pyplot import cm
8
- from mpl_toolkits.mplot3d.art3d import Poly3DCollection
9
-
10
- from fiqus.geom_generators.GeometryCCT import Winding, FQPL
11
-
12
-
13
- class PlotPythonCCT:
14
- def __init__(self, fdm, verbose=True):
15
- """
16
- Class to cct models postprocessing
17
- :param fdm: FiQuS data model
18
- :param verbose: If True more information is printed in python console.
19
- """
20
- self.cctdm = fdm.magnet
21
- self.model_folder = os.path.join(os.getcwd())
22
- self.magnet_name = fdm.general.magnet_name
23
-
24
- self.verbose = verbose
25
- # self.gu = GmshUtils(self.model_folder, self.verbose)
26
- # self.gu.initialize()
27
-
28
- def plot_elements_file(self, lfes=None):
29
- field_map_3D_csv = 'field_map_3D.csv'
30
- df = pd.read_csv(field_map_3D_csv, delimiter=',', engine='python')
31
- sAvePositions = df['sAvePositions [m]'].to_numpy(dtype='float')[:-1] # [:-1] to remove last nan
32
- ph_order = df['nodeToPhysicalTurn [-]'].to_numpy(dtype='int')[:-1] # physical turns, [:-1] to remove last nan
33
- el_order = df['nodeToHalfTurn [-]'].to_numpy(dtype='int')[:-1] # electric order turns, [:-1] to remove last nan
34
- x3Dsurf_1 = df['x3Dsurf 1 [m]'].to_numpy(dtype='float')
35
- y3Dsurf_1 = df['y3Dsurf 1 [m]'].to_numpy(dtype='float')
36
- z3Dsurf_1 = df['z3Dsurf 1 [m]'].to_numpy(dtype='float')
37
- el_order_unique = np.unique(el_order)
38
-
39
- per_turn = False
40
-
41
- fig = plt.figure(figsize=(16, 10))
42
- ax = fig.add_subplot(111, projection='3d', proj_type='ortho')
43
-
44
- if per_turn:
45
- colors = cm.rainbow(np.linspace(0, 1, np.shape(el_order_unique)[0]))
46
- for idx_turn, el_u in enumerate(el_order_unique.tolist()):
47
- indexes = np.where(el_u == el_order)[0]
48
- min_idx=np.min(indexes)
49
- max_idx=np.max(indexes+2)
50
- ax.plot(x3Dsurf_1[min_idx:max_idx], y3Dsurf_1[min_idx:max_idx], z3Dsurf_1[min_idx:max_idx], color=colors[idx_turn])
51
- else:
52
- ax.plot(x3Dsurf_1, y3Dsurf_1, z3Dsurf_1)
53
- # colors = cm.rainbow(np.linspace(0, 1, np.shape(x3Dsurf_1)[0]))
54
- # # ax.plot(x3Dsurf_1, y3Dsurf_1, z3Dsurf_1)
55
-
56
- # for idx in range(np.shape(x3Dsurf_1)[0]-1):
57
- # ax.plot([x3Dsurf_1[idx], x3Dsurf_1[idx+1]], [y3Dsurf_1[idx], y3Dsurf_1[idx+1]], [z3Dsurf_1[idx], z3Dsurf_1[idx+1]], color=colors[idx])
58
- # xmin, xmax, ymin, ymax, zmin, zmax = (0, 0, 0, 0, 0, 0)
59
- # #colors = cm.rainbow(np.linspace(0, 1, len(objects_list)))
60
- # ax.set_box_aspect((xmax - xmin, ymax - ymin, zmax - zmin)) # set axis to data aspect ratio
61
- # ax.set_xlim([xmin, xmax])
62
- # ax.set_ylim([ymin, ymax])
63
- # ax.set_zlim([zmin, zmax])
64
- ax.set_xlabel('x (m)')
65
- ax.set_ylabel('y (m)')
66
- ax.set_zlabel('z (m)')
67
- ax.view_init(elev=90, azim=0, vertical_axis='x')
68
- plt.show()
69
- pass
70
-
71
- def plot_elements_computed(self, lfes=None):
72
- """
73
- Makes a plot oh hexahedra by calculating 'on the fly' what is defined in the yaml data model for cct magnet.
74
- :param lfes: list with numbers of elements to put text labels on. For two windings an example would be [[3], [9]] to plot 3rd hex in the first winding and 9th in the second
75
- :return: Nothing. A matplotlib graph pops up on the screen.
76
- """
77
-
78
- windings = []
79
- fqpls = []
80
- for ww, _ in enumerate(self.cctdm.geometry.windings.names):
81
- winding_obj = Winding(self.cctdm, ww, post_proc=True)
82
- windings.append(winding_obj)
83
- for ff, _ in enumerate(self.cctdm.geometry.fqpls.names):
84
- fqpl_obj = FQPL(self.cctdm, ff)
85
- fqpls.append(fqpl_obj)
86
- objects_list = windings + fqpls
87
- if not lfes:
88
- lfes = [[] for _ in objects_list] # make it work if lfes is not supplied as input.
89
-
90
- def val_check(var, array, oper, func):
91
- new = func(array)
92
- if oper(new, var):
93
- return new
94
- else:
95
- return var
96
-
97
- fig = plt.figure(figsize=(16, 10))
98
- ax = fig.add_subplot(111, projection='3d', proj_type='ortho')
99
- xmin, xmax, ymin, ymax, zmin, zmax = (0, 0, 0, 0, 0, 0)
100
- colors = cm.rainbow(np.linspace(0, 1, len(objects_list)))
101
- for obj, lfe, fc in zip(objects_list, lfes, colors):
102
- xs = []
103
- ys = []
104
- zs = []
105
- hexes = obj.hexes
106
- vts = obj.vertices_to_surf
107
- for elem_num, points_dict in hexes.items():
108
- list_of_coor_tuples = []
109
- points_dict.pop('ct', None) # removing turn from dict
110
- for p_num, p_coords in points_dict.items():
111
-
112
- list_of_coor_tuples.append((p_coords['x'], p_coords['y'], p_coords['z']))
113
- if elem_num in lfe:
114
- ax.scatter(p_coords['x'], p_coords['y'], p_coords['z'])
115
- ax.text(p_coords['x'], p_coords['y'], p_coords['z'], f'{p_num}', zdir='z')
116
- for coor_acc, coor_key in zip([xs, ys, zs], ['x', 'y', 'z']):
117
- coor_acc.append(p_coords[coor_key])
118
- poly3d = [[list_of_coor_tuples[vts[ix][iy]] for iy in range(len(vts[0]))] for ix in range(len(vts))]
119
- ax.add_collection3d(Poly3DCollection(poly3d, edgecolors='k', facecolors=fc, linewidths=0.5, alpha=0.5))
120
- xmin = val_check(xmin, xs, le, np.min)
121
- xmax = val_check(xmax, xs, ge, np.max)
122
- ymin = val_check(ymin, ys, le, np.min)
123
- ymax = val_check(ymax, ys, ge, np.max)
124
- zmin = val_check(zmin, zs, le, np.min)
125
- zmax = val_check(zmax, zs, ge, np.max)
126
- ax.set_box_aspect((xmax - xmin, ymax - ymin, zmax - zmin)) # set axis to data aspect ratio
127
- ax.set_xlim([xmin, xmax])
128
- ax.set_ylim([ymin, ymax])
129
- ax.set_zlim([zmin, zmax])
130
- ax.set_xlabel('x (m)')
131
- ax.set_ylabel('y (m)')
132
- ax.set_zlabel('z (m)')
133
- ax.view_init(elev=90, azim=0, vertical_axis='x')
1
+ import os
2
+ from operator import le, ge
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+ import matplotlib.pyplot as plt
7
+ from matplotlib.pyplot import cm
8
+ from mpl_toolkits.mplot3d.art3d import Poly3DCollection
9
+
10
+ from fiqus.geom_generators.GeometryCCT import Winding, FQPL
11
+
12
+
13
+ class PlotPythonCCT:
14
+ def __init__(self, fdm, verbose=True):
15
+ """
16
+ Class to cct models postprocessing
17
+ :param fdm: FiQuS data model
18
+ :param verbose: If True more information is printed in python console.
19
+ """
20
+ self.cctdm = fdm.magnet
21
+ self.model_folder = os.path.join(os.getcwd())
22
+ self.magnet_name = fdm.general.magnet_name
23
+
24
+ self.verbose = verbose
25
+ # self.gu = GmshUtils(self.model_folder, self.verbose)
26
+ # self.gu.initialize()
27
+
28
+ def plot_elements_file(self, lfes=None):
29
+ field_map_3D_csv = 'field_map_3D.csv'
30
+ df = pd.read_csv(field_map_3D_csv, delimiter=',', engine='python')
31
+ sAvePositions = df['sAvePositions [m]'].to_numpy(dtype='float')[:-1] # [:-1] to remove last nan
32
+ ph_order = df['nodeToPhysicalTurn [-]'].to_numpy(dtype='int')[:-1] # physical turns, [:-1] to remove last nan
33
+ el_order = df['nodeToHalfTurn [-]'].to_numpy(dtype='int')[:-1] # electric order turns, [:-1] to remove last nan
34
+ x3Dsurf_1 = df['x3Dsurf 1 [m]'].to_numpy(dtype='float')
35
+ y3Dsurf_1 = df['y3Dsurf 1 [m]'].to_numpy(dtype='float')
36
+ z3Dsurf_1 = df['z3Dsurf 1 [m]'].to_numpy(dtype='float')
37
+ el_order_unique = np.unique(el_order)
38
+
39
+ per_turn = False
40
+
41
+ fig = plt.figure(figsize=(16, 10))
42
+ ax = fig.add_subplot(111, projection='3d', proj_type='ortho')
43
+
44
+ if per_turn:
45
+ colors = cm.rainbow(np.linspace(0, 1, np.shape(el_order_unique)[0]))
46
+ for idx_turn, el_u in enumerate(el_order_unique.tolist()):
47
+ indexes = np.where(el_u == el_order)[0]
48
+ min_idx=np.min(indexes)
49
+ max_idx=np.max(indexes+2)
50
+ ax.plot(x3Dsurf_1[min_idx:max_idx], y3Dsurf_1[min_idx:max_idx], z3Dsurf_1[min_idx:max_idx], color=colors[idx_turn])
51
+ else:
52
+ ax.plot(x3Dsurf_1, y3Dsurf_1, z3Dsurf_1)
53
+ # colors = cm.rainbow(np.linspace(0, 1, np.shape(x3Dsurf_1)[0]))
54
+ # # ax.plot(x3Dsurf_1, y3Dsurf_1, z3Dsurf_1)
55
+
56
+ # for idx in range(np.shape(x3Dsurf_1)[0]-1):
57
+ # ax.plot([x3Dsurf_1[idx], x3Dsurf_1[idx+1]], [y3Dsurf_1[idx], y3Dsurf_1[idx+1]], [z3Dsurf_1[idx], z3Dsurf_1[idx+1]], color=colors[idx])
58
+ # xmin, xmax, ymin, ymax, zmin, zmax = (0, 0, 0, 0, 0, 0)
59
+ # #colors = cm.rainbow(np.linspace(0, 1, len(objects_list)))
60
+ # ax.set_box_aspect((xmax - xmin, ymax - ymin, zmax - zmin)) # set axis to data aspect ratio
61
+ # ax.set_xlim([xmin, xmax])
62
+ # ax.set_ylim([ymin, ymax])
63
+ # ax.set_zlim([zmin, zmax])
64
+ ax.set_xlabel('x (m)')
65
+ ax.set_ylabel('y (m)')
66
+ ax.set_zlabel('z (m)')
67
+ ax.view_init(elev=90, azim=0, vertical_axis='x')
68
+ plt.show()
69
+ pass
70
+
71
+ def plot_elements_computed(self, lfes=None):
72
+ """
73
+ Makes a plot oh hexahedra by calculating 'on the fly' what is defined in the yaml data model for cct magnet.
74
+ :param lfes: list with numbers of elements to put text labels on. For two windings an example would be [[3], [9]] to plot 3rd hex in the first winding and 9th in the second
75
+ :return: Nothing. A matplotlib graph pops up on the screen.
76
+ """
77
+
78
+ windings = []
79
+ fqpls = []
80
+ for ww, _ in enumerate(self.cctdm.geometry.windings.names):
81
+ winding_obj = Winding(self.cctdm, ww, post_proc=True)
82
+ windings.append(winding_obj)
83
+ for ff, _ in enumerate(self.cctdm.geometry.fqpls.names):
84
+ fqpl_obj = FQPL(self.cctdm, ff)
85
+ fqpls.append(fqpl_obj)
86
+ objects_list = windings + fqpls
87
+ if not lfes:
88
+ lfes = [[] for _ in objects_list] # make it work if lfes is not supplied as input.
89
+
90
+ def val_check(var, array, oper, func):
91
+ new = func(array)
92
+ if oper(new, var):
93
+ return new
94
+ else:
95
+ return var
96
+
97
+ fig = plt.figure(figsize=(16, 10))
98
+ ax = fig.add_subplot(111, projection='3d', proj_type='ortho')
99
+ xmin, xmax, ymin, ymax, zmin, zmax = (0, 0, 0, 0, 0, 0)
100
+ colors = cm.rainbow(np.linspace(0, 1, len(objects_list)))
101
+ for obj, lfe, fc in zip(objects_list, lfes, colors):
102
+ xs = []
103
+ ys = []
104
+ zs = []
105
+ hexes = obj.hexes
106
+ vts = obj.vertices_to_surf
107
+ for elem_num, points_dict in hexes.items():
108
+ list_of_coor_tuples = []
109
+ points_dict.pop('ct', None) # removing turn from dict
110
+ for p_num, p_coords in points_dict.items():
111
+
112
+ list_of_coor_tuples.append((p_coords['x'], p_coords['y'], p_coords['z']))
113
+ if elem_num in lfe:
114
+ ax.scatter(p_coords['x'], p_coords['y'], p_coords['z'])
115
+ ax.text(p_coords['x'], p_coords['y'], p_coords['z'], f'{p_num}', zdir='z')
116
+ for coor_acc, coor_key in zip([xs, ys, zs], ['x', 'y', 'z']):
117
+ coor_acc.append(p_coords[coor_key])
118
+ poly3d = [[list_of_coor_tuples[vts[ix][iy]] for iy in range(len(vts[0]))] for ix in range(len(vts))]
119
+ ax.add_collection3d(Poly3DCollection(poly3d, edgecolors='k', facecolors=fc, linewidths=0.5, alpha=0.5))
120
+ xmin = val_check(xmin, xs, le, np.min)
121
+ xmax = val_check(xmax, xs, ge, np.max)
122
+ ymin = val_check(ymin, ys, le, np.min)
123
+ ymax = val_check(ymax, ys, ge, np.max)
124
+ zmin = val_check(zmin, zs, le, np.min)
125
+ zmax = val_check(zmax, zs, ge, np.max)
126
+ ax.set_box_aspect((xmax - xmin, ymax - ymin, zmax - zmin)) # set axis to data aspect ratio
127
+ ax.set_xlim([xmin, xmax])
128
+ ax.set_ylim([ymin, ymax])
129
+ ax.set_zlim([zmin, zmax])
130
+ ax.set_xlabel('x (m)')
131
+ ax.set_ylabel('y (m)')
132
+ ax.set_zlabel('z (m)')
133
+ ax.view_init(elev=90, azim=0, vertical_axis='x')
134
134
  plt.show()
@@ -1,19 +1,19 @@
1
- import os
2
-
3
- class PlotPythonMultipole:
4
- def __init__(self, fdm, verbose=True):
5
- """
6
- Class for making python plots of multipole magnets
7
- :param fdm: FiQuS data model
8
- :param verbose: If True more information is printed in python console.
9
- """
10
- self.fdm = fdm
11
- self.verbose = verbose
12
-
13
- def dummy_plot_func(self):
14
- """
15
- not implemented yet
16
- :return:
17
- :rtype:
18
- """
1
+ import os
2
+
3
+ class PlotPythonMultipole:
4
+ def __init__(self, fdm, verbose=True):
5
+ """
6
+ Class for making python plots of multipole magnets
7
+ :param fdm: FiQuS data model
8
+ :param verbose: If True more information is printed in python console.
9
+ """
10
+ self.fdm = fdm
11
+ self.verbose = verbose
12
+
13
+ def dummy_plot_func(self):
14
+ """
15
+ not implemented yet
16
+ :return:
17
+ :rtype:
18
+ """
19
19
  pass
@@ -34,7 +34,9 @@ class PostProcess:
34
34
  self.gu = GmshUtils(self.solution_folder, self.verbose)
35
35
  self.gu.initialize(verbosity_Gmsh=self.data.run.verbosity_Gmsh)
36
36
 
37
- self.brep_iron_curves = {1: set(), 2: set(), 3: set(), 4: set()}
37
+ self.brep_curves = {}
38
+ for name in self.data.magnet.geometry.electromagnetics.areas:
39
+ self.brep_curves[name] = {1: set(), 2: set(), 3: set(), 4: set()}
38
40
  self.strands = None
39
41
  self.crns = None
40
42
  self.avg_temperatures = pd.DataFrame()
@@ -80,7 +82,7 @@ class PostProcess:
80
82
  self.ax = fig1.add_subplot()
81
83
  self.ax.set_xlabel('x [cm]') # adjust other plots to cm
82
84
  self.ax.set_ylabel('y [cm]')
83
- # self.ax.set_xlim(0, 0.09) # todo
85
+ # self.ax.set_xlim(0, 0.09)
84
86
  # self.ax.set_ylim(0, 0.09)
85
87
 
86
88
  if not settings.model_dump().get('take_average_conductor_temperature', False):
@@ -104,11 +106,14 @@ class PostProcess:
104
106
  if 'compare_to_ROXIE' in settings.model_dump() and 'b' in settings.variables:
105
107
  b_index = settings.variables.index('b')
106
108
  file_to_open = os.path.join(self.solution_folder, f"b_{settings.volumes[b_index]}.pos")
109
+ gmsh.open(file_to_open)
107
110
  elif 'T' in settings.variables:
108
111
  T_index = settings.variables.index('T')
109
112
  file_to_open = os.path.join(self.solution_folder, f"T_{settings.volumes[T_index]}.pos")
113
+ gmsh.open(file_to_open)
114
+
110
115
 
111
- gmsh.open(file_to_open)
116
+
112
117
 
113
118
  def clear(self):
114
119
  self.md = dM.MultipoleData()
@@ -120,8 +125,9 @@ class PostProcess:
120
125
  if gui:
121
126
  self.gu.launch_interactive_GUI()
122
127
  else:
123
- gmsh.clear()
124
- gmsh.finalize()
128
+ if gmsh.isInitialized():
129
+ gmsh.clear()
130
+ gmsh.finalize()
125
131
 
126
132
  def loadAuxiliaryFile(self, run_type):
127
133
  self.md = Util.read_data_from_yaml(f"{self.mesh_files}_{run_type}.aux", dM.MultipoleData)
@@ -223,7 +229,11 @@ class PostProcess:
223
229
  # (self.postproc_settings['variables'] == self.supported_variables[self.physical_quantity]) &
224
230
  # (self.postproc_settings['volumes'] == 'Omega_p')].index[0]
225
231
 
226
- view = gmsh.view.getTags()[0]
232
+ try: view = gmsh.view.getTags()[0]
233
+ except IndexError:
234
+ print("Error with post processing")
235
+ return
236
+
227
237
  for i in range(len(strands_x)):
228
238
  is_new_conductor = i == 0 or self.strands['ht'][i] != self.strands['ht'][i - 1]
229
239
  is_new_block = i == 0 or self.strands['block'][i] != self.strands['block'][i - 1]