fiqus 2024.6.0__py3-none-any.whl → 2024.12.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 +290 -134
- fiqus/data/DataConductor.py +301 -301
- fiqus/data/DataFiQuS.py +128 -84
- fiqus/data/DataFiQuSCCT.py +150 -150
- fiqus/data/DataFiQuSConductor.py +84 -84
- fiqus/data/DataFiQuSConductorAC_Strand.py +565 -565
- fiqus/data/DataFiQuSMultipole.py +716 -42
- fiqus/data/DataFiQuSPancake3D.py +737 -278
- fiqus/data/DataMultipole.py +180 -15
- fiqus/data/DataRoxieParser.py +90 -51
- fiqus/data/DataSettings.py +121 -0
- fiqus/data/DataWindingsCCT.py +37 -37
- fiqus/data/RegionsModelFiQuS.py +18 -6
- fiqus/geom_generators/GeometryCCT.py +905 -905
- fiqus/geom_generators/GeometryConductorAC_Strand.py +1391 -1391
- fiqus/geom_generators/GeometryMultipole.py +1827 -227
- fiqus/geom_generators/GeometryPancake3D.py +316 -117
- fiqus/geom_generators/GeometryPancake3DUtils.py +549 -0
- fiqus/getdp_runners/RunGetdpCCT.py +4 -4
- fiqus/getdp_runners/RunGetdpConductorAC_Strand.py +201 -201
- fiqus/getdp_runners/RunGetdpMultipole.py +115 -42
- fiqus/getdp_runners/RunGetdpPancake3D.py +28 -6
- fiqus/mains/MainCCT.py +2 -2
- fiqus/mains/MainConductorAC_Strand.py +132 -132
- fiqus/mains/MainMultipole.py +113 -62
- fiqus/mains/MainPancake3D.py +63 -23
- fiqus/mesh_generators/MeshCCT.py +209 -209
- fiqus/mesh_generators/MeshConductorAC_Strand.py +656 -656
- fiqus/mesh_generators/MeshMultipole.py +1243 -181
- fiqus/mesh_generators/MeshPancake3D.py +275 -192
- fiqus/parsers/ParserCOND.py +825 -0
- 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 +855 -840
- fiqus/plotters/PlotPythonMultipole.py +18 -18
- fiqus/post_processors/PostProcessCCT.py +440 -440
- fiqus/post_processors/PostProcessConductorAC.py +49 -49
- fiqus/post_processors/PostProcessMultipole.py +353 -229
- fiqus/post_processors/PostProcessPancake3D.py +8 -13
- fiqus/pre_processors/PreProcessCCT.py +175 -175
- fiqus/pro_assemblers/ProAssembler.py +14 -6
- fiqus/pro_material_functions/ironBHcurves.pro +246 -246
- fiqus/pro_templates/combined/CCT_template.pro +274 -274
- fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -1025
- fiqus/pro_templates/combined/Multipole_template.pro +1694 -126
- fiqus/pro_templates/combined/Pancake3D_template.pro +2294 -1103
- fiqus/pro_templates/combined/TSA_materials.pro +162 -0
- fiqus/pro_templates/combined/materials.pro +36 -18
- fiqus/utils/Utils.py +508 -110
- fiqus/utils/update_data_settings.py +33 -0
- fiqus-2024.12.0.dist-info/METADATA +130 -0
- fiqus-2024.12.0.dist-info/RECORD +84 -0
- {fiqus-2024.6.0.dist-info → fiqus-2024.12.0.dist-info}/WHEEL +1 -1
- tests/test_FiQuS.py +1 -1
- tests/test_geometry_generators.py +101 -2
- tests/test_mesh_generators.py +154 -1
- tests/test_solvers.py +115 -21
- tests/utils/fiqus_test_classes.py +85 -21
- tests/utils/generate_reference_files_ConductorAC.py +57 -57
- tests/utils/generate_reference_files_Pancake3D.py +4 -5
- tests/utils/helpers.py +97 -97
- fiqus-2024.6.0.dist-info/METADATA +0 -103
- fiqus-2024.6.0.dist-info/RECORD +0 -79
- {fiqus-2024.6.0.dist-info → fiqus-2024.12.0.dist-info}/top_level.txt +0 -0
|
@@ -1,202 +1,202 @@
|
|
|
1
|
-
import timeit
|
|
2
|
-
import logging
|
|
3
|
-
import os
|
|
4
|
-
import subprocess
|
|
5
|
-
import re
|
|
6
|
-
import pandas as pd
|
|
7
|
-
import pickle
|
|
8
|
-
import numpy as np
|
|
9
|
-
|
|
10
|
-
import gmsh
|
|
11
|
-
|
|
12
|
-
from fiqus.utils.Utils import GmshUtils, FilesAndFolders
|
|
13
|
-
from fiqus.data.RegionsModelFiQuS import RegionsModel
|
|
14
|
-
import fiqus.data.DataFiQuSConductor as geom
|
|
15
|
-
from fiqus.geom_generators.GeometryConductorAC_Strand import TwistedStrand
|
|
16
|
-
|
|
17
|
-
from fiqus.pro_assemblers.ProAssembler import ASS_PRO
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
class Solve:
|
|
22
|
-
def __init__(self, fdm, GetDP_path, geometry_folder, mesh_folder, verbose=True):
|
|
23
|
-
self.fdm = fdm
|
|
24
|
-
self.cacdm = fdm.magnet
|
|
25
|
-
self.GetDP_path = GetDP_path
|
|
26
|
-
self.solution_folder = os.path.join(os.getcwd())
|
|
27
|
-
self.magnet_name = fdm.general.magnet_name
|
|
28
|
-
self.geometry_folder = geometry_folder
|
|
29
|
-
self.mesh_folder = mesh_folder
|
|
30
|
-
self.mesh_file = os.path.join(self.mesh_folder, f"{self.magnet_name}.msh")
|
|
31
|
-
self.pro_file = os.path.join(self.solution_folder, f"{self.magnet_name}.pro")
|
|
32
|
-
self.regions_file = os.path.join(mesh_folder, f"{self.magnet_name}.regions")
|
|
33
|
-
|
|
34
|
-
self.verbose = verbose
|
|
35
|
-
self.gu = GmshUtils(self.solution_folder, self.verbose)
|
|
36
|
-
self.gu.initialize()
|
|
37
|
-
|
|
38
|
-
self.ass_pro = ASS_PRO(os.path.join(self.solution_folder, self.magnet_name))
|
|
39
|
-
self.regions_model = FilesAndFolders.read_data_from_yaml(self.regions_file, RegionsModel)
|
|
40
|
-
self.material_properties_model = None
|
|
41
|
-
|
|
42
|
-
self.ed = {} # excitation dictionary
|
|
43
|
-
|
|
44
|
-
gmsh.option.setNumber("General.Terminal", verbose)
|
|
45
|
-
|
|
46
|
-
def read_excitation(self, inputs_folder_path):
|
|
47
|
-
"""
|
|
48
|
-
Function for reading a CSV file for the 'from_file' excitation case.
|
|
49
|
-
|
|
50
|
-
:param inputs_folder_path: The full path to the folder with input files.
|
|
51
|
-
:type inputs_folder_path: str
|
|
52
|
-
"""
|
|
53
|
-
if self.cacdm.solve.source_parameters.source_type == 'piecewise' and self.cacdm.solve.source_parameters.piecewise.source_csv_file:
|
|
54
|
-
input_file = os.path.join(inputs_folder_path, self.cacdm.solve.source_parameters.piecewise.source_csv_file)
|
|
55
|
-
print(f'Using excitation from file: {input_file}')
|
|
56
|
-
df = pd.read_csv(input_file, delimiter=',', engine='python')
|
|
57
|
-
excitation_time = df['time'].to_numpy(dtype='float').tolist()
|
|
58
|
-
self.ed['time'] = excitation_time
|
|
59
|
-
excitation_value = df['value'].to_numpy(dtype='float').tolist()
|
|
60
|
-
self.ed['value'] = excitation_value
|
|
61
|
-
|
|
62
|
-
def get_solution_parameters_from_yaml(self, inputs_folder_path):
|
|
63
|
-
"""
|
|
64
|
-
Function for reading material properties from the geometry YAML file.
|
|
65
|
-
|
|
66
|
-
This reads the 'solution' section of the YAML file and stores it in the solution folder.
|
|
67
|
-
This could also be a place to change the material properties in the future.
|
|
68
|
-
|
|
69
|
-
:param inputs_folder_path: The full path to the folder with input files.
|
|
70
|
-
:type inputs_folder_path: str
|
|
71
|
-
"""
|
|
72
|
-
if self.cacdm.geometry.io_settings.load.load_from_yaml:
|
|
73
|
-
#load the geometry class from the pkl file
|
|
74
|
-
geom_save_file = os.path.join(self.geometry_folder, f'{self.magnet_name}.pkl')
|
|
75
|
-
with open(geom_save_file, "rb") as geom_save_file:
|
|
76
|
-
geometry_class: TwistedStrand = pickle.load(geom_save_file)
|
|
77
|
-
|
|
78
|
-
input_yaml_file = os.path.join(inputs_folder_path, self.cacdm.geometry.io_settings.load.filename)
|
|
79
|
-
Conductor_dm = FilesAndFolders.read_data_from_yaml(input_yaml_file, geom.Conductor)
|
|
80
|
-
solution_parameters = Conductor_dm.Solution
|
|
81
|
-
|
|
82
|
-
# The geometry YAML file lists surfaces to exclude from the TI problem by their IDs. Here we convert these IDs to Gmsh physical surface tags.
|
|
83
|
-
# This is done by comparing the outer boundary points of the surfaces to exclude with the outer boundary points of the matrix partitions.
|
|
84
|
-
surfaces_excluded_from_TI_tags = []
|
|
85
|
-
|
|
86
|
-
for surface_ID in solution_parameters.Surfaces_excluded_from_TI: # 1) Find the outer boundary points of the surfaces to exclude
|
|
87
|
-
outer_boundary_points_a = []
|
|
88
|
-
outer_boundary_curves_a = Conductor_dm.Geometry.Areas[surface_ID].Boundary
|
|
89
|
-
for curve_ID in outer_boundary_curves_a:
|
|
90
|
-
curve = Conductor_dm.Geometry.Curves[curve_ID]
|
|
91
|
-
for point_ID in curve.Points:
|
|
92
|
-
point = Conductor_dm.Geometry.Points[point_ID]
|
|
93
|
-
outer_boundary_points_a.append(tuple(point.Coordinates))
|
|
94
|
-
|
|
95
|
-
for matrix_partition in geometry_class.matrix: # 2) Find the outer boundary points of the matrix partitions
|
|
96
|
-
outer_boundary_points_b = []
|
|
97
|
-
outer_boundary_curves_b = matrix_partition.boundary_curves
|
|
98
|
-
if len(outer_boundary_curves_b) == len(outer_boundary_curves_a): # If the number of boundary curves is different, the surfaces are not the same
|
|
99
|
-
for curve in outer_boundary_curves_b:
|
|
100
|
-
for point in curve.points:
|
|
101
|
-
outer_boundary_points_b.append(tuple(point.pos))
|
|
102
|
-
|
|
103
|
-
if np.allclose(sorted(outer_boundary_points_a), sorted(outer_boundary_points_b)): # If the outer boundary points are the same, the surfaces are the same
|
|
104
|
-
surfaces_excluded_from_TI_tags.append(matrix_partition.physical_surface_tag) # 3) Add the physical surface tag to the list of surfaces to exclude
|
|
105
|
-
break
|
|
106
|
-
|
|
107
|
-
solution_parameters.Surfaces_excluded_from_TI = surfaces_excluded_from_TI_tags # Replace the surface IDs with the physical surface tags
|
|
108
|
-
|
|
109
|
-
FilesAndFolders.write_data_to_yaml(os.path.join(self.solution_folder, "MaterialProperties.yaml"), solution_parameters.dict())
|
|
110
|
-
self.material_properties_model = solution_parameters
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def assemble_pro(self):
|
|
116
|
-
print("Assembling .pro file")
|
|
117
|
-
self.ass_pro.assemble_combined_pro(template = self.cacdm.solve.pro_template, rm = self.regions_model, dm = self.fdm, ed=self.ed, mp=self.material_properties_model)
|
|
118
|
-
|
|
119
|
-
def run_getdp(self, solve = True, postOperation = True, gui = False):
|
|
120
|
-
|
|
121
|
-
command = ["-v2", "-verbose", "3"]
|
|
122
|
-
if solve:
|
|
123
|
-
command += ["-solve", "MagDyn"]
|
|
124
|
-
if self.cacdm.solve.formulation_parameters.dynamic_correction:
|
|
125
|
-
command += ["-pos", "MagDyn", "MagDyn_dynCorr"]
|
|
126
|
-
else:
|
|
127
|
-
command += ["-pos", "MagDyn"]
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
startTime = timeit.default_timer()
|
|
131
|
-
getdpProcess = subprocess.Popen([self.GetDP_path, self.pro_file, "-msh", self.mesh_file] + command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
132
|
-
|
|
133
|
-
with getdpProcess.stdout:
|
|
134
|
-
for line in iter(getdpProcess.stdout.readline, b""):
|
|
135
|
-
line = line.decode("utf-8").rstrip()
|
|
136
|
-
line = line.split("\r")[-1]
|
|
137
|
-
if not "Test" in line:
|
|
138
|
-
if line.startswith("Info"):
|
|
139
|
-
parsedLine = re.sub(r"Info\s+:\s+", "", line)
|
|
140
|
-
logger.info(parsedLine)
|
|
141
|
-
elif line.startswith("Warning"):
|
|
142
|
-
parsedLine = re.sub(r"Warning\s+:\s+", "", line)
|
|
143
|
-
logger.warning(parsedLine)
|
|
144
|
-
elif line.startswith("Error"):
|
|
145
|
-
parsedLine = re.sub(r"Error\s+:\s+", "", line)
|
|
146
|
-
logger.error(parsedLine)
|
|
147
|
-
logger.error("Solving CAC failed.")
|
|
148
|
-
# raise Exception(parsedLine)
|
|
149
|
-
elif re.match("##", line):
|
|
150
|
-
logger.critical(line)
|
|
151
|
-
else:
|
|
152
|
-
logger.info(line)
|
|
153
|
-
|
|
154
|
-
simulation_time = timeit.default_timer()-startTime
|
|
155
|
-
# Save simulation time:
|
|
156
|
-
if solve:
|
|
157
|
-
logger.info(f"Solving CAC_1 has finished in {round(simulation_time, 3)} seconds.")
|
|
158
|
-
with open(os.path.join(self.solution_folder, 'test_temporary', 'simulation_time.txt'), 'w') as file:
|
|
159
|
-
file.write(str(simulation_time))
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if gui and ((postOperation and not solve) or (solve and postOperation and self.cacdm.postproc.generate_pos_files)):
|
|
163
|
-
# gmsh.option.setNumber("Geometry.Volumes", 1)
|
|
164
|
-
# gmsh.option.setNumber("Geometry.Surfaces", 1)
|
|
165
|
-
# gmsh.option.setNumber("Geometry.Curves", 1)
|
|
166
|
-
# gmsh.option.setNumber("Geometry.Points", 0)
|
|
167
|
-
posFiles = [
|
|
168
|
-
fileName
|
|
169
|
-
for fileName in os.listdir(self.solution_folder)
|
|
170
|
-
if fileName.endswith(".pos")
|
|
171
|
-
]
|
|
172
|
-
for posFile in posFiles:
|
|
173
|
-
gmsh.open(os.path.join(self.solution_folder, posFile))
|
|
174
|
-
self.gu.launch_interactive_GUI()
|
|
175
|
-
else:
|
|
176
|
-
gmsh.clear()
|
|
177
|
-
gmsh.finalize()
|
|
178
|
-
|
|
179
|
-
def cleanup(self):
|
|
180
|
-
"""
|
|
181
|
-
This funtion is used to remove .msh, .pre and .res files from the solution folder, as they may be large and not needed.
|
|
182
|
-
"""
|
|
183
|
-
magnet_name = self.fdm.general.magnet_name
|
|
184
|
-
cleanup = self.cacdm.postproc.cleanup
|
|
185
|
-
|
|
186
|
-
if cleanup.remove_res_file:
|
|
187
|
-
res_file_path = os.path.join(self.solution_folder, f"{magnet_name}.res")
|
|
188
|
-
if os.path.exists(res_file_path):
|
|
189
|
-
os.remove(res_file_path)
|
|
190
|
-
logger.info(f"Removed {magnet_name}.res")
|
|
191
|
-
|
|
192
|
-
if cleanup.remove_pre_file:
|
|
193
|
-
pre_file_path = os.path.join(self.solution_folder, f"{magnet_name}.pre")
|
|
194
|
-
if os.path.exists(pre_file_path):
|
|
195
|
-
os.remove(pre_file_path)
|
|
196
|
-
logger.info(f"Removed {magnet_name}.pre")
|
|
197
|
-
|
|
198
|
-
if cleanup.remove_msh_file:
|
|
199
|
-
msh_file_path = os.path.join(self.mesh_folder, f"{magnet_name}.msh")
|
|
200
|
-
if os.path.exists(msh_file_path):
|
|
201
|
-
os.remove(msh_file_path)
|
|
1
|
+
import timeit
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import re
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import pickle
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
import gmsh
|
|
11
|
+
|
|
12
|
+
from fiqus.utils.Utils import GmshUtils, FilesAndFolders
|
|
13
|
+
from fiqus.data.RegionsModelFiQuS import RegionsModel
|
|
14
|
+
import fiqus.data.DataFiQuSConductor as geom
|
|
15
|
+
from fiqus.geom_generators.GeometryConductorAC_Strand import TwistedStrand
|
|
16
|
+
|
|
17
|
+
from fiqus.pro_assemblers.ProAssembler import ASS_PRO
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
class Solve:
|
|
22
|
+
def __init__(self, fdm, GetDP_path, geometry_folder, mesh_folder, verbose=True):
|
|
23
|
+
self.fdm = fdm
|
|
24
|
+
self.cacdm = fdm.magnet
|
|
25
|
+
self.GetDP_path = GetDP_path
|
|
26
|
+
self.solution_folder = os.path.join(os.getcwd())
|
|
27
|
+
self.magnet_name = fdm.general.magnet_name
|
|
28
|
+
self.geometry_folder = geometry_folder
|
|
29
|
+
self.mesh_folder = mesh_folder
|
|
30
|
+
self.mesh_file = os.path.join(self.mesh_folder, f"{self.magnet_name}.msh")
|
|
31
|
+
self.pro_file = os.path.join(self.solution_folder, f"{self.magnet_name}.pro")
|
|
32
|
+
self.regions_file = os.path.join(mesh_folder, f"{self.magnet_name}.regions")
|
|
33
|
+
|
|
34
|
+
self.verbose = verbose
|
|
35
|
+
self.gu = GmshUtils(self.solution_folder, self.verbose)
|
|
36
|
+
self.gu.initialize()
|
|
37
|
+
|
|
38
|
+
self.ass_pro = ASS_PRO(os.path.join(self.solution_folder, self.magnet_name))
|
|
39
|
+
self.regions_model = FilesAndFolders.read_data_from_yaml(self.regions_file, RegionsModel)
|
|
40
|
+
self.material_properties_model = None
|
|
41
|
+
|
|
42
|
+
self.ed = {} # excitation dictionary
|
|
43
|
+
|
|
44
|
+
gmsh.option.setNumber("General.Terminal", verbose)
|
|
45
|
+
|
|
46
|
+
def read_excitation(self, inputs_folder_path):
|
|
47
|
+
"""
|
|
48
|
+
Function for reading a CSV file for the 'from_file' excitation case.
|
|
49
|
+
|
|
50
|
+
:param inputs_folder_path: The full path to the folder with input files.
|
|
51
|
+
:type inputs_folder_path: str
|
|
52
|
+
"""
|
|
53
|
+
if self.cacdm.solve.source_parameters.source_type == 'piecewise' and self.cacdm.solve.source_parameters.piecewise.source_csv_file:
|
|
54
|
+
input_file = os.path.join(inputs_folder_path, self.cacdm.solve.source_parameters.piecewise.source_csv_file)
|
|
55
|
+
print(f'Using excitation from file: {input_file}')
|
|
56
|
+
df = pd.read_csv(input_file, delimiter=',', engine='python')
|
|
57
|
+
excitation_time = df['time'].to_numpy(dtype='float').tolist()
|
|
58
|
+
self.ed['time'] = excitation_time
|
|
59
|
+
excitation_value = df['value'].to_numpy(dtype='float').tolist()
|
|
60
|
+
self.ed['value'] = excitation_value
|
|
61
|
+
|
|
62
|
+
def get_solution_parameters_from_yaml(self, inputs_folder_path):
|
|
63
|
+
"""
|
|
64
|
+
Function for reading material properties from the geometry YAML file.
|
|
65
|
+
|
|
66
|
+
This reads the 'solution' section of the YAML file and stores it in the solution folder.
|
|
67
|
+
This could also be a place to change the material properties in the future.
|
|
68
|
+
|
|
69
|
+
:param inputs_folder_path: The full path to the folder with input files.
|
|
70
|
+
:type inputs_folder_path: str
|
|
71
|
+
"""
|
|
72
|
+
if self.cacdm.geometry.io_settings.load.load_from_yaml:
|
|
73
|
+
#load the geometry class from the pkl file
|
|
74
|
+
geom_save_file = os.path.join(self.geometry_folder, f'{self.magnet_name}.pkl')
|
|
75
|
+
with open(geom_save_file, "rb") as geom_save_file:
|
|
76
|
+
geometry_class: TwistedStrand = pickle.load(geom_save_file)
|
|
77
|
+
|
|
78
|
+
input_yaml_file = os.path.join(inputs_folder_path, self.cacdm.geometry.io_settings.load.filename)
|
|
79
|
+
Conductor_dm = FilesAndFolders.read_data_from_yaml(input_yaml_file, geom.Conductor)
|
|
80
|
+
solution_parameters = Conductor_dm.Solution
|
|
81
|
+
|
|
82
|
+
# The geometry YAML file lists surfaces to exclude from the TI problem by their IDs. Here we convert these IDs to Gmsh physical surface tags.
|
|
83
|
+
# This is done by comparing the outer boundary points of the surfaces to exclude with the outer boundary points of the matrix partitions.
|
|
84
|
+
surfaces_excluded_from_TI_tags = []
|
|
85
|
+
|
|
86
|
+
for surface_ID in solution_parameters.Surfaces_excluded_from_TI: # 1) Find the outer boundary points of the surfaces to exclude
|
|
87
|
+
outer_boundary_points_a = []
|
|
88
|
+
outer_boundary_curves_a = Conductor_dm.Geometry.Areas[surface_ID].Boundary
|
|
89
|
+
for curve_ID in outer_boundary_curves_a:
|
|
90
|
+
curve = Conductor_dm.Geometry.Curves[curve_ID]
|
|
91
|
+
for point_ID in curve.Points:
|
|
92
|
+
point = Conductor_dm.Geometry.Points[point_ID]
|
|
93
|
+
outer_boundary_points_a.append(tuple(point.Coordinates))
|
|
94
|
+
|
|
95
|
+
for matrix_partition in geometry_class.matrix: # 2) Find the outer boundary points of the matrix partitions
|
|
96
|
+
outer_boundary_points_b = []
|
|
97
|
+
outer_boundary_curves_b = matrix_partition.boundary_curves
|
|
98
|
+
if len(outer_boundary_curves_b) == len(outer_boundary_curves_a): # If the number of boundary curves is different, the surfaces are not the same
|
|
99
|
+
for curve in outer_boundary_curves_b:
|
|
100
|
+
for point in curve.points:
|
|
101
|
+
outer_boundary_points_b.append(tuple(point.pos))
|
|
102
|
+
|
|
103
|
+
if np.allclose(sorted(outer_boundary_points_a), sorted(outer_boundary_points_b)): # If the outer boundary points are the same, the surfaces are the same
|
|
104
|
+
surfaces_excluded_from_TI_tags.append(matrix_partition.physical_surface_tag) # 3) Add the physical surface tag to the list of surfaces to exclude
|
|
105
|
+
break
|
|
106
|
+
|
|
107
|
+
solution_parameters.Surfaces_excluded_from_TI = surfaces_excluded_from_TI_tags # Replace the surface IDs with the physical surface tags
|
|
108
|
+
|
|
109
|
+
FilesAndFolders.write_data_to_yaml(os.path.join(self.solution_folder, "MaterialProperties.yaml"), solution_parameters.dict())
|
|
110
|
+
self.material_properties_model = solution_parameters
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def assemble_pro(self):
|
|
116
|
+
print("Assembling .pro file")
|
|
117
|
+
self.ass_pro.assemble_combined_pro(template = self.cacdm.solve.pro_template, rm = self.regions_model, dm = self.fdm, ed=self.ed, mp=self.material_properties_model)
|
|
118
|
+
|
|
119
|
+
def run_getdp(self, solve = True, postOperation = True, gui = False):
|
|
120
|
+
|
|
121
|
+
command = ["-v2", "-verbose", "3"]
|
|
122
|
+
if solve:
|
|
123
|
+
command += ["-solve", "MagDyn"]
|
|
124
|
+
if self.cacdm.solve.formulation_parameters.dynamic_correction:
|
|
125
|
+
command += ["-pos", "MagDyn", "MagDyn_dynCorr"]
|
|
126
|
+
else:
|
|
127
|
+
command += ["-pos", "MagDyn"]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
startTime = timeit.default_timer()
|
|
131
|
+
getdpProcess = subprocess.Popen([self.GetDP_path, self.pro_file, "-msh", self.mesh_file] + command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
132
|
+
|
|
133
|
+
with getdpProcess.stdout:
|
|
134
|
+
for line in iter(getdpProcess.stdout.readline, b""):
|
|
135
|
+
line = line.decode("utf-8").rstrip()
|
|
136
|
+
line = line.split("\r")[-1]
|
|
137
|
+
if not "Test" in line:
|
|
138
|
+
if line.startswith("Info"):
|
|
139
|
+
parsedLine = re.sub(r"Info\s+:\s+", "", line)
|
|
140
|
+
logger.info(parsedLine)
|
|
141
|
+
elif line.startswith("Warning"):
|
|
142
|
+
parsedLine = re.sub(r"Warning\s+:\s+", "", line)
|
|
143
|
+
logger.warning(parsedLine)
|
|
144
|
+
elif line.startswith("Error"):
|
|
145
|
+
parsedLine = re.sub(r"Error\s+:\s+", "", line)
|
|
146
|
+
logger.error(parsedLine)
|
|
147
|
+
logger.error("Solving CAC failed.")
|
|
148
|
+
# raise Exception(parsedLine)
|
|
149
|
+
elif re.match("##", line):
|
|
150
|
+
logger.critical(line)
|
|
151
|
+
else:
|
|
152
|
+
logger.info(line)
|
|
153
|
+
|
|
154
|
+
simulation_time = timeit.default_timer()-startTime
|
|
155
|
+
# Save simulation time:
|
|
156
|
+
if solve:
|
|
157
|
+
logger.info(f"Solving CAC_1 has finished in {round(simulation_time, 3)} seconds.")
|
|
158
|
+
with open(os.path.join(self.solution_folder, 'test_temporary', 'simulation_time.txt'), 'w') as file:
|
|
159
|
+
file.write(str(simulation_time))
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
if gui and ((postOperation and not solve) or (solve and postOperation and self.cacdm.postproc.generate_pos_files)):
|
|
163
|
+
# gmsh.option.setNumber("Geometry.Volumes", 1)
|
|
164
|
+
# gmsh.option.setNumber("Geometry.Surfaces", 1)
|
|
165
|
+
# gmsh.option.setNumber("Geometry.Curves", 1)
|
|
166
|
+
# gmsh.option.setNumber("Geometry.Points", 0)
|
|
167
|
+
posFiles = [
|
|
168
|
+
fileName
|
|
169
|
+
for fileName in os.listdir(self.solution_folder)
|
|
170
|
+
if fileName.endswith(".pos")
|
|
171
|
+
]
|
|
172
|
+
for posFile in posFiles:
|
|
173
|
+
gmsh.open(os.path.join(self.solution_folder, posFile))
|
|
174
|
+
self.gu.launch_interactive_GUI()
|
|
175
|
+
else:
|
|
176
|
+
gmsh.clear()
|
|
177
|
+
gmsh.finalize()
|
|
178
|
+
|
|
179
|
+
def cleanup(self):
|
|
180
|
+
"""
|
|
181
|
+
This funtion is used to remove .msh, .pre and .res files from the solution folder, as they may be large and not needed.
|
|
182
|
+
"""
|
|
183
|
+
magnet_name = self.fdm.general.magnet_name
|
|
184
|
+
cleanup = self.cacdm.postproc.cleanup
|
|
185
|
+
|
|
186
|
+
if cleanup.remove_res_file:
|
|
187
|
+
res_file_path = os.path.join(self.solution_folder, f"{magnet_name}.res")
|
|
188
|
+
if os.path.exists(res_file_path):
|
|
189
|
+
os.remove(res_file_path)
|
|
190
|
+
logger.info(f"Removed {magnet_name}.res")
|
|
191
|
+
|
|
192
|
+
if cleanup.remove_pre_file:
|
|
193
|
+
pre_file_path = os.path.join(self.solution_folder, f"{magnet_name}.pre")
|
|
194
|
+
if os.path.exists(pre_file_path):
|
|
195
|
+
os.remove(pre_file_path)
|
|
196
|
+
logger.info(f"Removed {magnet_name}.pre")
|
|
197
|
+
|
|
198
|
+
if cleanup.remove_msh_file:
|
|
199
|
+
msh_file_path = os.path.join(self.mesh_folder, f"{magnet_name}.msh")
|
|
200
|
+
if os.path.exists(msh_file_path):
|
|
201
|
+
os.remove(msh_file_path)
|
|
202
202
|
logger.info(f"Removed {magnet_name}.msh")
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import pathlib
|
|
3
|
+
import subprocess
|
|
4
|
+
import logging
|
|
5
|
+
import timeit
|
|
6
|
+
import re
|
|
3
7
|
|
|
4
8
|
import gmsh
|
|
5
|
-
|
|
6
9
|
from fiqus.pro_assemblers.ProAssembler import ASS_PRO as aP
|
|
7
10
|
from fiqus.utils.Utils import GmshUtils
|
|
8
11
|
from fiqus.utils.Utils import FilesAndFolders as Util
|
|
9
12
|
from fiqus.data import DataFiQuS as dF
|
|
10
|
-
from fiqus.data
|
|
13
|
+
from fiqus.data import RegionsModelFiQuS as rM
|
|
14
|
+
from fiqus.data import DataMultipole as dM
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
class AssignNaming:
|
|
@@ -19,75 +25,142 @@ class AssignNaming:
|
|
|
19
25
|
self.data: dF.FDM() = data
|
|
20
26
|
|
|
21
27
|
self.naming_conv = {'omega': 'Omega', 'boundary': 'Bd_', 'powered': '_p', 'induced': '_i', 'air': '_a',
|
|
22
|
-
'air_far_field': '_aff', 'iron': '_bh', 'conducting': '_c', 'terms': 'Terms'}
|
|
23
|
-
self.data.magnet.postproc.volumes = \
|
|
24
|
-
[self.naming_conv['omega'] + self.naming_conv[var] if
|
|
25
|
-
|
|
28
|
+
'air_far_field': '_aff', 'iron': '_bh', 'conducting': '_c', 'insulator': '_ins', 'terms': 'Terms'}
|
|
29
|
+
self.data.magnet.postproc.electromagnetics.volumes = \
|
|
30
|
+
[self.naming_conv['omega'] + (self.naming_conv[var] if var != 'omega' else '') for var in self.data.magnet.postproc.electromagnetics.volumes]
|
|
31
|
+
self.data.magnet.postproc.thermal.volumes = \
|
|
32
|
+
[self.naming_conv['omega'] + (self.naming_conv[var] if var != 'omega' else '') for var in self.data.magnet.postproc.thermal.volumes]
|
|
26
33
|
|
|
27
34
|
|
|
28
35
|
class RunGetdpMultipole:
|
|
29
|
-
def __init__(self, data: AssignNaming = None,
|
|
30
|
-
settings: dict = None, verbose: bool = False):
|
|
36
|
+
def __init__(self, data: AssignNaming = None, solution_folder: str = None, GetDP_path: str = None, verbose: bool = False):
|
|
31
37
|
"""
|
|
32
38
|
Class to solve pro file
|
|
33
39
|
:param data: FiQuS data model
|
|
34
|
-
:param
|
|
40
|
+
:param GetDP_path: settings data model
|
|
35
41
|
:param verbose: If True more information is printed in python console.
|
|
36
42
|
"""
|
|
43
|
+
logger.info(
|
|
44
|
+
f"Initializing Multipole runner for {os.path.basename(solution_folder)}."
|
|
45
|
+
)
|
|
37
46
|
self.data: dF.FDM() = data.data
|
|
38
47
|
self.naming_conv: dict = data.naming_conv
|
|
39
|
-
self.set: dF.FiQuSSettings() = sett
|
|
40
48
|
self.solution_folder = solution_folder
|
|
41
|
-
self.
|
|
49
|
+
self.GetDP_path = GetDP_path
|
|
42
50
|
self.verbose: bool = verbose
|
|
51
|
+
self.call_method = 'subprocess' # or onelab
|
|
43
52
|
|
|
44
|
-
self.
|
|
53
|
+
self.rm_EM = rM.RegionsModel()
|
|
54
|
+
self.rm_TH = rM.RegionsModel()
|
|
55
|
+
self.rc = dM.MultipoleRegionCoordinate()\
|
|
56
|
+
if self.data.magnet.mesh.thermal.isothermal_conductors and self.data.magnet.solve.thermal.solve_type else None
|
|
45
57
|
|
|
46
58
|
self.gu = GmshUtils(self.solution_folder, self.verbose)
|
|
47
|
-
self.gu.initialize()
|
|
59
|
+
self.gu.initialize(verbosity_Gmsh=self.data.run.verbosity_Gmsh)
|
|
48
60
|
self.occ = gmsh.model.occ
|
|
49
61
|
self.mesh = gmsh.model.mesh
|
|
50
62
|
|
|
51
63
|
self.brep_iron_curves = {1: set(), 2: set(), 3: set(), 4: set()}
|
|
52
|
-
self.
|
|
64
|
+
self.mesh_files = os.path.join(os.path.dirname(self.solution_folder), self.data.general.magnet_name)
|
|
53
65
|
self.model_file = os.path.join(self.solution_folder, 'Center_line.csv')
|
|
54
66
|
|
|
55
|
-
self.
|
|
56
|
-
else self.data.magnet.solve.I_initial[0])
|
|
67
|
+
self.mf = {'EM': f"{self.mesh_files}_EM.msh", 'TH': f"{self.mesh_files}_TH.msh"}
|
|
57
68
|
|
|
58
|
-
def
|
|
59
|
-
|
|
69
|
+
def loadRegionFiles(self):
|
|
70
|
+
if self.data.magnet.solve.electromagnetics.solve_type:
|
|
71
|
+
self.rm_EM = Util.read_data_from_yaml(f"{self.mesh_files}_EM.reg", rM.RegionsModel)
|
|
72
|
+
if self.data.magnet.solve.thermal.solve_type:
|
|
73
|
+
self.rm_TH = Util.read_data_from_yaml(f"{self.mesh_files}_TH.reg", rM.RegionsModel)
|
|
60
74
|
|
|
61
|
-
def
|
|
62
|
-
self.
|
|
63
|
-
for name in self.rm.powered['Multipole'].vol.names:
|
|
64
|
-
if name[-3:] == 'pos':
|
|
65
|
-
self.rm.powered['Multipole'].vol.currents.append(self.II)
|
|
66
|
-
else:
|
|
67
|
-
self.rm.powered['Multipole'].vol.currents.append(-self.II)
|
|
75
|
+
def loadRegionCoordinateFile(self):
|
|
76
|
+
self.rc = Util.read_data_from_yaml(f"{self.mesh_files}_TH.reco", dM.MultipoleRegionCoordinate)
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
def assemblePro(self):
|
|
79
|
+
logger.info(f"Assembling pro file...")
|
|
80
|
+
start_time = timeit.default_timer()
|
|
81
|
+
ap = aP(file_base_path=os.path.join(self.solution_folder, self.data.general.magnet_name), naming_conv=self.naming_conv)
|
|
71
82
|
BH_curves_path = os.path.join(pathlib.Path(os.path.dirname(__file__)).parent, 'pro_material_functions', 'ironBHcurves.pro')
|
|
72
|
-
ap.assemble_combined_pro(template=self.
|
|
83
|
+
ap.assemble_combined_pro(template='Multipole_template.pro', rm_EM=self.rm_EM, rm_TH=self.rm_TH, rc=self.rc, dm=self.data, mf=self.mf, BH_curves_path=BH_curves_path)
|
|
84
|
+
logger.info(
|
|
85
|
+
f"Assembling pro file took"
|
|
86
|
+
f" {timeit.default_timer() - start_time:.2f} s."
|
|
87
|
+
)
|
|
73
88
|
|
|
74
89
|
def solve_and_postprocess(self):
|
|
75
|
-
|
|
76
|
-
self.
|
|
90
|
+
commands = None
|
|
91
|
+
if self.call_method == 'onelab':
|
|
92
|
+
commands = f"-solve -v2 -pos -verbose {self.data.run.verbosity_GetDP}"
|
|
93
|
+
elif self.call_method == 'subprocess':
|
|
94
|
+
commands = []
|
|
95
|
+
commands.append(["-solve", 'resolution', "-v2", "-pos", "Dummy", "-verbose", str(self.data.run.verbosity_GetDP)])
|
|
96
|
+
|
|
97
|
+
self._run(commands=commands)
|
|
77
98
|
|
|
78
99
|
def postprocess(self):
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
if self.call_method == 'onelab':
|
|
101
|
+
command = "-v2 -pos -verbose {self.data.run.verbosity_GetDP}"
|
|
102
|
+
elif self.call_method == 'subprocess':
|
|
103
|
+
command = [["-v2", "-pos", "-verbose", str(self.data.run.verbosity_GetDP)]]
|
|
104
|
+
self._run(commands=command)
|
|
105
|
+
|
|
106
|
+
def _run(self, commands):
|
|
107
|
+
logger.info("Solving...")
|
|
108
|
+
start_time = timeit.default_timer()
|
|
109
|
+
if self.call_method == 'onelab':
|
|
110
|
+
for command in commands:
|
|
111
|
+
gmsh.onelab.run(f"{self.data.general.magnet_name}",
|
|
112
|
+
f"{self.GetDP_path} {os.path.join(self.solution_folder, self.data.general.magnet_name)}.pro {command}")
|
|
113
|
+
gmsh.onelab.setChanged("GetDP", 0)
|
|
114
|
+
elif self.call_method == 'subprocess':
|
|
115
|
+
# subprocess.call([f"{self.GetDP_path}", f"{os.path.join(self.solution_folder, self.data.general.magnet_name)}.pro"] + command + ["-msh", f"{self.mesh_files}.msh"])
|
|
116
|
+
|
|
117
|
+
# view_tag = gmsh.view.getTags() # this should be b
|
|
118
|
+
# # # v = "View[" + str(gmsh.view.getIndex('b')) + "]"
|
|
119
|
+
# gmsh.view.write(view_tag, f"{os.path.join(self.solution_folder, self.data.general.magnet_name)}-view.msh")
|
|
120
|
+
|
|
121
|
+
if self.data.magnet.solve.noOfMPITasks:
|
|
122
|
+
mpi_prefix = ["mpiexec", "-np", str(self.data.magnet.solve.noOfMPITasks)]
|
|
123
|
+
else:
|
|
124
|
+
mpi_prefix = []
|
|
125
|
+
|
|
126
|
+
for command in commands:
|
|
127
|
+
getdpProcess = subprocess.Popen(
|
|
128
|
+
mpi_prefix + [f"{self.GetDP_path}", f"{os.path.join(self.solution_folder, self.data.general.magnet_name)}.pro"] +
|
|
129
|
+
command,
|
|
130
|
+
stdout=subprocess.PIPE,
|
|
131
|
+
stderr=subprocess.STDOUT,
|
|
132
|
+
)
|
|
133
|
+
with getdpProcess.stdout:
|
|
134
|
+
for line in iter(getdpProcess.stdout.readline, b""):
|
|
135
|
+
line = line.decode("utf-8").rstrip()
|
|
136
|
+
line = line.split("\r")[-1]
|
|
137
|
+
if "Info" in line:
|
|
138
|
+
parsedLine = re.sub(r"Info\s*:\s*", "", line)
|
|
139
|
+
logger.info(parsedLine)
|
|
140
|
+
elif "Warning" in line:
|
|
141
|
+
parsedLine = re.sub(r"Warning\s*:\s*", "", line)
|
|
142
|
+
if "Unknown" not in parsedLine:
|
|
143
|
+
logger.warning(parsedLine)
|
|
144
|
+
elif "Error" in line:
|
|
145
|
+
parsedLine = re.sub(r"Error\s*:\s*", "", line)
|
|
146
|
+
logger.error(parsedLine)
|
|
147
|
+
raise Exception(parsedLine)
|
|
148
|
+
elif "Critical" in line:
|
|
149
|
+
parsedLine = re.sub(r"Critical\s*:\s*", "", line)
|
|
150
|
+
logger.critical(parsedLine)
|
|
151
|
+
# catch the maximum temperature line
|
|
152
|
+
elif "Maximum temperature" in line:
|
|
153
|
+
parsedLine = re.sub(r"Print\s*:\s*", "", line)
|
|
154
|
+
logger.info(parsedLine)
|
|
155
|
+
# this activates the debugging message mode
|
|
156
|
+
elif self.data.run.verbosity_GetDP > 99:
|
|
157
|
+
logger.info(line)
|
|
158
|
+
|
|
159
|
+
getdpProcess.wait()
|
|
160
|
+
|
|
161
|
+
logger.info(
|
|
162
|
+
f"Solving took {timeit.default_timer() - start_time:.2f} s."
|
|
163
|
+
)
|
|
91
164
|
|
|
92
165
|
def ending_step(self, gui: bool = False):
|
|
93
166
|
if gui:
|