fiqus 2025.11.0__py3-none-any.whl → 2026.1.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 +9 -0
- fiqus/data/DataConductor.py +112 -3
- fiqus/data/DataFiQuS.py +4 -3
- fiqus/data/DataFiQuSConductorAC_CC.py +345 -0
- fiqus/data/DataFiQuSConductorAC_Rutherford.py +569 -0
- fiqus/data/DataFiQuSConductorAC_Strand.py +3 -3
- fiqus/data/DataFiQuSHomogenizedConductor.py +478 -0
- fiqus/geom_generators/GeometryConductorAC_CC.py +1906 -0
- fiqus/geom_generators/GeometryConductorAC_Rutherford.py +706 -0
- fiqus/geom_generators/GeometryConductorAC_Strand_RutherfordCopy.py +1848 -0
- fiqus/geom_generators/GeometryHomogenizedConductor.py +183 -0
- fiqus/getdp_runners/RunGetdpConductorAC_CC.py +123 -0
- fiqus/getdp_runners/RunGetdpConductorAC_Rutherford.py +200 -0
- fiqus/getdp_runners/RunGetdpHomogenizedConductor.py +178 -0
- fiqus/mains/MainConductorAC_CC.py +148 -0
- fiqus/mains/MainConductorAC_Rutherford.py +76 -0
- fiqus/mains/MainHomogenizedConductor.py +112 -0
- fiqus/mesh_generators/MeshConductorAC_CC.py +1305 -0
- fiqus/mesh_generators/MeshConductorAC_Rutherford.py +235 -0
- fiqus/mesh_generators/MeshConductorAC_Strand_RutherfordCopy.py +718 -0
- fiqus/mesh_generators/MeshHomogenizedConductor.py +229 -0
- fiqus/post_processors/PostProcessAC_CC.py +65 -0
- fiqus/post_processors/PostProcessAC_Rutherford.py +142 -0
- fiqus/post_processors/PostProcessHomogenizedConductor.py +114 -0
- fiqus/pro_templates/combined/CAC_CC_template.pro +542 -0
- fiqus/pro_templates/combined/CAC_Rutherford_template.pro +1742 -0
- fiqus/pro_templates/combined/HomogenizedConductor_template.pro +1663 -0
- {fiqus-2025.11.0.dist-info → fiqus-2026.1.0.dist-info}/METADATA +9 -12
- {fiqus-2025.11.0.dist-info → fiqus-2026.1.0.dist-info}/RECORD +36 -13
- tests/test_geometry_generators.py +40 -0
- tests/test_mesh_generators.py +76 -0
- tests/test_solvers.py +137 -0
- /fiqus/pro_templates/combined/{ConductorAC_template.pro → CAC_Strand_template.pro} +0 -0
- {fiqus-2025.11.0.dist-info → fiqus-2026.1.0.dist-info}/LICENSE.txt +0 -0
- {fiqus-2025.11.0.dist-info → fiqus-2026.1.0.dist-info}/WHEEL +0 -0
- {fiqus-2025.11.0.dist-info → fiqus-2026.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
Group {
|
|
2
|
+
// Volumes (surfaces in 2D)
|
|
3
|
+
HTS = Region[{<< rm.powered.HTS.vol.numbers | join(', ') >>}];
|
|
4
|
+
{% set ps_names = rm.induced.Stabilizer.vol.names or [] %}
|
|
5
|
+
{% set ps_nums = rm.induced.Stabilizer.vol.numbers or [] %}
|
|
6
|
+
Copper = Region[{<< ps_nums[ ps_names.index('CopperTop') ] >>, << ps_nums[ ps_names.index('CopperBottom') ] >>, << ps_nums[ ps_names.index('CopperLeft') ] >>, << ps_nums[ ps_names.index('CopperRight') ] >>}];
|
|
7
|
+
Silver = Region[{<< ps_nums[ ps_names.index('SilverTop') ] >>, << ps_nums[ ps_names.index('SilverBottom') ] >>}];
|
|
8
|
+
Substrate = Region[{<< ps_nums[ ps_names.index('Substrate') ] >>}];
|
|
9
|
+
Air = Region[<< rm.air.vol.number >>];
|
|
10
|
+
// Surfaces (curves in 2D)
|
|
11
|
+
Air_out = Region[<< rm.air.surf.number >>];
|
|
12
|
+
Air_in = Region[<< rm.air.line.number >>];
|
|
13
|
+
// Cuts
|
|
14
|
+
Cut = Region[<< rm.air.cochain.numbers[0] >>];
|
|
15
|
+
// Cut = Region[{}];
|
|
16
|
+
Cuts = Region[{ Cut }];
|
|
17
|
+
// Gauging points
|
|
18
|
+
Gauging_point = Region[{<< rm.air.point.numbers[0] >>}];
|
|
19
|
+
// Abstract domains
|
|
20
|
+
LinOmegaC = Region[{ Copper, Silver, Substrate }];
|
|
21
|
+
NonLinOmegaC = Region[{ HTS }];
|
|
22
|
+
OmegaC = Region[{ LinOmegaC, NonLinOmegaC }];
|
|
23
|
+
OmegaCC = Region[{ Air }];
|
|
24
|
+
Omega = Region[{ OmegaC, OmegaCC }];
|
|
25
|
+
BndOmegaC = Region[{ Air_in }];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
Function {
|
|
29
|
+
// ------- GENERAL PARAMETERS -------
|
|
30
|
+
T[] = <<dm.magnet.solve.general_parameters.temperature>>;
|
|
31
|
+
|
|
32
|
+
// ------- MATERIAL PARAMETERS -------
|
|
33
|
+
mu0 = Pi*4e-7; // [H/m]
|
|
34
|
+
nu0 = 1.0/mu0; // [m/H]
|
|
35
|
+
mu[Omega] = mu0;
|
|
36
|
+
nu[Omega] = nu0;
|
|
37
|
+
// Normal resistivities
|
|
38
|
+
{% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu' %}
|
|
39
|
+
rho[Copper] = CFUN_rhoCu_T_B[T[], $1]{<<dm.conductors[dm.magnet.solve.conductor_name].strand.RRR>>}; // [Ohm*m]
|
|
40
|
+
{% endif %}
|
|
41
|
+
{% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_silver == 'CFUN_rhoAg' %}
|
|
42
|
+
rho[Silver] = CFUN_rhoAg_T_B[T[], $1]{<<dm.conductors[dm.magnet.solve.conductor_name].strand.RRR_silver>>, 273}; // [Ohm*m]
|
|
43
|
+
{% endif %}
|
|
44
|
+
{% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_substrate == 'CFUN_rhoHast' %}
|
|
45
|
+
rho[Substrate] = CFUN_rhoHast_T[T[]]; // [Ohm*m]
|
|
46
|
+
{% endif %}
|
|
47
|
+
// Power law
|
|
48
|
+
{% if dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Succi_fixed' %}
|
|
49
|
+
jc[] = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Jc_factor>> * CFUN_HTS_JcFit_Succi_T_B[T[], $1];
|
|
50
|
+
{% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Fujikura' %}
|
|
51
|
+
jc[] = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Jc_factor>> * CFUN_HTS_JcFit_Fujikura_T_B_theta[T[], $1, $2];
|
|
52
|
+
{% endif %}
|
|
53
|
+
ec = <<dm.conductors[dm.magnet.solve.conductor_name].strand.ec_superconductor>>;
|
|
54
|
+
n = <<dm.conductors[dm.magnet.solve.conductor_name].strand.n_value_superconductor>>; // [-] power law index, one key parameter for the power law
|
|
55
|
+
eps_jc[] = <<dm.conductors[dm.magnet.solve.conductor_name].strand.minimum_jc_fraction>> * jc[<<dm.conductors[dm.magnet.solve.conductor_name].strand.minimum_jc_field>>, 0.0];
|
|
56
|
+
rho_power[] = ec / (Max[jc[$2, $3],eps_jc[]]) * (Norm[$1] / (Max[jc[$2, $3],eps_jc[]]))^(n - 1); // [Ohm m] power law resistivity
|
|
57
|
+
e_power[] = rho_power[$1, $2, $3] * $1;
|
|
58
|
+
dedj_power[] = (
|
|
59
|
+
ec / ((Max[jc[$2, $3],eps_jc[]])#1) * (Norm[$1]/#1)^(n - 1) * TensorDiag[1, 1, 1] +
|
|
60
|
+
ec / (#1)^3 * (n - 1) * (Norm[$1]/#1)^(n - 3) * SquDyadicProduct[$1]);
|
|
61
|
+
rho[HTS] = rho_power[$1, $2, $3];
|
|
62
|
+
dedj[HTS] = dedj_power[$1, $2, $3];
|
|
63
|
+
|
|
64
|
+
angle[] = 180/Pi * Atan2[CompX[$1], CompY[$1]]; // angle of vector with respect to the normal direction in degree (assuming the tape normal is along the y-axis)
|
|
65
|
+
|
|
66
|
+
// ------- SOURCE PARAMETERS -------
|
|
67
|
+
directionApplied[] = Vector[Sin[<<dm.magnet.solve.source_parameters.field_angle_with_respect_to_normal_direction>>*Pi/180], Cos[<<dm.magnet.solve.source_parameters.field_angle_with_respect_to_normal_direction>>*Pi/180], 0.];
|
|
68
|
+
{% if dm.magnet.solve.source_parameters.source_type == 'sine' %}
|
|
69
|
+
// Sine wave source (with DC component)
|
|
70
|
+
f = <<dm.magnet.solve.source_parameters.sine.frequency>>; // Frequency of applied field [Hz]
|
|
71
|
+
time_multiplier = 1; // Set to 1, as it is being used in the resolution
|
|
72
|
+
I_transport[] = <<dm.magnet.solve.source_parameters.sine.current_amplitude>> * Sin[2*Pi*f * $Time];
|
|
73
|
+
hsVal[] = nu0 * (<<dm.magnet.solve.source_parameters.sine.field_amplitude>> * Sin[2*Pi*f * $Time]) * directionApplied[];
|
|
74
|
+
hsVal_prev[] = nu0 *(<<dm.magnet.solve.source_parameters.sine.field_amplitude>> * Sin[2*Pi*f * ($Time-$DTime)]) * directionApplied[];
|
|
75
|
+
{% elif dm.magnet.solve.source_parameters.source_type == 'piecewise' %}
|
|
76
|
+
time_multiplier = <<dm.magnet.solve.source_parameters.piecewise.time_multiplier>>;
|
|
77
|
+
applied_field_multiplier = <<dm.magnet.solve.source_parameters.piecewise.applied_field_multiplier>>;
|
|
78
|
+
transport_current_multiplier = <<dm.magnet.solve.source_parameters.piecewise.transport_current_multiplier>>;
|
|
79
|
+
{% if dm.magnet.solve.source_parameters.piecewise.source_csv_file %} // Source from CSV file
|
|
80
|
+
timeList() = {<<ed['time']|join(', ')>>};
|
|
81
|
+
bList() = {<<ed['b']|join(', ')>>};
|
|
82
|
+
IList() = {<<ed['I']|join(', ')>>};
|
|
83
|
+
timebList() = ListAlt[timeList(), bList()];
|
|
84
|
+
timeIList() = ListAlt[timeList(), IList()];
|
|
85
|
+
hsVal[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{List[timebList()]} * directionApplied[];
|
|
86
|
+
hsVal_prev[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time-$DTime)/time_multiplier]]{List[timebList()]} * directionApplied[];
|
|
87
|
+
I_transport[] = transport_current_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{List[timeIList()]};
|
|
88
|
+
{% else %} // Source from parameters given as lists
|
|
89
|
+
times_source_piecewise_linear() = {<<dm.magnet.solve.source_parameters.piecewise.times|join(', ')>>};
|
|
90
|
+
transport_currents_relative_piecewise_linear() = {<<dm.magnet.solve.source_parameters.piecewise.transport_currents_relative|join(', ')>>};
|
|
91
|
+
applied_fields_relative_piecewise_linear() = {<<dm.magnet.solve.source_parameters.piecewise.applied_fields_relative|join(', ')>>};
|
|
92
|
+
hsVal[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{ListAlt[times_source_piecewise_linear(), applied_fields_relative_piecewise_linear()]} * directionApplied[];
|
|
93
|
+
hsVal_prev[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time-$DTime)/time_multiplier]]{ListAlt[times_source_piecewise_linear(), applied_fields_relative_piecewise_linear()]} * directionApplied[];
|
|
94
|
+
I_transport[] = transport_current_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{ListAlt[times_source_piecewise_linear(), transport_currents_relative_piecewise_linear()]};
|
|
95
|
+
{% endif %}
|
|
96
|
+
{% endif %}
|
|
97
|
+
dbsdt[] = mu0 * (hsVal[] - hsVal_prev[]) / $DTime; // must be a finite difference to avoid error accumulation
|
|
98
|
+
|
|
99
|
+
// ------- NUMERICAL PARAMETERS -------
|
|
100
|
+
timeStart = 0.; // Initial time [s]
|
|
101
|
+
{% if dm.magnet.solve.source_parameters.source_type == 'sine'%}
|
|
102
|
+
timeFinal = <<dm.magnet.solve.numerical_parameters.sine.number_of_periods_to_simulate>>/f; // Final time for source definition (s)
|
|
103
|
+
dt = 1 / (f*<<dm.magnet.solve.numerical_parameters.sine.timesteps_per_period>>); // Time step (initial if adaptive) (s)
|
|
104
|
+
dt_max = dt; // Fixed maximum time step
|
|
105
|
+
dt_max_var[] = dt_max;
|
|
106
|
+
{% else %}
|
|
107
|
+
timeFinal = <<dm.magnet.solve.numerical_parameters.piecewise.time_to_simulate>>;
|
|
108
|
+
{% if dm.magnet.solve.numerical_parameters.piecewise.variable_max_timestep %}
|
|
109
|
+
times_max_timestep_piecewise_linear() = {<<dm.magnet.solve.numerical_parameters.piecewise.times_max_timestep_piecewise_linear|join(', ')>>};
|
|
110
|
+
max_timestep_piecewise_linear() = {<<dm.magnet.solve.numerical_parameters.piecewise.max_timestep_piecewise_linear|join(', ')>>};
|
|
111
|
+
dt = max_timestep_piecewise_linear(0);
|
|
112
|
+
dt_max_var[] = InterpolationLinear[Max[0,$Time]]{ListAlt[times_max_timestep_piecewise_linear(), max_timestep_piecewise_linear()]};
|
|
113
|
+
{% else %}
|
|
114
|
+
dt = timeFinal / <<dm.magnet.solve.numerical_parameters.piecewise.timesteps_per_time_to_simulate>>;
|
|
115
|
+
dt_max = dt; // Fixed maximum time step
|
|
116
|
+
dt_max_var[] = dt_max;
|
|
117
|
+
{% endif %}
|
|
118
|
+
{% if dm.magnet.solve.numerical_parameters.piecewise.force_stepping_at_times_piecewise_linear%}
|
|
119
|
+
control_time_instants_list() = {<<dm.magnet.solve.source_parameters.piecewise.times|join(', ')>>, 1e99}; // last one is just to avoid 'seg. fault' errors
|
|
120
|
+
{% endif %}
|
|
121
|
+
{% endif %}
|
|
122
|
+
iter_max = 60; // Maximum number of iterations (after which we exit the iterative loop)
|
|
123
|
+
extrapolationOrder = 1; // Extrapolation order for predictor
|
|
124
|
+
tol_energy = <<dm.magnet.solve.numerical_parameters.relative_tolerance>>; // Tolerance on the relative change of the power indicator
|
|
125
|
+
|
|
126
|
+
// ------- SIMULATION NAME -------
|
|
127
|
+
name = "txt_files";
|
|
128
|
+
resDirectory = StrCat["./",name];
|
|
129
|
+
infoResidualFile = StrCat[resDirectory,"/residual.txt"];
|
|
130
|
+
outputPower = StrCat[resDirectory,"/power.txt"]; // File updated during runtime
|
|
131
|
+
crashReportFile = StrCat[resDirectory,"/crash_report.txt"];
|
|
132
|
+
outputTemperature = StrCat[resDirectory,"/temperature.txt"];
|
|
133
|
+
|
|
134
|
+
{% if dm.magnet.solve.initial_conditions.init_type == 'pos_file' %}
|
|
135
|
+
h_from_file[] = VectorField[XYZ[]]; // After GmshRead[] in Resolution, this vector field contains the solution from a .pos file that can be accessed at any point XYZ[]
|
|
136
|
+
{% endif %}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
Constraint {
|
|
140
|
+
{ Name phi ;
|
|
141
|
+
Case {
|
|
142
|
+
{Region Gauging_point ; Value 0.0 ;}
|
|
143
|
+
{% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
|
|
144
|
+
{Region Omega ; Type InitFromResolution ; NameOfResolution Projection_h_to_h ;}
|
|
145
|
+
{% endif %}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
{ Name Current ;
|
|
149
|
+
Case {
|
|
150
|
+
{Region Cut ; Type Assign ; Value 1.0 ; TimeFunction I_transport[] ;}
|
|
151
|
+
{% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
|
|
152
|
+
{Region Cuts ; Type InitFromResolution ; NameOfResolution Projection_h_to_h ;}
|
|
153
|
+
{% endif %}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
{ Name h ; Type Assign ;
|
|
157
|
+
Case {
|
|
158
|
+
{% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
|
|
159
|
+
{Region OmegaC ; Type InitFromResolution ; NameOfResolution Projection_h_to_h ;}
|
|
160
|
+
{% endif %}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
FunctionSpace {
|
|
166
|
+
// Function space for magnetic field h in h-conform formulation. Main field for the magnetodynamic problem.
|
|
167
|
+
{ Name h_space; Type Form1;
|
|
168
|
+
BasisFunction {
|
|
169
|
+
{ Name gradpsin; NameOfCoef phin; Function BF_GradNode;
|
|
170
|
+
Support Region[{OmegaCC, Air_out}]; Entity NodesOf[OmegaCC]; } // Extend support to boundary for surface integration
|
|
171
|
+
{ Name gradpsin; NameOfCoef phin2; Function BF_GroupOfEdges;
|
|
172
|
+
Support OmegaC; Entity GroupsOfEdgesOnNodesOf[BndOmegaC]; } // To treat properly the conducting domain boundary
|
|
173
|
+
{ Name psie; NameOfCoef he; Function BF_Edge;
|
|
174
|
+
Support OmegaC; Entity EdgesOf[All, Not BndOmegaC]; }
|
|
175
|
+
{ Name ci; NameOfCoef Ii; Function BF_GroupOfEdges;
|
|
176
|
+
Support Omega; Entity GroupsOfEdgesOf[Cuts]; }
|
|
177
|
+
}
|
|
178
|
+
GlobalQuantity {
|
|
179
|
+
{ Name I ; Type AliasOf ; NameOfCoef Ii ; }
|
|
180
|
+
{ Name V ; Type AssociatedWith ; NameOfCoef Ii ; }
|
|
181
|
+
}
|
|
182
|
+
Constraint {
|
|
183
|
+
{ NameOfCoef he; EntityType EdgesOf; NameOfConstraint h; }
|
|
184
|
+
{ NameOfCoef phin; EntityType NodesOf; NameOfConstraint phi; }
|
|
185
|
+
{ NameOfCoef phin2; EntityType NodesOf; NameOfConstraint phi; }
|
|
186
|
+
{ NameOfCoef Ii ; EntityType GroupsOfEdgesOf ; NameOfConstraint Current ; }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
Jacobian {
|
|
192
|
+
{ Name Vol ;
|
|
193
|
+
Case {
|
|
194
|
+
{Region All ; Jacobian Vol ;}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
{ Name Sur ;
|
|
198
|
+
Case {
|
|
199
|
+
{ Region All ; Jacobian Sur ; }
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
Integration {
|
|
205
|
+
{ Name Int ;
|
|
206
|
+
Case {
|
|
207
|
+
{ Type Gauss ;
|
|
208
|
+
Case {
|
|
209
|
+
{ GeoElement Point ; NumberOfPoints 1 ; }
|
|
210
|
+
{ GeoElement Line ; NumberOfPoints 3 ; }
|
|
211
|
+
{ GeoElement Triangle ; NumberOfPoints 3 ; }
|
|
212
|
+
{ GeoElement Quadrangle ; NumberOfPoints 4 ; }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
Formulation{
|
|
220
|
+
// h-formulation
|
|
221
|
+
{ Name MagDyn_hphi; Type FemEquation;
|
|
222
|
+
Quantity {
|
|
223
|
+
{ Name h; Type Local; NameOfSpace h_space; }
|
|
224
|
+
{ Name hp;Type Local; NameOfSpace h_space; } // to avoid auto-symmetrization by GetDP
|
|
225
|
+
{ Name I; Type Global; NameOfSpace h_space[I]; }
|
|
226
|
+
{ Name V; Type Global; NameOfSpace h_space[V]; }
|
|
227
|
+
}
|
|
228
|
+
Equation {
|
|
229
|
+
// Time derivative of b (NonMagnDomain)
|
|
230
|
+
Galerkin { [ mu[] * Dof{h} / $DTime , {h} ];
|
|
231
|
+
In Omega; Integration Int; Jacobian Vol; }
|
|
232
|
+
Galerkin { [ - mu[] * {h}[1] / $DTime , {h} ];
|
|
233
|
+
In Omega; Integration Int; Jacobian Vol; }
|
|
234
|
+
// Induced current (linear OmegaC)
|
|
235
|
+
Galerkin { [ rho[mu0*Norm[{h}]] * Dof{d h} , {d h} ];
|
|
236
|
+
In LinOmegaC; Integration Int; Jacobian Vol; }
|
|
237
|
+
Galerkin { [ rho[{d h}, mu0*Norm[{h}], angle[mu0*{h}]] * {d h} , {d h} ];
|
|
238
|
+
In NonLinOmegaC; Integration Int; Jacobian Vol; }
|
|
239
|
+
Galerkin { [ dedj[{d h}, mu0*Norm[{h}], angle[mu0*{h}]] * Dof{d h} , {d hp} ];
|
|
240
|
+
In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
|
|
241
|
+
Galerkin { [ - dedj[{d h}, mu0*Norm[{h}], angle[mu0*{h}]] * {d h} , {d hp} ];
|
|
242
|
+
In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
|
|
243
|
+
// Natural boundary condition for normal flux density
|
|
244
|
+
Galerkin { [ - dbsdt[] * Normal[] , {dInv h} ];
|
|
245
|
+
In Air_out; Integration Int; Jacobian Sur; }
|
|
246
|
+
// Global term
|
|
247
|
+
GlobalTerm { [ Dof{V} , {I} ] ; In Cuts ; }
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
{% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
|
|
251
|
+
// Projection formulation for initial condition
|
|
252
|
+
{ Name Projection_h_to_h; Type FemEquation;
|
|
253
|
+
Quantity {
|
|
254
|
+
{ Name h; Type Local; NameOfSpace h_space; }
|
|
255
|
+
}
|
|
256
|
+
Equation{
|
|
257
|
+
// For the current formulation, it seems to be accurate enough to project the field directly (and not its curl as an intermediate to reconstruct it).
|
|
258
|
+
// Validity of this to be checked again if we go to different meshes between the initial condition and the following simulation
|
|
259
|
+
Galerkin { [ Dof{h}, {h} ] ;
|
|
260
|
+
In Omega ; Jacobian Vol ; Integration Int ; }
|
|
261
|
+
{% if dm.magnet.solve.initial_conditions.init_type == 'pos_file' %}
|
|
262
|
+
Galerkin { [ - h_from_file[], {h} ] ;
|
|
263
|
+
In Omega ; Jacobian Vol ; Integration Int ; }
|
|
264
|
+
{% elif dm.magnet.solve.initial_conditions.init_type == 'uniform_field' %}
|
|
265
|
+
Galerkin { [ - hsVal[], {h} ] ;
|
|
266
|
+
In Omega ; Jacobian Vol ; Integration Int ; }
|
|
267
|
+
{% endif %}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
{% endif %}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
Macro RelaxationFactors
|
|
274
|
+
// Initialize by computing the increment Delta_x
|
|
275
|
+
CopySolution[A,'x_New'];
|
|
276
|
+
AddVector[A, 1, 'x_New', -1, 'x_Prev', 'Delta_x'];
|
|
277
|
+
// Initialize parameters for relaxation search
|
|
278
|
+
Evaluate[$mult = 1.5625];
|
|
279
|
+
Evaluate[$relaxFactor = 0.4096 ]; // Initial relaxation factor (under-relaxation)
|
|
280
|
+
Evaluate[$decreasing = 0]; // Start by increasing the initial factor (possibly up to over-relaxation)
|
|
281
|
+
Evaluate[$relaxTestNb = 0];
|
|
282
|
+
Evaluate[$maxRelaxTestNb = 6];
|
|
283
|
+
// Try with the initial relaxation factor (save in x_Opt)
|
|
284
|
+
AddVector[A, 1, 'x_Prev', $relaxFactor, 'Delta_x', 'x_Opt']; Evaluate[$factor_Opt = $relaxFactor];
|
|
285
|
+
CopySolution['x_Opt', A]; Generate[A]; GetResidual[A, $res];
|
|
286
|
+
// Print[{$relaxFactor, $res}, Format " Initial factor: %g (res: %g)"];
|
|
287
|
+
// Loop until residual does no longer decrease
|
|
288
|
+
Evaluate[$relaxFactor = $mult * $relaxFactor ];
|
|
289
|
+
While[$relaxTestNb < $maxRelaxTestNb]{
|
|
290
|
+
Evaluate[$res_prev = $res];
|
|
291
|
+
AddVector[A, 1, 'x_Prev', $relaxFactor, 'Delta_x', 'x_New'];
|
|
292
|
+
CopySolution['x_New', A]; Generate[A]; GetResidual[A, $res];
|
|
293
|
+
Evaluate[$relaxTestNb = $relaxTestNb + 1];
|
|
294
|
+
// If residual decreases...
|
|
295
|
+
Test[$res < $res_prev]{
|
|
296
|
+
// Print[{$relaxFactor, $res, $res_prev}, Format " It has decreased with factor: %g (res: %g, previous: %g)"];
|
|
297
|
+
CopySolution[A,'x_Opt']; Evaluate[$factor_Opt = $relaxFactor];
|
|
298
|
+
// Try another relaxation factor
|
|
299
|
+
Test[$decreasing == 0]{
|
|
300
|
+
Evaluate[$relaxFactor = $relaxFactor * $mult];
|
|
301
|
+
}
|
|
302
|
+
{
|
|
303
|
+
Evaluate[$relaxFactor = $relaxFactor / $mult];
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// otherwise...
|
|
307
|
+
{
|
|
308
|
+
// Print[{$relaxFactor, $res, $res_prev}, Format " It has NOT decreased with factor: %g (res: %g, previous: %g)"];
|
|
309
|
+
// If just starting, decrease the factor instead...
|
|
310
|
+
Test[$relaxTestNb == 1]{
|
|
311
|
+
Evaluate[$decreasing = 1];
|
|
312
|
+
Evaluate[$relaxFactor = $relaxFactor / ($mult*$mult)];
|
|
313
|
+
}
|
|
314
|
+
// otherwise, exit the loop and use the last guess as the optimum
|
|
315
|
+
{
|
|
316
|
+
Break; // Abort the while loop
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
CopySolution['x_Opt',A]; // Copy the optimal solution into the system
|
|
321
|
+
Return
|
|
322
|
+
|
|
323
|
+
Macro CustomIterativeLoop
|
|
324
|
+
// Compute first solution guess and residual at step $TimeStep
|
|
325
|
+
Generate[A];
|
|
326
|
+
Solve[A]; Evaluate[ $syscount = $syscount + 1 ];
|
|
327
|
+
Generate[A]; GetResidual[A, $res0];
|
|
328
|
+
Evaluate[ $res = $res0 ];
|
|
329
|
+
Evaluate[ $iter = 0 ];
|
|
330
|
+
Evaluate[ $convCrit = 1e99 ];
|
|
331
|
+
Evaluate[ $factor_Opt = 1 ]; // relaxation factor; 1 by default, might be modified if RelaxationFactors macro is called
|
|
332
|
+
PostOperation[MagDyn_energy];
|
|
333
|
+
Print[{$iter, $res, $res / $res0, $indicLoss},
|
|
334
|
+
Format "%g %14.12e %14.12e %14.12e 1", File infoResidualFile];
|
|
335
|
+
// ----- Enter the iterative loop (hand-made) -----
|
|
336
|
+
While[$convCrit > 1 && $res / $res0 <= 1e10 && $iter < iter_max]{
|
|
337
|
+
{% if dm.magnet.solve.numerical_parameters.relaxation_factors %}
|
|
338
|
+
CopySolution[A,'x_Prev']; // Save previous solution
|
|
339
|
+
{% endif %}
|
|
340
|
+
Solve[A]; Evaluate[ $syscount = $syscount + 1 ];
|
|
341
|
+
{% if dm.magnet.solve.numerical_parameters.relaxation_factors %}
|
|
342
|
+
// If the number of iteration exceeds 10, try to improve the solution further (i.e., reduce the residual); otherwise, just continue
|
|
343
|
+
Test[$iter >= 10]{Call RelaxationFactors;}
|
|
344
|
+
{% endif %}
|
|
345
|
+
Generate[A]; GetResidual[A, $res];
|
|
346
|
+
Evaluate[ $iter = $iter + 1 ];
|
|
347
|
+
Evaluate[ $indicLossOld = $indicLoss];
|
|
348
|
+
PostOperation[MagDyn_energy];
|
|
349
|
+
Print[{$iter, $res, $res / $res0, $indicLoss, $factor_Opt},
|
|
350
|
+
Format "%g %14.12e %14.12e %14.12e %g", File infoResidualFile];
|
|
351
|
+
// Evaluate the convergence indicator
|
|
352
|
+
Evaluate[ $relChangeACLoss = Abs[($indicLossOld - $indicLoss)/((Abs[$indicLossOld]>1e-7 || $iter < 10) ? $indicLossOld:1e-7)] ];
|
|
353
|
+
Evaluate[ $convCrit = $relChangeACLoss/tol_energy];
|
|
354
|
+
}
|
|
355
|
+
Return
|
|
356
|
+
|
|
357
|
+
Resolution {
|
|
358
|
+
{ Name MagDyn;
|
|
359
|
+
System {
|
|
360
|
+
{Name A; NameOfFormulation MagDyn_hphi;}
|
|
361
|
+
}
|
|
362
|
+
Operation {
|
|
363
|
+
// Initialize directories
|
|
364
|
+
CreateDirectory[resDirectory];
|
|
365
|
+
DeleteFile[outputPower];
|
|
366
|
+
DeleteFile[infoResidualFile];
|
|
367
|
+
// Initialize the solution (initial condition)
|
|
368
|
+
SetTime[ timeStart ];
|
|
369
|
+
SetDTime[ dt ];
|
|
370
|
+
SetTimeStep[ 0 ];
|
|
371
|
+
InitSolution[A];
|
|
372
|
+
SaveSolution[A]; // Saves the solution x (from Ax = B) to .res file
|
|
373
|
+
Evaluate[ $syscount = 0 ];
|
|
374
|
+
Evaluate[ $saved = 1 ];
|
|
375
|
+
Evaluate[ $elapsedCTI = 1 ]; // Number of control time instants already treated
|
|
376
|
+
Evaluate[ $isCTI = 0 ];
|
|
377
|
+
// ----- Enter implicit Euler time integration loop (hand-made) -----
|
|
378
|
+
// Avoid too close steps at the end. Stop the simulation if the step becomes ridiculously small
|
|
379
|
+
SetExtrapolationOrder[ extrapolationOrder ];
|
|
380
|
+
While[$Time < timeFinal] {
|
|
381
|
+
SetTime[ $Time + $DTime ]; // Time instant at which we are looking for the solution
|
|
382
|
+
SetTimeStep[ $TimeStep + 1 ];
|
|
383
|
+
{% if dm.magnet.solve.numerical_parameters.piecewise.force_stepping_at_times_piecewise_linear and dm.magnet.solve.source_parameters.source_type == 'piecewise'%}
|
|
384
|
+
// Make sure all CTI are exactly chosen
|
|
385
|
+
Evaluate[ $isCTI = 0 ];
|
|
386
|
+
Test[$Time >= time_multiplier*AtIndex[$elapsedCTI]{List[control_time_instants_list]} - 1e-7 ]{
|
|
387
|
+
Evaluate[ $isCTI = 1, $prevDTime = $DTime ]; // Also save the previous time step to restart from it after the CTI
|
|
388
|
+
SetDTime[ time_multiplier*AtIndex[$elapsedCTI]{List[control_time_instants_list]} - $Time + $DTime ];
|
|
389
|
+
SetTime[ time_multiplier*AtIndex[$elapsedCTI]{List[control_time_instants_list]} ]; // To compute exactly at the asked time instant
|
|
390
|
+
Print[{$Time}, Format "*** Control time instant: %g s."];
|
|
391
|
+
}
|
|
392
|
+
{% endif %}
|
|
393
|
+
// Iterative loop defined as a macro above
|
|
394
|
+
Print[{$Time, $DTime, $TimeStep}, Format "Start new time step. Time: %g s. Time step: %g s. Step: %g."];
|
|
395
|
+
Call CustomIterativeLoop;
|
|
396
|
+
// Has it converged? If yes, save solution and possibly increase the time step...
|
|
397
|
+
Test[ $iter < iter_max && ($res / $res0 <= 1e10 || $res0 == 0)]{
|
|
398
|
+
Print[{$Time, $DTime, $iter}, Format "Converged time %g s with time step %g s in %g iterations."];
|
|
399
|
+
// Save the solution of few time steps (small correction to avoid bad rounding)
|
|
400
|
+
// Test[ $Time >= $saved * writeInterval - 1e-7 || $Time + $DTime >= timeFinal]{
|
|
401
|
+
// Test[ $Time >= $saved * $DTime - 1e-7 || $Time + $DTime >= timeFinal]{
|
|
402
|
+
Test[ 1 ]{
|
|
403
|
+
SaveSolution[A];
|
|
404
|
+
// post
|
|
405
|
+
PostOperation[MagDyn_energy];
|
|
406
|
+
Print[{$Time, $saved}, Format "Saved time %g s (saved solution number %g). Output power infos:"];
|
|
407
|
+
Print[{$Time, $indicLoss}, Format "%g %14.12e", File outputPower];
|
|
408
|
+
Evaluate[$saved = $saved + 1];
|
|
409
|
+
}
|
|
410
|
+
{% if dm.magnet.solve.numerical_parameters.voltage_per_meter_stopping_criterion %}
|
|
411
|
+
PostOperation[V];
|
|
412
|
+
Print[{$voltage}, Format "Voltage per meter: %14.12e V/m"];
|
|
413
|
+
Test[ Abs[$voltage] >= <<dm.magnet.solve.numerical_parameters.voltage_per_meter_stopping_criterion>>]{
|
|
414
|
+
Print["Stopping voltage per meter of <<dm.magnet.solve.numerical_parameters.voltage_per_meter_stopping_criterion>> V/m reached. Stopping simulation."];
|
|
415
|
+
Break; // Abort the time loop
|
|
416
|
+
}
|
|
417
|
+
{% endif %}
|
|
418
|
+
{% if dm.magnet.solve.numerical_parameters.piecewise.force_stepping_at_times_piecewise_linear and dm.magnet.solve.source_parameters.source_type == 'piecewise' %}
|
|
419
|
+
// Consider the time step before the control time instant (if relevant) and increment $elapsedCTI
|
|
420
|
+
Test[ $isCTI == 1 ]{
|
|
421
|
+
Evaluate[ $elapsedCTI = $elapsedCTI + 1 ];
|
|
422
|
+
SetDTime[ $prevDTime ];
|
|
423
|
+
}
|
|
424
|
+
{% endif %}
|
|
425
|
+
// Increase the step if we converged sufficiently "fast" (and not a control time instant)
|
|
426
|
+
Test[ $iter < iter_max / 4 && $DTime < dt_max_var[] && $isCTI == 0 ]{
|
|
427
|
+
Evaluate[ $dt_new = Min[$DTime * 2, dt_max_var[]] ];
|
|
428
|
+
Print[{$dt_new}, Format "*** Fast convergence: increasing time step to %g"];
|
|
429
|
+
SetDTime[$dt_new];
|
|
430
|
+
}
|
|
431
|
+
Test[ $DTime > dt_max_var[]]{
|
|
432
|
+
Evaluate[ $dt_new = dt_max_var[] ];
|
|
433
|
+
Print[{$dt_new}, Format "*** Variable maximum time-stepping: reducing time step to %g"];
|
|
434
|
+
SetDTime[$dt_new];
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// ...otherwise, reduce the time step and try again
|
|
438
|
+
{
|
|
439
|
+
Evaluate[ $dt_new = $DTime / 2 ];
|
|
440
|
+
Print[{$iter, $dt_new},
|
|
441
|
+
Format "*** Non convergence (iter %g): recomputing with reduced step %g"];
|
|
442
|
+
RemoveLastSolution[A];
|
|
443
|
+
SetTime[$Time - $DTime];
|
|
444
|
+
SetTimeStep[$TimeStep - 1];
|
|
445
|
+
SetDTime[$dt_new];
|
|
446
|
+
// If it gets ridicoulously small, end the simulation, and report the information in crash file.
|
|
447
|
+
Test[ $dt_new < dt_max_var[]/10000 ]{
|
|
448
|
+
Print[{$iter, $dt_new, $Time},
|
|
449
|
+
Format "*** Non convergence (iter %g): time step %g too small, stopping the simulation at time %g s.", File crashReportFile];
|
|
450
|
+
// Print[A];
|
|
451
|
+
Exit;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
} // ----- End time loop -----
|
|
455
|
+
// Print information about the resolution and the nonlinear iterations
|
|
456
|
+
Print[{$syscount}, Format "Total number of linear systems solved: %g"];
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
{% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
|
|
460
|
+
{ Name Projection_h_to_h;
|
|
461
|
+
System {
|
|
462
|
+
{Name Projection_h_to_h; NameOfFormulation Projection_h_to_h; DestinationSystem A ;}
|
|
463
|
+
}
|
|
464
|
+
Operation {
|
|
465
|
+
{% if dm.magnet.solve.initial_conditions.init_type == 'pos_file' %}
|
|
466
|
+
GmshRead[StrCat["../", "Solution_<<dm.magnet.solve.initial_conditions.solution_to_init_from>>/", "last_magnetic_field.pos"]]; // This file has to be in format without mesh (no -v2, here with GmshParsed format)
|
|
467
|
+
{% endif %}
|
|
468
|
+
Generate[Projection_h_to_h]; Solve[Projection_h_to_h];
|
|
469
|
+
TransferSolution[Projection_h_to_h];
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
{% endif %}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
PostProcessing {
|
|
476
|
+
{ Name MagDyn_hphi; NameOfFormulation MagDyn_hphi;
|
|
477
|
+
Quantity {
|
|
478
|
+
{ Name phi; Value{ Local{ [ {dInv h} ] ;
|
|
479
|
+
In OmegaCC; Jacobian Vol; } } }
|
|
480
|
+
{ Name h; Value{ Local{ [ {h} ] ;
|
|
481
|
+
In Omega; Jacobian Vol; } } }
|
|
482
|
+
{ Name b; Value{ Local{ [ mu[] * {h} ] ;
|
|
483
|
+
In Omega; Jacobian Vol; } } }
|
|
484
|
+
{ Name b_reaction; Value{ Local{ [ mu[] * ({h} - hsVal[]) ] ;
|
|
485
|
+
In Omega; Jacobian Vol; } } }
|
|
486
|
+
{ Name j; Value{ Local{ [ {d h} ] ;
|
|
487
|
+
In OmegaC; Jacobian Vol; } } }
|
|
488
|
+
{ Name power; Value{
|
|
489
|
+
Local{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ; In LinOmegaC; Jacobian Vol; }
|
|
490
|
+
Local{ [rho[{d h}, mu0*Norm[{h}], angle[mu0*{h}]] * {d h} * {d h}] ; In NonLinOmegaC; Jacobian Vol; } } }
|
|
491
|
+
{ Name totalLoss;
|
|
492
|
+
Value{
|
|
493
|
+
Integral{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ;
|
|
494
|
+
In LinOmegaC ; Integration Int ; Jacobian Vol; }
|
|
495
|
+
Integral{ [rho[{d h}, mu0*Norm[{h}], angle[mu0*{h}]] * {d h} * {d h}] ;
|
|
496
|
+
In NonLinOmegaC ; Integration Int ; Jacobian Vol; }
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
{ Name I; Value { Term{ [ {I} ] ; In Cuts; } } }
|
|
500
|
+
{ Name V; Value { Term{ [ {V} ] ; In Cuts; } } }
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
PostOperation {
|
|
506
|
+
{ Name MagDyn;
|
|
507
|
+
NameOfPostProcessing MagDyn_hphi;
|
|
508
|
+
Operation {
|
|
509
|
+
{% set units_dict = {
|
|
510
|
+
"b": "T",
|
|
511
|
+
"b_reaction": "T",
|
|
512
|
+
"h": "A/m",
|
|
513
|
+
"phi": "A",
|
|
514
|
+
"j": "A/m2",
|
|
515
|
+
"jz": "A/m2",
|
|
516
|
+
"jc": "A/m2",
|
|
517
|
+
"power": "W/m3",
|
|
518
|
+
} %}
|
|
519
|
+
// Local field solutions
|
|
520
|
+
{% for quantity, region in zip(dm.magnet.postproc.pos_files.quantities, dm.magnet.postproc.pos_files.regions) %}
|
|
521
|
+
Print[ <<quantity>>, OnElementsOf <<region>> , File StrCat["<<quantity>>_<<region>>.pos"], Name "<<quantity>> [<<units_dict[quantity]>>]" ];
|
|
522
|
+
{% endfor %}
|
|
523
|
+
Print[ I, OnRegion Cut, File StrCat[resDirectory,"/I.txt"], Format SimpleTable];
|
|
524
|
+
Print[ V, OnRegion Cut, File StrCat[resDirectory,"/V.txt"], Format SimpleTable];
|
|
525
|
+
// Last magnetic field solution for projection. Always saved. Note the special format GmshParsed required for proper GmshRead[] operation in the later pre-resolution.
|
|
526
|
+
Print[ h, OnElementsOf Omega, Format GmshParsed , File "last_magnetic_field.pos", Name "h [A/m]", LastTimeStepOnly ];
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
{ Name MagDyn_energy; LastTimeStepOnly 1 ;
|
|
531
|
+
NameOfPostProcessing MagDyn_hphi;
|
|
532
|
+
Operation {
|
|
533
|
+
Print[ totalLoss[OmegaC], OnGlobal, Format Table, StoreInVariable $indicLoss, File StrCat[resDirectory,"/dummy_loss.txt"] ];
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
{ Name V; LastTimeStepOnly 1 ;
|
|
537
|
+
NameOfPostProcessing MagDyn_hphi;
|
|
538
|
+
Operation {
|
|
539
|
+
Print[ V, OnRegion Cut, Format Table, StoreInVariable $voltage, File StrCat[resDirectory,"/dummy_v.txt"] ];
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|