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.
Files changed (54) hide show
  1. fiqus/MainFiQuS.py +24 -28
  2. fiqus/data/DataConductor.py +350 -301
  3. fiqus/data/DataFiQuS.py +42 -115
  4. fiqus/data/DataFiQuSCCT.py +150 -150
  5. fiqus/data/DataFiQuSConductor.py +97 -84
  6. fiqus/data/DataFiQuSConductorAC_Strand.py +701 -565
  7. fiqus/data/DataModelCommon.py +439 -0
  8. fiqus/data/DataMultipole.py +0 -13
  9. fiqus/data/DataRoxieParser.py +7 -0
  10. fiqus/data/DataWindingsCCT.py +37 -37
  11. fiqus/data/RegionsModelFiQuS.py +61 -104
  12. fiqus/geom_generators/GeometryCCT.py +904 -905
  13. fiqus/geom_generators/GeometryConductorAC_Strand.py +1863 -1391
  14. fiqus/geom_generators/GeometryMultipole.py +5 -4
  15. fiqus/geom_generators/GeometryPancake3D.py +1 -1
  16. fiqus/getdp_runners/RunGetdpCCT.py +13 -4
  17. fiqus/getdp_runners/RunGetdpConductorAC_Strand.py +341 -201
  18. fiqus/getdp_runners/RunGetdpPancake3D.py +2 -2
  19. fiqus/mains/MainConductorAC_Strand.py +141 -133
  20. fiqus/mains/MainMultipole.py +6 -5
  21. fiqus/mains/MainPancake3D.py +3 -4
  22. fiqus/mesh_generators/MeshCCT.py +209 -209
  23. fiqus/mesh_generators/MeshConductorAC_Strand.py +709 -656
  24. fiqus/mesh_generators/MeshMultipole.py +43 -46
  25. fiqus/parsers/ParserDAT.py +16 -16
  26. fiqus/parsers/ParserGetDPOnSection.py +212 -212
  27. fiqus/parsers/ParserGetDPTimeTable.py +134 -134
  28. fiqus/parsers/ParserMSH.py +53 -53
  29. fiqus/parsers/ParserPOS.py +214 -214
  30. fiqus/parsers/ParserRES.py +142 -142
  31. fiqus/plotters/PlotPythonCCT.py +133 -133
  32. fiqus/plotters/PlotPythonConductorAC.py +1079 -855
  33. fiqus/plotters/PlotPythonMultipole.py +18 -18
  34. fiqus/post_processors/PostProcessCCT.py +444 -440
  35. fiqus/post_processors/PostProcessConductorAC.py +997 -49
  36. fiqus/post_processors/PostProcessMultipole.py +19 -19
  37. fiqus/pre_processors/PreProcessCCT.py +175 -175
  38. fiqus/pro_material_functions/ironBHcurves.pro +246 -246
  39. fiqus/pro_templates/combined/CCT_template.pro +275 -274
  40. fiqus/pro_templates/combined/ConductorAC_template.pro +1474 -1025
  41. fiqus/pro_templates/combined/Multipole_template.pro +5 -5
  42. fiqus/utils/Utils.py +12 -7
  43. {fiqus-2025.2.0.dist-info → fiqus-2025.11.0.dist-info}/METADATA +65 -63
  44. fiqus-2025.11.0.dist-info/RECORD +86 -0
  45. {fiqus-2025.2.0.dist-info → fiqus-2025.11.0.dist-info}/WHEEL +1 -1
  46. tests/test_geometry_generators.py +4 -0
  47. tests/test_mesh_generators.py +5 -0
  48. tests/test_solvers.py +41 -4
  49. tests/utils/fiqus_test_classes.py +15 -6
  50. tests/utils/generate_reference_files_ConductorAC.py +57 -57
  51. tests/utils/helpers.py +97 -97
  52. fiqus-2025.2.0.dist-info/RECORD +0 -85
  53. {fiqus-2025.2.0.dist-info → fiqus-2025.11.0.dist-info}/LICENSE.txt +0 -0
  54. {fiqus-2025.2.0.dist-info → fiqus-2025.11.0.dist-info}/top_level.txt +0 -0
@@ -1,1025 +1,1474 @@
1
- Group {
2
- // ------- PROBLEM DEFINITION -------
3
- // Filaments
4
- Filaments_SC = Region[{<<rm.powered.Filaments.vol.numbers|join(', ')>>}]; // Filament superconducting region
5
- Filament_holes = Region[{<<rm.powered.Filaments.surf_in.numbers|join(', ')>>}]; // Filament holes
6
-
7
- Filaments = Region[{Filaments_SC, Filament_holes}]; // Filaments (including holes)
8
-
9
-
10
- BndFilaments = Region[{<<rm.powered.Filaments.surf.numbers|join(', ')>>}]; // Filament boundaries
11
- Cuts = Region[{<<rm.powered.Filaments.cochain.numbers|join(', ')>>}]; // Cuts on filament boundaries
12
-
13
- // Define the filaments, boundaries and cuts according to their layer and index in the layer
14
- {% if len(rm.powered.Filaments.vol.numbers) % 6 == 0 %} // There is no inner layer (of length 1)
15
- {% for layer in range( int(len(rm.powered.Filaments.vol.numbers)/6 )) %}
16
- {% for filament_index in range(6) %}
17
- filament_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.vol.numbers[layer*6 + filament_index]>>}]; // Filament surface
18
- filamentBnd_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.surf.numbers[layer*6 + filament_index]>>}]; // Boundary
19
- Cut_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.cochain.numbers[layer*6 + filament_index]>>}]; // Cut
20
- {%endfor%}
21
- {%endfor%}
22
-
23
- {% elif len(rm.powered.Filaments.vol.numbers) % 6 == 1 %} // There is an inner layer (of length 1)
24
- // Define the inner point
25
- filament_0_0 = Region[{<<rm.powered.Filaments.vol.numbers[0]>>}];
26
- filamentBnd_0_0 = Region[{<<rm.powered.Filaments.surf.numbers[0]>>}];
27
- Cut_0_0 = Region[{<<rm.powered.Filaments.cochain.numbers[0]>>}];
28
-
29
- {% for layer in range(0, int((len(rm.powered.Filaments.vol.numbers)-1) /6 ) ) %}
30
- {% for filament_index in range(6) %}
31
- filament_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.vol.numbers[layer*6 + filament_index + 1]>>}];
32
- filamentBnd_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.surf.numbers[layer*6 + filament_index + 1]>>}];
33
- Cut_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.cochain.numbers[layer*6 + filament_index + 1]>>}];
34
- {%endfor%}
35
- {%endfor%}
36
-
37
- {% endif %}
38
-
39
- // To assign different material properties to each filament hole, we need to assign them separately.
40
- {% for i, hole in enumerate(rm.powered.Filaments.surf_in.numbers) %}
41
- FilamentHole_<<i>> = Region[{<<hole>>}];
42
- {%endfor%}
43
-
44
-
45
-
46
- // Matrix partitions
47
- {% for i, matrix_partition in enumerate(rm.induced.Matrix.vol.numbers)%}
48
- Matrix_<<i>> = Region[{<<matrix_partition>>}];
49
- {%endfor%}
50
-
51
- // Matrix
52
- Matrix = Region[{<<rm.induced.Matrix.vol.numbers|join(', ') >>}];
53
- BndMatrix = Region[ <<rm.induced.Matrix.surf_out.numbers[0]>> ]; // Strand outer boundary
54
- BndMatrixCut = Region[ <<rm.induced.Matrix.cochain.numbers[0]>> ]; // Strand outer boundary cut
55
- Cuts += Region[ BndMatrixCut ]; // important to add the matrix cut to the region of the cuts
56
-
57
- // Air
58
- Air = Region[ <<rm.air.vol.number>> ]; // Air surface
59
- BndAir = Region[ <<rm.air.surf.number>> ]; // Air outer boundary
60
-
61
- // Define a region for the matrix partitions to be included in the TI (in-plane) problem
62
- TI_adjacent_region = Region[ {Air} ]; // Define the regions adjacent to the region on which the TI problem is solved (used for defining h_perp_space_dynamic)
63
- {% if dm.magnet.geometry.io_settings.load.load_from_yaml %}
64
- Matrix_partitions_excluded_from_TI = Region[{<<mp.Surfaces_excluded_from_TI|join(', ')>>}]; // Matrix partitions excluded from the IP problem
65
-
66
- Matrix_partitions_for_TI = Region[ {Matrix} ];
67
- Matrix_partitions_for_TI -= Region[ {Matrix_partitions_excluded_from_TI} ]; // Matrix partitions included in the IP problem
68
-
69
- TI_adjacent_region += Region[ {Matrix_partitions_excluded_from_TI} ]; // Define the regions adjacent to the region on which the TI problem is solved (used for defining h_perp_space_dynamic)
70
- {% else %}
71
- Matrix_partitions_for_TI = Region[ {Matrix} ];
72
- {% endif %}
73
-
74
-
75
- // Split into conducting and non-conducting domains
76
- LinOmegaC = Region[ {Matrix, Filament_holes} ];
77
- NonLinOmegaC = Region[ {Filaments_SC} ];
78
- OmegaC = Region[ {LinOmegaC, NonLinOmegaC} ];
79
- BndOmegaC = Region[ {BndMatrix, BndFilaments} ];
80
- OmegaCC = Region[ {Air} ];
81
- Omega = Region[ {OmegaC, OmegaCC} ]; // the whole domain (only surfaces in 2D, or volumes in 3D)
82
-
83
- OmegaC_AndBnd = Region[{OmegaC, BndOmegaC}]; // useful for function space definition
84
- OmegaCC_AndBnd = Region[{OmegaCC, BndOmegaC, BndAir}]; // useful for function space definition
85
-
86
- // Here we define points on the boundaries of the filaments and the outer matrix boundary.
87
- // These points are used to fix the magnetic potential to zero on the boundaries.
88
- MatrixPointOnBoundary = Region[{<<rm.air.point.numbers[0]>>}];
89
- FilamentPointsOnBoundaries = Region[{<<rm.powered.Filaments.curve.numbers|join(', ')>>}];
90
- ArbitraryPoints = Region[{MatrixPointOnBoundary, FilamentPointsOnBoundaries}];
91
-
92
- }
93
-
94
- Function{
95
- // ------- GEOMETRY PARAMETERS -------
96
- {% if len(rm.powered.Filaments.vol.numbers) % 6 == 0 %}
97
- number_of_layers = <<len(rm.powered.Filaments.vol.numbers)>>/6;
98
- {% elif len(rm.powered.Filaments.vol.numbers) % 6 == 1 %}
99
- number_of_layers = (<<len(rm.powered.Filaments.vol.numbers)>>-1) / 6;
100
- {% endif %}
101
- // ------- MATERIAL PARAMETERS -------
102
- temperature = <<dm.magnet.solve.general_parameters.temperature>>;
103
- T[] = temperature; // this can be made a function of time if needed. Later on, T may also be a field we solve for.
104
- RRR_matrix = <<dm.conductors[dm.magnet.solve.conductor_name].strand.RRR>>;
105
-
106
- mu0 = Pi*4e-7; // [H/m]
107
- nu0 = 1.0/mu0; // [m/H]
108
- mu[Omega] = mu0;
109
- nu[Omega] = nu0;
110
- // Copper
111
- {% if isinstance(dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer, float) %} // If the matrix has constant resistivity we can assign it directly
112
- rho[Matrix] = <<dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer>>;
113
- {% elif dm.magnet.geometry.io_settings.load.load_from_yaml %}
114
- // If we load the geometry from a YAML file, we also have material properties assigned in a MaterialProperties (mp) data structure.
115
- {% for i, matrix_partition_material in zip(range(len(rm.induced.Matrix.vol.numbers)), rm.induced.Matrix.vol.names)%}
116
- {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu_T' %}
117
- rho[Matrix_<<i>>] = CFUN_rhoCu_T[T[]]{0, << mp.Materials[matrix_partition_material].RRR >>};
118
- {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu' %}
119
- rho[Matrix_<<i>>] = CFUN_rhoCu_T_B[T[], $1]{<< mp.Materials[matrix_partition_material].RRR >>};
120
- {% endif %}
121
- {%endfor%}
122
- {% else %} // If we don't load the geometry from a YAML file, we can assign the material properties directly
123
- {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu_T' %}
124
- rho[Matrix] = CFUN_rhoCu_T[T[]]{0, RRR_matrix};
125
- {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu' %}
126
- rho[Matrix] = CFUN_rhoCu_T_B[T[], $1]{RRR_matrix};
127
- {% endif %}
128
- {% endif %}
129
-
130
- // Superconducting filaments (nonlinear now)
131
- ec = <<dm.conductors[dm.magnet.solve.conductor_name].strand.ec_superconductor>>; // [V/m], the value 1e-4 V/m is a common convention
132
-
133
- {% if dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Ic_A_NbTi' and dm.conductors[dm.magnet.solve.conductor_name].strand.material_superconductor == 'NbTi' %}
134
- jc[] = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Jc_5T_4_2K>> / 1.70732393e9 * CFUN_IcNbTi_T_B_a[T[], $1, 1]; // [A/m2] critical current density as function of temperature and field amplitude
135
- {% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Ic_A_NbTi' and dm.conductors[dm.magnet.solve.conductor_name].strand.material_superconductor == 'Nb3Sn' %}
136
- jc[] = CFUN_IcNb3Sn_T_B_a[T[], $1, 1]; // [A/m2] critical current density as function of temperature and field amplitude
137
- {% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Bordini' %}
138
- jc[] = 2.25*CFUN_Jc_Bordini_T_B[T[], $1]{60400/1.056e-6, 16.47, 30.77, 1.025}; // [A/m2], parameters from DOI: 10.1109/TASC.2022.3167655 (see conclusion)
139
- {% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Constant Jc' %}
140
- jc[] = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Jc_constant>>; // [A/m2] constant critical current density
141
- {% endif %}
142
-
143
-
144
- n = <<dm.conductors[dm.magnet.solve.conductor_name].strand.n_value_superconductor>>; // [-] power law index, one key parameter for the power law
145
-
146
- rho_power[] = ec / jc[$2] * (Norm[$1] / jc[$2])^(n - 1); // [Ohm m] power law resistivity
147
- dedj_power[] = (
148
- ec / jc[$2] * (Norm[$1]/jc[$2])^(n - 1) * TensorDiag[1, 1, 1] +
149
- ec / jc[$2]^3 * (n - 1) * (Norm[$1]/jc[$2])^(n - 3) * SquDyadicProduct[$1]);
150
-
151
- outer_product[] = Tensor[CompX[$1]*CompX[$2], CompX[$1]*CompY[$2], CompX[$1]*CompZ[$2], CompY[$1]*CompX[$2], CompY[$1]*CompY[$2], CompY[$1]*CompZ[$2], CompZ[$1]*CompX[$2], CompZ[$1]*CompY[$2], CompZ[$1]*CompZ[$2]];
152
- dedb_power[] = -n*ec/jc[Norm[$2]]^2 * (Norm[$1]/jc[Norm[$2]])^(n-1) * ( jc[Norm[$2] + 0.005] - jc[Max[0, Norm[$2] - 0.005]] ) / (0.01) * outer_product[$1, $2]/(Norm[$2]+1e-10);
153
-
154
-
155
- {% if dm.magnet.solve.general_parameters.superconductor_linear %}
156
- mult_copper_like = 1e-5;
157
- rho[Filaments_SC] = mult_copper_like * 1.81e-10; // <<dm.magnet.solve.material_properties.matrix.resistivity.constant>>;
158
- dedj[Filaments_SC] = mult_copper_like * 1.81e-10; //<<dm.magnet.solve.material_properties.matrix.resistivity.constant>>;
159
- {% else %}
160
- rho[Filaments_SC] = rho_power[$1, $2];
161
- dedj[Filaments_SC] = dedj_power[$1, $2];
162
- {% endif %}
163
- dedb[Filaments_SC] = dedb_power[$1, $2];
164
-
165
- {% if dm.magnet.geometry.io_settings.load.load_from_yaml %}
166
- // We assign material properties to each filament hole. Currently we just assign the same properties as the matrix, but with a different RRR.
167
- // This must be changed to actually use the correct material property read from the geometry YAML.
168
- {% for i, hole_material in zip(range(len(rm.powered.Filaments.surf_in.numbers)), rm.powered.Filaments.surf_in.names)%}
169
- {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu_T' %}
170
- rho[FilamentHole_<<i>>] = CFUN_rhoCu_T[T[]]{0, << mp.Materials[hole_material].RRR >>};
171
- {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu' %}
172
- rho[FilamentHole_<<i>>] = CFUN_rhoCu_T_B[T[], $1]{<< mp.Materials[hole_material].RRR >>};
173
- {% elif isinstance(dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer, float) %} // If the matrix has constant resistivity we can assign it directly
174
- rho[FilamentHole_<<i>>] = <<dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer>>;
175
- {% endif %}
176
- {% endfor %}
177
- {% endif %}
178
-
179
- sigma[] = 1/rho[$1] ; // Can only be used in the matrix
180
-
181
- // HEAT APPROXIMATION
182
- {% if dm.conductors[dm.magnet.solve.conductor_name].strand.Cv_material_superconductor == 'CFUN_CvNbTi' %}
183
- filament_Cv[] = CFUN_CvNbTi_T_B[$1, $2]{0, 1, 0}; // Volumetric heat capacity [J/(m3 K)], as function of temperature and field magnitude.
184
- {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.Cv_material_superconductor == 'CFUN_CvNb3Sn' %}
185
- filament_Cv[] = CFUN_CvNb3Sn_T_B[$1, $2]; // Volumetric heat capacity [J/(m3 K)], as function of temperature and field magnitude.
186
- {% endif %}
187
-
188
- {% if dm.conductors[dm.magnet.solve.conductor_name].strand.Cv_material_stabilizer == 'CFUN_CvCu' %}
189
- matrix_Cv[] = CFUN_CvCu_T[$1]; // Volumetric heat capacity [J/(m3 K)], as function of temperature
190
- {% endif %}
191
-
192
-
193
- // ------- SOURCE PARAMETERS -------
194
- bmax = <<dm.magnet.solve.source_parameters.sine.field_amplitude>>; // Maximum applied magnetic induction [T]
195
- f = <<dm.magnet.solve.source_parameters.sine.frequency>>; // Frequency of applied field [Hz]
196
- // Direction and value of applied field
197
- {% if dm.magnet.solve.source_parameters.source_type != 'sine' %}
198
- directionApplied[] = Vector[0., 1., 0.];
199
- {% endif %}
200
-
201
- {% if dm.magnet.solve.source_parameters.source_type == 'sine' %} // Sine wave source (potentially with DC component)
202
- time_multiplier = 1;
203
-
204
- I_transport[] = <<dm.magnet.solve.source_parameters.sine.superimposed_DC.current_magnitude>> + <<dm.magnet.solve.source_parameters.sine.current_amplitude>> * Sin[2*Pi*f * $Time];
205
-
206
- constant_field_direction[] = Vector[0., 1., 0.];
207
- directionApplied[] = Vector[Cos[<<dm.magnet.solve.source_parameters.sine.field_angle>>*Pi/180], Sin[<<dm.magnet.solve.source_parameters.sine.field_angle>>*Pi/180], 0.];
208
-
209
- hsVal[] = nu0 * <<dm.magnet.solve.source_parameters.sine.superimposed_DC.field_magnitude>> * constant_field_direction[] + nu0 * <<dm.magnet.solve.source_parameters.sine.field_amplitude>> * Sin[2*Pi*f * $Time] * directionApplied[];
210
- hsVal_prev[] = nu0 * <<dm.magnet.solve.source_parameters.sine.superimposed_DC.field_magnitude>> * constant_field_direction[] + nu0 * <<dm.magnet.solve.source_parameters.sine.field_amplitude>> * Sin[2*Pi*f * ($Time-$DTime)] * directionApplied[];
211
-
212
- {% elif dm.magnet.solve.source_parameters.source_type == 'piecewise' %}
213
- time_multiplier = <<dm.magnet.solve.source_parameters.piecewise.time_multiplier>>;
214
- applied_field_multiplier = <<dm.magnet.solve.source_parameters.piecewise.applied_field_multiplier>>;
215
- transport_current_multiplier = <<dm.magnet.solve.source_parameters.piecewise.transport_current_multiplier>>;
216
-
217
- {% if dm.magnet.solve.source_parameters.piecewise.source_csv_file %} // Source from CSV file
218
- timeList() = {<<ed['time']|join(', ')>>};
219
- valueList() = {<<ed['value']|join(', ')>>};
220
- timeValuesList() = ListAlt[timeList(), valueList()];
221
-
222
- hsVal[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{List[timeValuesList()]} * directionApplied[];
223
- hsVal_prev[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time-$DTime)/time_multiplier]]{List[timeValuesList()]} * directionApplied[];
224
- I_transport[] = transport_current_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{List[timeValuesList()]};
225
-
226
- {% else %} // Source from parameters
227
- times_source_piecewise_linear() = {<<dm.magnet.solve.source_parameters.piecewise.times|join(', ')>>};
228
- transport_currents_relative_piecewise_linear() = {<<dm.magnet.solve.source_parameters.piecewise.transport_currents_relative|join(', ')>>};
229
- applied_fields_relative_piecewise_linear() = {<<dm.magnet.solve.source_parameters.piecewise.applied_fields_relative|join(', ')>>};
230
-
231
- hsVal[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{ListAlt[times_source_piecewise_linear(), applied_fields_relative_piecewise_linear()]} * directionApplied[];
232
- hsVal_prev[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time-$DTime)/time_multiplier]]{ListAlt[times_source_piecewise_linear(), applied_fields_relative_piecewise_linear()]} * directionApplied[];
233
- I_transport[] = transport_current_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{ListAlt[times_source_piecewise_linear(), transport_currents_relative_piecewise_linear()]};
234
- {% endif %}
235
- {% endif %}
236
-
237
- // For the natural boundary condition (restricted to fields of constant direction for the moment, should be generalized)
238
- dbsdt[] = mu0 * (hsVal[] - hsVal_prev[]) / $DTime; // must be a finite difference to avoid error accumulation
239
-
240
- // ------- NUMERICAL PARAMETERS -------
241
- timeStart = 0.; // Initial time [s]
242
-
243
- {% if dm.magnet.solve.source_parameters.source_type == 'sine'%}
244
- timeFinal = <<dm.magnet.solve.numerical_parameters.sine.number_of_periods_to_simulate>>/f; // Final time for source definition (s)
245
- dt = 1 / (f*<<dm.magnet.solve.numerical_parameters.sine.timesteps_per_period>>); // Time step (initial if adaptive) (s)
246
- dt_max = dt; // Fixed maximum time step
247
- dt_max_var[] = dt_max;
248
- {% else %}
249
- timeFinal = <<dm.magnet.solve.numerical_parameters.piecewise.time_to_simulate>>;
250
-
251
- {% if dm.magnet.solve.numerical_parameters.piecewise.variable_max_timestep %}
252
- times_max_timestep_piecewise_linear() = {<<dm.magnet.solve.numerical_parameters.piecewise.times_max_timestep_piecewise_linear|join(', ')>>};
253
- max_timestep_piecewise_linear() = {<<dm.magnet.solve.numerical_parameters.piecewise.max_timestep_piecewise_linear|join(', ')>>};
254
- dt = max_timestep_piecewise_linear(0);
255
- dt_max_var[] = InterpolationLinear[Max[0,$Time]]{ListAlt[times_max_timestep_piecewise_linear(), max_timestep_piecewise_linear()]};
256
- {% else %}
257
- dt = timeFinal / <<dm.magnet.solve.numerical_parameters.piecewise.timesteps_per_time_to_simulate>>;
258
- dt_max = dt; // Fixed maximum time step
259
- dt_max_var[] = dt_max;
260
- {% endif %}
261
-
262
- {% if dm.magnet.solve.numerical_parameters.piecewise.force_stepping_at_times_piecewise_linear%}
263
- control_time_instants_list() = {<<dm.magnet.solve.source_parameters.piecewise.times|join(', ')>>, 1e99}; // last one is just to avoid 'seg. fault' errors
264
- {% endif %}
265
-
266
- {% endif %}
267
-
268
- // Set correction factor based on the periodicity length
269
- {% if dm.magnet.solve.formulation_parameters.two_ell_periodicity %}
270
- correctionFactor = 0.827; // to be automatized (equal to sin(x)/x with x = pi*ell/p, with ell = p/6 in the hexagonal lattice case)
271
- ell = 2*correctionFactor * <<dm.conductors[dm.magnet.solve.conductor_name].strand.fil_twist_pitch>> / 6;
272
- {% else %}
273
- correctionFactor = 0.9549;
274
- ell = correctionFactor * <<dm.conductors[dm.magnet.solve.conductor_name].strand.fil_twist_pitch>> / 6;
275
- {% endif %}
276
-
277
- iter_max = 60; // Maximum number of iterations (after which we exit the iterative loop)
278
- extrapolationOrder = 1; // Extrapolation order for predictor
279
- tol_energy = 1e-6; // Tolerance on the relative change of the power indicator
280
- writeInterval = dt; // Time interval to save the solution [s]
281
-
282
- // ------- SIMULATION NAME -------
283
- name = "test_temporary";
284
- resDirectory = StrCat["./",name];
285
- infoResidualFile = StrCat[resDirectory,"/residual.txt"];
286
- outputPower = StrCat[resDirectory,"/power.txt"]; // File updated during runtime
287
- crashReportFile = StrCat[resDirectory,"/crash_report.txt"];
288
- outputTemperature = StrCat[resDirectory,"/temperature.txt"];
289
-
290
- {% if dm.magnet.solve.initial_conditions.init_from_pos_file %}
291
- 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[]
292
- {% endif %}
293
- }
294
-
295
- Constraint {
296
- { Name phi ;
297
- Case {
298
- {% if dm.magnet.solve.source_parameters.boundary_condition_type == 'Natural' %} // For natural boundary condition (in formulation)
299
- {Region ArbitraryPoints ; Value 0.0 ;} // Fix the magnetic potential to zero on the boundaries of the filaments and the outer matrix boundary
300
- {% elif dm.magnet.solve.source_parameters.boundary_condition_type == 'Essential' %}
301
- {Region BndAir ; Type Assign ; Value XYZ[]*directionApplied[] ; TimeFunction hsVal[] * directionApplied[] ;} // Essential boundary condition (not compatible with transport current)
302
- {% endif %}
303
- {% if dm.magnet.solve.initial_conditions.init_from_pos_file %}
304
- {Region Omega ; Type InitFromResolution ; NameOfResolution Projection_h_to_h ;}
305
- {% endif %}
306
- }
307
- }
308
- { Name Current ;
309
- Case {
310
- {Region BndMatrixCut ; Type Assign ; Value 1.0 ; TimeFunction I_transport[] ;} // Contraint for the total transport current
311
- {% if dm.magnet.solve.initial_conditions.init_from_pos_file %}
312
- {Region Cuts ; Type InitFromResolution ; NameOfResolution Projection_h_to_h ;}
313
- {% endif %}
314
- // {Region Cuts ; Type Assign ; Value 0. ;} // for debugging (or to model fully uncoupled filaments without transport current)
315
- }
316
- }
317
- { Name Voltage ; Case {} } // Empty to avoid warnings
318
- { Name Current_plane ; Case {} } // Empty to avoid warnings
319
- { Name Voltage_plane ;
320
- Case {
321
- // The constraint below can be useful for debugging (together with the Current one on Cuts, they uncouple the OOP and IP problems in the linked-flux formulation)
322
- //{Region filamentBnd_1_1 ; Type Assign ; Value 1. ;} // Put just one to non-zero voltage to see a non-trivial solution
323
- //{Region BndFilaments ; Type Assign ; Value 0. ;} // All the other ones are forced to be zero
324
- }
325
- }
326
- { Name v_plane ;
327
- Case {
328
- // {Region filamentBnd_1_1 ; Type Assign ; Value 1. ;}
329
- // {Region BndFilaments ; Type Assign ; Value 0. ;}
330
- {Region MatrixPointOnBoundary ; Type Assign ; Value 0. ;}
331
- }
332
- }
333
-
334
- {% if dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
335
- // As only the curl of the field is projected, the absolute value is unknown. We need to symmetrize the field
336
- { Name hp_static ;
337
- Case {
338
- // {Region CenterPoint ; Value 0.0 ;} // replaced by a Lagrange multiplier in the formulation (more general, as there might be a filament at the center!)
339
- }
340
- }
341
- {% endif %}
342
-
343
- // This is the key constraint for coupling global quantities: it contains the links between the filaments
344
- {Name ElectricalCircuit ; Type Network ;
345
- Case circuit {
346
- // A filament in the center is treated separately.
347
- {% if len(rm.powered.Filaments.vol.numbers) % 6 == 1 %}
348
- { Region Cut_0_0 ; Branch { 0, 0 } ; }
349
- { Region filamentBnd_0_0 ; Branch {1000, 0} ; }
350
- {% endif %}
351
-
352
- {% if dm.magnet.solve.formulation_parameters.two_ell_periodicity %}
353
- {% for layer in range(1, int( len(rm.powered.Filaments.vol.numbers)/6 ) + 1) %}
354
- {% for filament in range(1, 4) %}
355
- { Region Cut_<<layer>>_<<2*filament>> ; Branch { <<100*layer + 2*filament-1>>, <<100*layer + (2*filament+1)%6>> } ; }
356
- { Region filamentBnd_<<layer>>_<<2*filament-1>> ; Branch {1000, <<100*layer + 2*filament-1>>} ; }
357
- { Region Cut_<<layer>>_<<(2*filament+1)%6>> ; Branch { <<100*layer + 2*filament>>, <<100*layer + (2*filament+1)%6+1>> } ; }
358
- { Region filamentBnd_<<layer>>_<<2*filament>> ; Branch {1000, <<100*layer + 2*filament>>} ; }
359
- {%endfor%}
360
- {%endfor%}
361
-
362
- {% else %}
363
- {% for layer in range(1, int( len(rm.powered.Filaments.vol.numbers)/6 ) + 1) %}
364
- {% for filament in range(1, 7) %}
365
- { Region Cut_<<layer>>_<<filament>> ; Branch { <<100*layer + filament>>, <<100*layer + filament%6+1>> } ; }
366
- { Region filamentBnd_<<layer>>_<<filament>> ; Branch {1000, <<100*layer + filament>>} ; }
367
- {%endfor%}
368
- {%endfor%}
369
- {% endif %}
370
- }
371
- }
372
- { Name h ; Type Assign ;
373
- Case {
374
- {% if dm.magnet.solve.initial_conditions.init_from_pos_file %}
375
- {Region OmegaC ; Type InitFromResolution ; NameOfResolution Projection_h_to_h ;}
376
- {% endif %}
377
- }
378
- }
379
-
380
- }
381
-
382
- FunctionSpace {
383
- // Function space for magnetic field h in h-conform formulation. Main field for the magnetodynamic problem.
384
- // h = sum phi_n * grad(psi_n) (nodes in Omega_CC with boundary)
385
- // + sum h_e * psi_e (edges in Omega_C)
386
- // + sum I_i * c_i (cuts, global basis functions for net current intensity) not yet here
387
- { Name h_space; Type Form1;
388
- BasisFunction {
389
- { Name gradpsin; NameOfCoef phin; Function BF_GradNode;
390
- Support OmegaCC_AndBnd; Entity NodesOf[OmegaCC]; } // Extend support to boundary for surface integration (e.g. useful for weak B.C.)
391
- { Name gradpsin; NameOfCoef phin2; Function BF_GroupOfEdges;
392
- Support OmegaC; Entity GroupsOfEdgesOnNodesOf[BndOmegaC]; } // To treat properly the Omega_CC-Omega_C boundary
393
- { Name psie; NameOfCoef he; Function BF_Edge;
394
- Support OmegaC_AndBnd; Entity EdgesOf[All, Not BndOmegaC]; }
395
- { Name sc; NameOfCoef Ii; Function BF_GroupOfEdges;
396
- Support Omega; Entity GroupsOfEdgesOf[Cuts]; } // The region Cuts contains the union of all the relevant cuts (cohomology basis function support)
397
- }
398
- GlobalQuantity {
399
- { Name I ; Type AliasOf ; NameOfCoef Ii ; }
400
- { Name V ; Type AssociatedWith ; NameOfCoef Ii ; }
401
- }
402
-
403
- Constraint {
404
- { NameOfCoef he; EntityType EdgesOf; NameOfConstraint h; }
405
- { NameOfCoef phin; EntityType NodesOf; NameOfConstraint phi; }
406
- { NameOfCoef phin2; EntityType NodesOf; NameOfConstraint phi; }
407
- { NameOfCoef Ii ;
408
- EntityType GroupsOfEdgesOf ; NameOfConstraint Current ; }
409
- { NameOfCoef V ;
410
- EntityType GroupsOfNodesOf ; NameOfConstraint Voltage ; }
411
- }
412
- }
413
- // Function space for the in-plane voltage field. Main field for the electrokinetics problem.
414
- // The (in-plane) coupling current derive from this voltage j_coupling = - sigma * grad(v).
415
- { Name v_space_elKin ; Type Form0 ;
416
- BasisFunction {
417
- { Name vn ; NameOfCoef vn ; Function BF_Node ;
418
- Support Matrix_partitions_for_TI ; Entity NodesOf[All, Not BndFilaments] ; }
419
- { Name vi; NameOfCoef vi; Function BF_GroupOfNodes;
420
- Support Matrix_partitions_for_TI; Entity GroupsOfNodesOf[BndFilaments]; }
421
- }
422
- GlobalQuantity {
423
- { Name V ; Type AliasOf ; NameOfCoef vi ; }
424
- { Name I ; Type AssociatedWith ; NameOfCoef vi ; }
425
- }
426
- Constraint {
427
- { NameOfCoef vn ; EntityType NodesOf ; NameOfConstraint v_plane ; }
428
- { NameOfCoef V ;
429
- EntityType Region ; NameOfConstraint Voltage_plane ; }
430
- { NameOfCoef I ;
431
- EntityType Region ; NameOfConstraint Current_plane ; }
432
- }
433
- }
434
- {% if dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
435
- // The curl of this space is the projection of the coupling current on this subspace (no net current from fil.)
436
- { Name h_perp_space_static; Type Form1P;
437
- BasisFunction {
438
- { Name sn; NameOfCoef hn; Function BF_PerpendicularEdge;
439
- Support Matrix_partitions_for_TI; Entity NodesOf[All]; }
440
- }
441
- Constraint {
442
- { NameOfCoef hn; EntityType NodesOf; NameOfConstraint hp_static; }
443
- }
444
- }
445
- // This is a Lagrange multplier for forcing the integral of hp_static to be zero in the matrix (avg field = 0)
446
- { Name h_perp_space_static_lagrange; Type Vector;
447
- BasisFunction {
448
- { Name sn_lag; NameOfCoef hn_lag; Function BF_RegionZ;
449
- Support Matrix_partitions_for_TI; Entity Matrix_partitions_for_TI; } // Ideally, should be only one function (not one per matrix part). To be double-checked!
450
- }
451
- }
452
- // This space will correct the static space above with dynamic effects
453
- { Name h_perp_space_dynamic; Type Form1P;
454
- BasisFunction {
455
- { Name sn; NameOfCoef hn; Function BF_PerpendicularEdge;
456
- Support Matrix_partitions_for_TI; Entity NodesOf[All, Not TI_adjacent_region ]; }
457
- }
458
- }
459
- {% endif %}
460
- }
461
-
462
- Jacobian {
463
- { Name Vol ;
464
- Case {
465
- {Region All ; Jacobian Vol ;}
466
- }
467
- }
468
- { Name Sur ;
469
- Case {
470
- { Region All ; Jacobian Sur ; }
471
- }
472
- }
473
- }
474
-
475
- Integration {
476
- { Name Int ;
477
- Case {
478
- { Type Gauss ;
479
- Case {
480
- { GeoElement Point ; NumberOfPoints 1 ; }
481
- { GeoElement Line ; NumberOfPoints 3 ; }
482
- { GeoElement Triangle ; NumberOfPoints 3 ; }
483
- }
484
- }
485
- }
486
- }
487
- }
488
-
489
- Formulation{
490
- // h-formulation
491
- { Name MagDyn_hphi; Type FemEquation;
492
- Quantity {
493
- // Functions for the out-of-plane (OOP) problem
494
- { Name h; Type Local; NameOfSpace h_space; }
495
- { Name hp; Type Local; NameOfSpace h_space; }
496
- { Name I; Type Global; NameOfSpace h_space[I]; }
497
- { Name V; Type Global; NameOfSpace h_space[V]; }
498
- // Functions for the in-plane (IP) problem
499
- { Name v; Type Local; NameOfSpace v_space_elKin; }
500
- { Name Vp; Type Global; NameOfSpace v_space_elKin[V]; }
501
- { Name Ip; Type Global; NameOfSpace v_space_elKin[I]; }
502
- }
503
- Equation {
504
- // --- OOP problem ---
505
- // Time derivative of b (NonMagnDomain)
506
- Galerkin { [ ell* mu[] * Dof{h} / $DTime , {h} ];
507
- In Omega; Integration Int; Jacobian Vol; }
508
- Galerkin { [ - ell*mu[] * {h}[1] / $DTime , {h} ];
509
- In Omega; Integration Int; Jacobian Vol; }
510
- // Induced current (linear OmegaC)
511
- Galerkin { [ ell*rho[mu0*Norm[{h}]] * Dof{d h} , {d h} ];
512
- In LinOmegaC; Integration Int; Jacobian Vol; }
513
- // Induced current (non-linear OmegaC)
514
- Galerkin { [ ell*rho[{d h}, mu0*Norm[{h}]] * {d h} , {d h} ];
515
- In NonLinOmegaC; Integration Int; Jacobian Vol; }
516
-
517
- Galerkin { [ ell*dedj[{d h}, mu0*Norm[{h}]] * Dof{d h} , {d h} ];
518
- In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
519
- Galerkin { [ - ell*dedj[{d h}, mu0*Norm[{h}]] * {d h} , {d h} ];
520
- In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
521
-
522
- {% if dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type != 'Constant Jc' and dm.magnet.solve.general_parameters.superconductor_linear == False%}
523
- Galerkin { [ ell*dedb[{d h}, mu0*{h}] * mu0*Dof{h} , {d hp} ];
524
- In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
525
- Galerkin { [ -ell*dedb[{d h}, mu0*{h}] * mu0*{h} , {d hp} ];
526
- In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
527
- {% endif %}
528
-
529
- // Natural boundary condition for normal flux density (useful when transport current is an essential condition)
530
- {% if dm.magnet.solve.source_parameters.boundary_condition_type == 'Natural' %}
531
- Galerkin { [ - ell*dbsdt[] * Normal[] , {dInv h} ];
532
- In BndAir; Integration Int; Jacobian Sur; }
533
- {% endif %}
534
- // Global term
535
- GlobalTerm { [ Dof{V} , {I} ] ; In Cuts ; }
536
-
537
- // --- IP problem ---
538
- Galerkin { [ ell * sigma[mu0*Norm[{h}]] * Dof{d v} , {d v} ];
539
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; } // Matrix
540
- GlobalTerm { [ Dof{Ip} , {Vp} ] ; In BndFilaments ; }
541
-
542
- // --- Coupling between OOP and IP problems via circuit equations ---
543
- GlobalEquation {
544
- Type Network ; NameOfConstraint ElectricalCircuit ;
545
- { Node {I}; Loop {V}; Equation {V}; In Cuts ; }
546
- { Node {Ip}; Loop {Vp}; Equation {Ip}; In BndFilaments ; }
547
- }
548
- }
549
- }
550
- {% if dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
551
- // h-formulation
552
- { Name MagDyn_hphi_dynCorr; Type FemEquation;
553
- Quantity {
554
- // Functions for the OOP and IP problems, that are just used here (and not solved for)
555
- { Name h; Type Local; NameOfSpace h_space; }
556
- { Name v; Type Local; NameOfSpace v_space_elKin; }
557
- // Functions for the dynamic correction of the IP problem
558
- { Name hp_static; Type Local; NameOfSpace h_perp_space_static; }
559
- { Name hp_static_lagrange; Type Local; NameOfSpace h_perp_space_static_lagrange; }
560
- { Name hp_dynamic; Type Local; NameOfSpace h_perp_space_dynamic; }
561
- }
562
- Equation {
563
- // --- Dynamic correction of the IP problem ---
564
- // Projection of the static current flow on the curl of a "static" magnetic field
565
- Galerkin { [ sigma[mu0*Norm[{h}]] * {d v} , {d hp_static} ]; // No DOF! Just take the solution of the previously solved v-based formulation
566
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
567
- Galerkin { [ Dof{d hp_static} , {d hp_static} ];
568
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
569
- Galerkin { [ Dof{hp_static_lagrange} , {hp_static} ];
570
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
571
- Galerkin { [ Dof{hp_static} , {hp_static_lagrange} ];
572
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; } // ensure the magnetic field is averaged to zero over the matrix
573
- // Introduce a dynamic component to the magnetic field
574
- Galerkin { [ mu[] * Dof{hp_static} / $DTime , {hp_dynamic} ];
575
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
576
- Galerkin { [ mu[] * Dof{hp_dynamic} / $DTime , {hp_dynamic} ];
577
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
578
- Galerkin { [ - mu[] * {hp_static}[1] / $DTime , {hp_dynamic} ];
579
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
580
- Galerkin { [ - mu[] * {hp_dynamic}[1] / $DTime , {hp_dynamic} ];
581
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
582
- //Galerkin { [ - Dof{d v} , {d hp_dynamic} ];
583
- // In Matrix; Integration Int; Jacobian Vol; } // Static field is curl-free so this contribution is unnecessary!
584
- Galerkin { [ rho[mu0*Norm[{h}]] * Dof{d hp_dynamic} , {d hp_dynamic} ];
585
- In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; } // Only the dynamic correction is an eddy current
586
- }
587
- }
588
- {% endif %}
589
- {% if dm.magnet.solve.initial_conditions.init_from_pos_file %}
590
- // Projection formulation for initial condition
591
- { Name Projection_h_to_h; Type FemEquation;
592
- Quantity {
593
- { Name h; Type Local; NameOfSpace h_space; }
594
- }
595
- Equation{
596
- // 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).
597
- // Validity of this to be checked again if we go to different meshes between the initial condition and the following simulation
598
- Galerkin { [ Dof{h}, {h} ] ;
599
- In Omega ; Jacobian Vol ; Integration Int ; }
600
- Galerkin { [ - h_from_file[], {h} ] ;
601
- In Omega ; Jacobian Vol ; Integration Int ; }
602
- }
603
- }
604
- {% endif %}
605
- }
606
-
607
- Macro CustomIterativeLoop
608
- // Compute first solution guess and residual at step $TimeStep
609
- Generate[A];
610
- Solve[A]; Evaluate[ $syscount = $syscount + 1 ];
611
- Generate[A]; GetResidual[A, $res0];
612
- Evaluate[ $res = $res0 ];
613
- Evaluate[ $iter = 0 ];
614
- Evaluate[ $convCrit = 1e99 ];
615
- PostOperation[MagDyn_energy];
616
- Print[{$iter, $res, $res / $res0, $indicFilamentLoss},
617
- Format "%g %14.12e %14.12e %14.12e", File infoResidualFile];
618
- // ----- Enter the iterative loop (hand-made) -----
619
- While[$convCrit > 1 && $res / $res0 <= 1e10 && $iter < iter_max]{
620
- Solve[A]; Evaluate[ $syscount = $syscount + 1 ];
621
- Generate[A]; GetResidual[A, $res];
622
- Evaluate[ $iter = $iter + 1 ];
623
- Evaluate[ $indicFilamentLossOld = $indicFilamentLoss];
624
- PostOperation[MagDyn_energy];
625
- Print[{$iter, $res, $res / $res0, $indicFilamentLoss},
626
- Format "%g %14.12e %14.12e %14.12e", File infoResidualFile];
627
- // Evaluate the convergence indicator
628
- Evaluate[ $relChangeACLoss = Abs[($indicFilamentLossOld - $indicFilamentLoss)/((Abs[$indicFilamentLossOld]>1e-7 || $iter < 10) ? $indicFilamentLossOld:1e-7)] ];
629
- Evaluate[ $convCrit = $relChangeACLoss/tol_energy];
630
- }
631
- Return
632
-
633
- Resolution {
634
- { Name MagDyn;
635
- System {
636
- {Name A; NameOfFormulation MagDyn_hphi;}
637
- {% if dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
638
- {Name A_dynCorr; NameOfFormulation MagDyn_hphi_dynCorr;}
639
- {% endif %}
640
- }
641
- Operation {
642
- // Initialize directories
643
- CreateDirectory[resDirectory];
644
- DeleteFile[outputPower];
645
- DeleteFile[infoResidualFile];
646
- // Initialize the solution (initial condition)
647
- SetTime[ timeStart ];
648
- SetDTime[ dt ];
649
- SetTimeStep[ 0 ];
650
- InitSolution[A];
651
- SaveSolution[A]; // Saves the solution x (from Ax = B) to .res file
652
- Evaluate[ $syscount = 0 ];
653
- Evaluate[ $saved = 1 ];
654
- Evaluate[ $elapsedCTI = 1 ]; // Number of control time instants already treated
655
- Evaluate[ $isCTI = 0 ];
656
-
657
- {% if dm.magnet.solve.formulation_parameters.compute_temperature %}
658
- Evaluate[ $cumulative_temperature = 0 ];
659
- Evaluate[ $dT_old = 0 ];
660
- {% endif %}
661
-
662
- {% if dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
663
- InitSolution[A_dynCorr];
664
- SaveSolution[A_dynCorr];
665
- {% else %}
666
- Evaluate[ $indicTotalLoss_dyn = 0 ]; // Put it to zero to avoid warnings
667
- Evaluate[ $indicCouplingLoss_dyn = 0 ]; // Put it to zero to avoid warnings
668
- {% endif %}
669
- // ----- Enter implicit Euler time integration loop (hand-made) -----
670
- // Avoid too close steps at the end. Stop the simulation if the step becomes ridiculously small
671
- SetExtrapolationOrder[ extrapolationOrder ];
672
- While[$Time < timeFinal] {
673
- SetTime[ $Time + $DTime ]; // Time instant at which we are looking for the solution
674
- SetTimeStep[ $TimeStep + 1 ];
675
- {% if dm.magnet.solve.numerical_parameters.piecewise.force_stepping_at_times_piecewise_linear and dm.magnet.solve.source_parameters.source_type == 'piecewise'%}
676
- // Make sure all CTI are exactly chosen
677
- Evaluate[ $isCTI = 0 ];
678
- Test[$Time >= time_multiplier*AtIndex[$elapsedCTI]{List[control_time_instants_list]} - 1e-7 ]{
679
- Evaluate[ $isCTI = 1, $prevDTime = $DTime ]; // Also save the previous time step to restart from it after the CTI
680
- SetDTime[ time_multiplier*AtIndex[$elapsedCTI]{List[control_time_instants_list]} - $Time + $DTime ];
681
- SetTime[ time_multiplier*AtIndex[$elapsedCTI]{List[control_time_instants_list]} ]; // To compute exactly at the asked time instant
682
- Print[{$Time}, Format "*** Control time instant: %g s."];
683
- }
684
- {% endif %}
685
- // Iterative loop defined as a macro above
686
- Print[{$Time, $DTime, $TimeStep}, Format "Start new time step. Time: %g s. Time step: %g s. Step: %g."];
687
- Call CustomIterativeLoop;
688
- // Has it converged? If yes, save solution and possibly increase the time step...
689
- Test[ $iter < iter_max && ($res / $res0 <= 1e10 || $res0 == 0)]{
690
- Print[{$Time, $DTime, $iter}, Format "Converged time %g s with time step %g s in %g iterations."];
691
- // Save the solution of few time steps (small correction to avoid bad rounding)
692
- // Test[ $Time >= $saved * writeInterval - 1e-7 || $Time + $DTime >= timeFinal]{
693
- Test[ 1 ]{
694
- // Test[ $Time >= $saved * $DTime - 1e-7 || $Time + $DTime >= timeFinal]{
695
- SaveSolution[A];
696
- // post
697
- PostOperation[test_Losses];
698
- {% if dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
699
- Generate[A_dynCorr]; Solve[A_dynCorr]; SaveSolution[A_dynCorr];
700
- PostOperation[test_Losses_dynCorr];
701
- {% endif %}
702
- Print[{$Time, $saved}, Format "Saved time %g s (saved solution number %g). Output power infos:"];
703
- Print[{$Time, $indicFilamentLoss, $indicCouplingLoss, $indicEddyLoss, $indicTotalLoss, $indicCouplingLoss_dyn, $indicTotalLoss_dyn},
704
- Format "%g %14.12e %14.12e %14.12e %14.12e %14.12e %14.12e", File outputPower];
705
-
706
- // Compute the temperature
707
- {% if dm.magnet.solve.formulation_parameters.compute_temperature == True %}
708
- PostOperation[heat_capacity];
709
- Evaluate[$dT = $DTime * $indicTotalLoss/$heat_capacity_per_unit_length];
710
- Evaluate[ $cumulative_temperature = $cumulative_temperature + ($dT_old + $dT)/2 ];
711
- Print[{$Time, $dT, $cumulative_temperature}, Format "%g %14.12e %14.12e", File outputTemperature];
712
- Evaluate[ $dT_old = $dT ];
713
- {% endif %}
714
-
715
- Evaluate[$saved = $saved + 1];
716
- }
717
- {% if dm.magnet.solve.numerical_parameters.piecewise.force_stepping_at_times_piecewise_linear and dm.magnet.solve.source_parameters.source_type == 'piecewise' %}
718
- // Consider the time step before the control time instant (if relevant) and increment $elapsedCTI
719
- Test[ $isCTI == 1 ]{
720
- Evaluate[ $elapsedCTI = $elapsedCTI + 1 ];
721
- SetDTime[ $prevDTime ];
722
- }
723
- {% endif %}
724
- // Increase the step if we converged sufficiently "fast" (and not a control time instant)
725
- Test[ $iter < iter_max / 4 && $DTime < dt_max_var[] && $isCTI == 0 ]{
726
- Evaluate[ $dt_new = Min[$DTime * 2, dt_max_var[]] ];
727
- Print[{$dt_new}, Format "*** Fast convergence: increasing time step to %g"];
728
- SetDTime[$dt_new];
729
- }
730
- Test[ $DTime > dt_max_var[]]{
731
- Evaluate[ $dt_new = dt_max_var[] ];
732
- Print[{$dt_new}, Format "*** Variable maximum time-stepping: reducing time step to %g"];
733
- SetDTime[$dt_new];
734
- }
735
- }
736
- // ...otherwise, reduce the time step and try again
737
- {
738
- Evaluate[ $dt_new = $DTime / 2 ];
739
- Print[{$iter, $dt_new},
740
- Format "*** Non convergence (iter %g): recomputing with reduced step %g"];
741
- RemoveLastSolution[A];
742
- SetTime[$Time - $DTime];
743
- SetTimeStep[$TimeStep - 1];
744
- SetDTime[$dt_new];
745
- // If it gets ridicoulously small, end the simulation, and report the information in crash file.
746
- Test[ $dt_new < dt_max/10000 ]{
747
- Print[{$iter, $dt_new, $Time},
748
- Format "*** Non convergence (iter %g): time step %g too small, stopping the simulation at time %g s.", File crashReportFile];
749
- // Print[A];
750
- Exit;
751
- }
752
- }
753
- } // ----- End time loop -----
754
- // Print information about the resolution and the nonlinear iterations
755
- Print[{$syscount}, Format "Total number of linear systems solved: %g"];
756
- }
757
- }
758
- {% if dm.magnet.solve.initial_conditions.init_from_pos_file %}
759
- { Name Projection_h_to_h;
760
- System {
761
- {Name Projection_h_to_h; NameOfFormulation Projection_h_to_h; DestinationSystem A ;}
762
- }
763
- Operation {
764
- GmshRead[StrCat["../", "<<dm.magnet.solve.initial_conditions.pos_file_to_init_from>>", ".pos"]]; // This file has to be in format without mesh (no -v2, here with GmshParsed format)
765
- Generate[Projection_h_to_h]; Solve[Projection_h_to_h];
766
- TransferSolution[Projection_h_to_h];
767
- }
768
- }
769
- {% endif %}
770
- }
771
-
772
- PostProcessing {
773
- { Name MagDyn_hphi; NameOfFormulation MagDyn_hphi;
774
- Quantity {
775
- { Name phi; Value{ Local{ [ {dInv h} ] ;
776
- In OmegaCC; Jacobian Vol; } } }
777
- { Name h; Value{ Local{ [ {h} ] ;
778
- In Omega; Jacobian Vol; } } }
779
- { Name b; Value{ Local{ [ mu[] * {h} ] ;
780
- In Omega; Jacobian Vol; } } }
781
- { Name b_reaction; Value{ Local{ [ mu[] * ({h} - hsVal[]) ] ;
782
- In Omega; Jacobian Vol; } } }
783
- { Name j; Value{ Local{ [ {d h} ] ;
784
- In OmegaC; Jacobian Vol; } } }
785
- { Name jc; Value{ Local{ [ jc[mu0*Norm[{h}]] ] ;
786
- In NonLinOmegaC; Jacobian Vol; } } }
787
- { Name power_filaments; Value{ Local{ [ rho[{d h}, mu0*Norm[{h}]] * {d h} * {d h} ] ; // j*e : Power (only for filaments)
788
- In NonLinOmegaC; Jacobian Vol; } } }
789
- { Name sigma_matrix; Value{ Local{ [ sigma[mu0*Norm[{h}]]] ;
790
- In Matrix; Jacobian Vol; } } }
791
- { Name j_plane; Value{ Local{ [ -1/rho[mu0*Norm[{h}]] * {d v} ] ;
792
- In Matrix; Jacobian Vol; } } }
793
- { Name v_plane; Value{ Local{ [ {v} ] ;
794
- In Matrix; Jacobian Vol; } } }
795
- { Name power_matrix;
796
- Value{
797
- Local{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ; // j*e = rho*j^2 in matrix (eddy)
798
- In Matrix ; Integration Int ; Jacobian Vol; }
799
- Local{ [ (sigma[mu0*Norm[{h}]] * {d v}) * {d v}] ; // j*e = sigma*e^2 in matrix (coupling)
800
- In Matrix ; Integration Int ; Jacobian Vol; }
801
- }
802
- }
803
- { Name couplingLoss;
804
- Value{
805
- Integral{ [ (sigma[mu0*Norm[{h}]] * {d v}) * {d v}] ; // j*e = sigma*e^2 in matrix
806
- In Matrix ; Integration Int ; Jacobian Vol; }
807
- }
808
- }
809
- { Name eddyLoss; // NEW. Eddyloss was computed as totalLoss[matrix], which combines eddy and couplingLoss
810
- Value{
811
- Integral{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ; // EddyLoss = rho*j^2 in matrix
812
- In Matrix ; Integration Int ; Jacobian Vol; }
813
- }
814
- }
815
- { Name totalLoss;
816
- Value{
817
- // Separate OmegaC into Matrix and nonlinear (resistivities take different argument types)
818
- Integral{ [rho[{d h}, mu0*Norm[{h}]] * {d h} * {d h}] ; // j*e = rho*j^2 (filaments)
819
- In NonLinOmegaC ; Integration Int ; Jacobian Vol; }
820
- Integral{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ; // j*e = rho*j^2 (eddy)
821
- In Matrix ; Integration Int ; Jacobian Vol; }
822
- Integral{ [ (sigma[mu0*Norm[{h}]]*{d v}) * {d v}] ; // j*e = sigma*e^2 in matrix (coupling)
823
- In Matrix ; Integration Int ; Jacobian Vol; }
824
- }
825
- }
826
-
827
- { Name heat_capacity;
828
- Value{
829
- Integral{ [(filament_Cv[T[] + $cumulative_temperature, mu0*Norm[{h}]] )] ; // j*e = rho*j^2 in filaments (?)
830
- In NonLinOmegaC ; Integration Int ; Jacobian Vol; }
831
- Integral{ [(matrix_Cv[T[] + $cumulative_temperature]) ] ; // j*e = rho*j^2 in filaments (?)
832
- In NonLinOmegaC ; Integration Int ; Jacobian Vol; }
833
- }
834
- }
835
-
836
- { Name I; Value { Term{ [ {I} ] ; In Cuts; } } }
837
- { Name V; Value { Term{ [ {V} ] ; In Cuts; } } }
838
- { Name Ip; Value { Term{ [ {Ip} ] ; In BndFilaments; } } }
839
- { Name Vp; Value { Term{ [ {Vp} ] ; In BndFilaments; } } }
840
- { Name I_integral;
841
- Value{
842
- Integral{ [ {d h} * Vector[0,0,1]] ;
843
- In OmegaC ; Integration Int ; Jacobian Vol; }
844
- }
845
- }
846
- { Name I_abs_integral;
847
- Value{
848
- Integral{ [ Fabs[{d h} * Vector[0,0,1]]] ;
849
- In OmegaC ; Integration Int ; Jacobian Vol; }
850
- }
851
- }
852
- // Applied field (useful for magnetization plots)
853
- { Name hsVal; Value{ Term { [ hsVal[] ]; In Omega; } } }
854
- // Magnetization: integral of 1/2 * (r /\ j) in a conducting (sub-)domain
855
- { Name m_avg; Value{ Integral{ [ 0.5 * XYZ[] /\ {d h} ] ;
856
- In OmegaC; Integration Int; Jacobian Vol; } } }
857
- // Magnetic energy
858
- { Name magnetic_energy; Value{ Integral{ [ 0.5 * mu[] * {h} * {h} ] ;
859
- In Omega; Integration Int; Jacobian Vol; } } }
860
- }
861
- }
862
- {% if dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
863
- { Name MagDyn_hphi_dynCorr; NameOfFormulation MagDyn_hphi_dynCorr;
864
- Quantity {
865
- { Name hp_static; Value{ Local{ [ {hp_static} ] ;
866
- In Matrix_partitions_for_TI; Jacobian Vol; } } }
867
- { Name hp_static_lagrange; Value{ Local{ [ {hp_static_lagrange} ] ;
868
- In Matrix_partitions_for_TI; Jacobian Vol; } } }
869
- { Name j_static; Value{ Local{ [ {d hp_static} ] ;
870
- In Matrix_partitions_for_TI; Jacobian Vol; } } }
871
- { Name hp_dynamic; Value{ Local{ [ {hp_dynamic} ] ;
872
- In Matrix_partitions_for_TI; Jacobian Vol; } } }
873
- { Name j_dynamic; Value{ Local{ [ {d hp_dynamic} ] ;
874
- In Matrix_partitions_for_TI; Jacobian Vol; } } }
875
- { Name j_stadyn; Value{ Local{ [ {d hp_static} + {d hp_dynamic} ] ;
876
- In Matrix_partitions_for_TI; Jacobian Vol; } } }
877
- { Name j_stadyn_mixed; Value{ Local{ [ - sigma[mu0*Norm[{h}]] * {d v} + {d hp_dynamic} ] ;
878
- In Matrix_partitions_for_TI; Jacobian Vol; } } }
879
- { Name totalLoss_dyn;
880
- Value{
881
- // Integral{ [rho[{d h}, mu0*Norm[{h}]] * {d h} * {d h}] ;
882
- // In OmegaC ; Integration Int ; Jacobian Vol; }
883
- // NEW separate OmegaC into Matrix and nonlinear
884
- Integral{ [rho[{d h}, mu0*Norm[{h}]] * {d h} * {d h}] ; // j*e = rho*j^2 in filaments
885
- In NonLinOmegaC ; Integration Int ; Jacobian Vol; }
886
- Integral{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ; // Eddy
887
- In Matrix ; Integration Int ; Jacobian Vol; }
888
- Integral{ [ (- sigma[mu0*Norm[{h}]]*{d v} + {d hp_dynamic}) * (- {d v} + rho[mu0*Norm[{h}]] * {d hp_dynamic})] ;
889
- In Matrix_partitions_for_TI ; Integration Int ; Jacobian Vol; } // Attention to signs!
890
- }
891
- }
892
- { Name couplingLoss_dyn;
893
- Value{
894
- Integral{ [(- sigma[mu0*Norm[{h}]]*{d v} + {d hp_dynamic}) * (- {d v} + rho[mu0*Norm[{h}]] * {d hp_dynamic})] ;
895
- In Matrix_partitions_for_TI ; Integration Int ; Jacobian Vol; } // Attention to signs!
896
- }
897
- }
898
- }
899
- }
900
- {% endif %}
901
- }
902
-
903
- PostOperation {
904
- { Name MagDyn;
905
- NameOfPostProcessing MagDyn_hphi;
906
- Operation {
907
- // Local field solutions
908
- {% if dm.magnet.postproc.generate_pos_files%}
909
- Print[ b, OnElementsOf Omega , File StrCat["b.pos"], Name "b [T]" ];
910
- Print[ b_reaction, OnElementsOf Omega , File StrCat["br.pos"], Name "br [T]" ];
911
- Print[ j, OnElementsOf OmegaC , File StrCat["j.pos"], Name "j [A/m2]" ];
912
- // Print[ jc, OnElementsOf NonLinOmegaC , File StrCat["jc.pos"], Name "jc [A/m2]" ];
913
- Print[ j_plane, OnElementsOf Matrix , File StrCat["jPlane.pos"], Name "j_plane [A/m2]" ];
914
- Print[ v_plane, OnElementsOf Matrix , File StrCat["vPlane.pos"], Name "v_plane [V/m]" ];
915
- // Print[ power_filaments, OnElementsOf NonLinOmegaC , File StrCat["powFil_f.pos"], Name "powerFilaments [W]" ];
916
- // Print[ power_matrix, OnElementsOf Matrix , File StrCat["powMat_f.pos"], Name "powerMatrix [W]" ];
917
- // Print[ sigma_matrix, OnElementsOf Matrix , File StrCat["sigmaMat_f.pos"], Name "sigmaMatrix [S/m]" ];
918
- Print[ phi, OnElementsOf OmegaCC , File StrCat["phi_f.pos"], Name "phi [A]" ];
919
- {% endif %}
920
- // Global solutions
921
- Print[ I, OnRegion BndMatrixCut, File StrCat[resDirectory,"/I_transport.txt"], Format SimpleTable];
922
- Print[ V, OnRegion BndMatrixCut, File StrCat[resDirectory,"/V_transport.txt"], Format SimpleTable];
923
- Print[ hsVal[Omega], OnRegion Matrix, Format TimeTable, File StrCat[resDirectory, "/hs_val.txt"]];
924
- Print[ m_avg[Filaments], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/magn_fil.txt"]];
925
- Print[ m_avg[Matrix], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/magn_matrix.txt"]];
926
- Print[ magnetic_energy[OmegaC], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/magnetic_energy_internal.txt"]];
927
- {% if dm.magnet.postproc.compute_current_per_filament == True %}
928
- // Integrals of local quantities
929
- Print[I_integral[filament_1_1], OnGlobal, File StrCat[resDirectory,"/I_integral.txt"], Format SimpleTable];
930
- For i In {1:number_of_layers}
931
- For j In {((i==1)?2:1):6}
932
- Print[I_integral[filament~{i}~{j}], OnGlobal, File > StrCat[resDirectory,"/I_integral.txt"], Format SimpleTable];
933
- EndFor
934
- EndFor
935
- Print[I_abs_integral[filament_1_1], OnGlobal, File StrCat[resDirectory,"/I_abs_integral.txt"], Format SimpleTable];
936
- For i In {1:number_of_layers}
937
- For j In {((i==1)?2:1):6}
938
- Print[I_abs_integral[filament~{i}~{j}], OnGlobal, File > StrCat[resDirectory,"/I_abs_integral.txt"], Format SimpleTable];
939
- EndFor
940
- EndFor
941
- // Global quantities (out-of-plane)
942
- Print[I, OnRegion Cut_1_1, File StrCat[resDirectory,"/I.txt"], Format SimpleTable];
943
- For i In {1:number_of_layers}
944
- For j In {((i==1)?2:1):6}
945
- Print[I, OnRegion Cut~{i}~{j}, File > StrCat[resDirectory,"/I.txt"], Format SimpleTable];
946
- EndFor
947
- EndFor
948
- Print[V, OnRegion Cut_1_1, File StrCat[resDirectory,"/V.txt"], Format SimpleTable];
949
- For i In {1:number_of_layers}
950
- For j In {((i==1)?2:1):6}
951
- Print[V, OnRegion Cut~{i}~{j}, File > StrCat[resDirectory,"/V.txt"], Format SimpleTable];
952
- EndFor
953
- EndFor
954
- // Global quantities (in-plane)
955
- Print[Ip, OnRegion filamentBnd_1_1, File StrCat[resDirectory,"/Ip.txt"], Format SimpleTable];
956
- For i In {1:number_of_layers}
957
- For j In {((i==1)?2:1):6}
958
- Print[Ip, OnRegion filamentBnd~{i}~{j}, File > StrCat[resDirectory,"/Ip.txt"], Format SimpleTable];
959
- EndFor
960
- EndFor
961
- Print[Vp, OnRegion filamentBnd_1_1, File StrCat[resDirectory,"/Vp.txt"], Format SimpleTable];
962
- For i In {1:number_of_layers}
963
- For j In {((i==1)?2:1):6}
964
- Print[Vp, OnRegion filamentBnd~{i}~{j}, File > StrCat[resDirectory,"/Vp.txt"], Format SimpleTable];
965
- EndFor
966
- EndFor
967
- {% endif %}
968
- {% if dm.magnet.postproc.save_last_current_density is not none %}
969
- // Last current density distribution for projection (not ready yet)
970
- Print[ j, OnElementsOf OmegaC, Format GmshParsed , File StrCat["../", "<<dm.magnet.postproc.save_last_current_density>>", ".pos"], Name "j [A/m2]", LastTimeStepOnly ];
971
- {% endif %}
972
- {% if dm.magnet.postproc.save_last_magnetic_field != "None" %}
973
- // Last magnetic field solution for projection. Note the special format GmshParsed required for proper GmshRead[] operation in the later pre-resolution.
974
- Print[ h, OnElementsOf Omega, Format GmshParsed , File StrCat["../", "<<dm.magnet.postproc.save_last_magnetic_field>>", ".pos"], Name "h [A/m]", LastTimeStepOnly ];
975
- {% endif %}
976
- }
977
- }
978
- {% if dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
979
- { Name MagDyn_dynCorr;
980
- NameOfPostProcessing MagDyn_hphi_dynCorr;
981
- Operation {
982
- {% if dm.magnet.postproc.generate_pos_files%}
983
- Print[ j_dynamic, OnElementsOf Matrix_partitions_for_TI , File StrCat["j_dynamic.pos"], Name "j_dynamic [A/m2]" ];
984
- Print[ j_stadyn_mixed, OnElementsOf Matrix_partitions_for_TI , File StrCat["j_stadyn_mixed.pos"], Name "j_stadyn_mixed [A/m2]" ];
985
- Print[ hp_dynamic, OnElementsOf Matrix_partitions_for_TI , File StrCat["hp_dynamic.pos"], Name "hp_dynamic [A/m]" ];
986
- // Print[ hp_static, OnElementsOf Matrix , File StrCat["hp_static.pos"], Name "hp_static [A/m]" ];
987
- // Print[ hp_static_lagrange, OnElementsOf Matrix , File StrCat["hp_static_lagrange.pos"], Name "hp_static_lagrange [A/m]" ];
988
- {% endif %}
989
- }
990
- }
991
- {% endif %}
992
- { Name MagDyn_energy; LastTimeStepOnly 1 ;
993
- NameOfPostProcessing MagDyn_hphi;
994
- Operation {
995
- Print[ totalLoss[NonLinOmegaC], OnGlobal, Format Table, StoreInVariable $indicFilamentLoss, File StrCat[resDirectory,"/dummy.txt"] ];
996
- }
997
- }
998
- { Name test_Losses; LastTimeStepOnly 1 ;
999
- NameOfPostProcessing MagDyn_hphi;
1000
- Operation {
1001
- Print[ totalLoss[NonLinOmegaC], OnGlobal, Format Table, StoreInVariable $indicFilamentLoss, File StrCat[resDirectory,"/dummy.txt"] ];
1002
- // Print[ totalLoss[Matrix], OnGlobal, Format Table, StoreInVariable $indicEddyLoss, File > StrCat[resDirectory,"/dummy.txt"] ];
1003
- Print[ eddyLoss[Matrix], OnGlobal, Format Table, StoreInVariable $indicEddyLoss, File > StrCat[resDirectory,"/dummy.txt"] ];
1004
- Print[ couplingLoss[Matrix], OnGlobal, Format Table, StoreInVariable $indicCouplingLoss, File > StrCat[resDirectory,"/dummy.txt"] ];
1005
- Print[ totalLoss[OmegaC], OnGlobal, Format Table, StoreInVariable $indicTotalLoss, File > StrCat[resDirectory,"/dummy.txt"] ];
1006
- }
1007
- }
1008
-
1009
- { Name heat_capacity; LastTimeStepOnly 1 ;
1010
- NameOfPostProcessing MagDyn_hphi;
1011
- Operation {
1012
- Print[ heat_capacity[OmegaC], OnGlobal, Format Table, StoreInVariable $heat_capacity_per_unit_length, File StrCat[resDirectory,"/dummy.txt"] ];
1013
- }
1014
- }
1015
-
1016
- {% if dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
1017
- { Name test_Losses_dynCorr; LastTimeStepOnly 1 ;
1018
- NameOfPostProcessing MagDyn_hphi_dynCorr;
1019
- Operation {
1020
- Print[ totalLoss_dyn[OmegaC], OnGlobal, Format Table, StoreInVariable $indicTotalLoss_dyn, File > StrCat[resDirectory,"/dummy.txt"] ];
1021
- Print[ couplingLoss_dyn[Matrix_partitions_for_TI], OnGlobal, Format Table, StoreInVariable $indicCouplingLoss_dyn, File > StrCat[resDirectory,"/dummy.txt"] ];
1022
- }
1023
- }
1024
- {% endif %}
1025
- }
1
+ Group {
2
+ // ------- PROBLEM DEFINITION -------
3
+ // Filaments
4
+ Filaments_SC = Region[{<<rm.powered.Filaments.vol.numbers|join(', ')>>}]; // Filament superconducting region
5
+ Filament_holes = Region[{<<rm.powered.Filaments.surf_in.numbers|join(', ')>>}]; // Filament holes
6
+
7
+ // Filaments = Region[{Filaments_SC, Filament_holes}]; // Filaments (including holes) - not used
8
+
9
+ BndFilaments = Region[{<<rm.powered.Filaments.surf.numbers|join(', ')>>}]; // Filament boundaries
10
+ BndFilaments_holes = Region[{<<rm.insulator.curve.numbers|join(', ')>>}]; // Filament hole boundaries
11
+ Cuts = Region[{<<rm.powered.Filaments.cochain.numbers|join(', ')>>}]; // Cuts on filament boundaries
12
+
13
+ // Define the filaments, boundaries and cuts according to their layer and index in the layer
14
+ {% if len(rm.powered.Filaments.vol.numbers) % 6 == 0 %} // There is no inner layer (of length 1)
15
+ {% for layer in range( int(len(rm.powered.Filaments.vol.numbers)/6 )) %}
16
+ {% for filament_index in range(6) %}
17
+ filament_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.vol.numbers[layer*6 + filament_index]>>}]; // Filament surface
18
+ filamentBnd_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.surf.numbers[layer*6 + filament_index]>>}]; // Boundary
19
+ Cut_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.cochain.numbers[layer*6 + filament_index]>>}]; // Cut
20
+ {%endfor%}
21
+ {%endfor%}
22
+
23
+ {% elif len(rm.powered.Filaments.vol.numbers) % 6 == 1 %} // There is an inner layer (of length 1)
24
+ // Define the inner point
25
+ filament_0_0 = Region[{<<rm.powered.Filaments.vol.numbers[0]>>}];
26
+ filamentBnd_0_0 = Region[{<<rm.powered.Filaments.surf.numbers[0]>>}];
27
+ Cut_0_0 = Region[{<<rm.powered.Filaments.cochain.numbers[0]>>}];
28
+
29
+ {% for layer in range(0, int((len(rm.powered.Filaments.vol.numbers)-1) /6 ) ) %}
30
+ {% for filament_index in range(6) %}
31
+ filament_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.vol.numbers[layer*6 + filament_index + 1]>>}];
32
+ filamentBnd_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.surf.numbers[layer*6 + filament_index + 1]>>}];
33
+ Cut_<<layer + 1>>_<<filament_index + 1>> = Region[{<<rm.powered.Filaments.cochain.numbers[layer*6 + filament_index + 1]>>}];
34
+ {%endfor%}
35
+ {%endfor%}
36
+
37
+ {% endif %}
38
+
39
+ // To assign different material properties to each filament hole, we need to assign them separately.
40
+ {% for i, hole in enumerate(rm.powered.Filaments.surf_in.numbers) %}
41
+ FilamentHole_<<i>> = Region[{<<hole>>}];
42
+ {%endfor%}
43
+
44
+ // Matrix partitions
45
+ {% for i, matrix_partition in enumerate(rm.induced.Matrix.vol.numbers)%}
46
+ Matrix_<<i>> = Region[{<<matrix_partition>>}];
47
+ {%endfor%}
48
+
49
+ // Matrix
50
+ Matrix = Region[{<<rm.induced.Matrix.vol.numbers|join(', ')>>}];
51
+ BndMatrix = Region[ <<rm.induced.Matrix.surf_out.numbers[0]>> ]; // Strand outer boundary
52
+ BndMatrixCut = Region[ {<<rm.induced.Matrix.cochain.numbers|join(', ')>>} ]; // Strand outer boundary cut
53
+
54
+ // Air
55
+ Air = Region[<<rm.air.vol.number>>]; // Air surface
56
+ BndAir = Region[<<rm.air.surf.number>>]; // Air outer boundary
57
+
58
+ // Cuts
59
+ {% if dm.magnet.geometry.type == 'periodic_square' %}
60
+ // Manual cuts
61
+ CutAirVertical = Region[<<rm.air.cochain.numbers[0]>>];
62
+ AirVerticalBoundary = Region[<<rm.air.cochain.numbers[1]>>];
63
+ CutAirHorizontal = Region[<<rm.air.cochain.numbers[2]>>];
64
+ AirHorizontalBoundary = Region[<<rm.air.cochain.numbers[3]>>];
65
+
66
+ BndMatrixCut += Region[{CutAirVertical, CutAirHorizontal}];
67
+ // Transition Layers of cuts
68
+ CutAirVerticalTL = ElementsOf[AirVerticalBoundary, OnOneSideOf CutAirVertical];
69
+ CutAirHorizontalTL = ElementsOf[AirHorizontalBoundary, OnOneSideOf CutAirHorizontal];
70
+
71
+ // Domain boundaries for periodic condition
72
+ BndDomainTop = Region[<<rm.induced.Domain.surf_out.numbers[0]>>];
73
+ BndDomainBottom = Region[<<rm.induced.Domain.surf_out.numbers[1]>>];
74
+ BndDomainLeft = Region[<<rm.induced.Domain.surf_out.numbers[2]>>];
75
+ BndDomainRight = Region[<<rm.induced.Domain.surf_out.numbers[3]>>];
76
+ {% endif %}
77
+ // Cuts += Region[BndMatrixCut]; // The matrix cut is not part of the 'Cuts' group anymore, but is rtreated separately
78
+
79
+ // Define a region for the matrix partitions to be included in the TI (transverse currents) problem
80
+ TI_adjacent_region = Region[ {Air} ]; // Define the regions adjacent to the region on which the TI problem is solved (used for defining h_perp_space_dynamic)
81
+ {% if dm.magnet.geometry.io_settings.load.load_from_yaml %}
82
+ Matrix_partitions_excluded_from_TI = Region[{<<mp.Surfaces_excluded_from_TI|join(', ')>>}]; // Matrix partitions excluded from the TI problem
83
+
84
+ Matrix_partitions_for_TI = Region[ {Matrix} ];
85
+ Matrix_partitions_for_TI -= Region[ {Matrix_partitions_excluded_from_TI} ]; // Matrix partitions included in the TI problem
86
+
87
+ TI_adjacent_region += Region[ {Matrix_partitions_excluded_from_TI} ]; // Define the regions adjacent to the region on which the TI problem is solved (used for defining h_perp_space_dynamic)
88
+ {% else %}
89
+ Matrix_partitions_for_TI = Region[ {Matrix} ];
90
+ {% endif %}
91
+
92
+ {% if dm.magnet.solve.diffusion_barriers.enable %}
93
+ // Regions for resistors representing diffusion barriers
94
+ Resistors_diffusion_barrier = Region[ {} ]; // Initialize the region for the resistors representing diffusion barriers
95
+ {% if len(rm.powered.Filaments.vol.numbers) % 6 == 0 %} // There is no inner layer (of length 1)
96
+ {% for layer in range( int(len(rm.powered.Filaments.vol.numbers)/6 )) %}
97
+ {% for filament_index in range(6) %}
98
+ R_diffusion_barrier_<<layer+1>>_<<filament_index+1>> = Region[{<<5000 + layer*6 + filament_index + 1>>}];
99
+ Resistors_diffusion_barrier += Region[{R_diffusion_barrier_<<layer+1>>_<<filament_index+1>>}];
100
+ {%endfor%}
101
+ {%endfor%}
102
+
103
+ {% elif len(rm.powered.Filaments.vol.numbers) % 6 == 1 %} // There is an inner layer (of length 1)
104
+
105
+ {% for layer in range(0, int((len(rm.powered.Filaments.vol.numbers)-1) /6 ) ) %}
106
+ {% for filament_index in range(6) %}
107
+ R_diffusion_barrier_<<layer+1>>_<<filament_index+1>> = Region[{<<5000 + layer*6 + filament_index + 1>>}];
108
+ Resistors_diffusion_barrier += Region[{R_diffusion_barrier_<<layer+1>>_<<filament_index+1>>}];
109
+ {%endfor%}
110
+ {%endfor%}
111
+
112
+ {% endif %}
113
+ {% endif %}
114
+
115
+ {% if dm.magnet.solve.global_diffusion_barrier.enable %}
116
+ // Defining physical regions for the global diffusion barrier in the matrix between filament region and external region
117
+ GlobalDiffusionBarrier = Region[ {<<mp.GlobalDiffusionBarrier.RegionTag>>} ]; // the barrier itself
118
+ MatrixInternalSurface = Region[ {<<mp.GlobalDiffusionBarrier.InternalRegionTag>>} ]; // the physical region inside of it, of which the barrier is the external boundary
119
+ // (for safe implementation of the discontinuous function space, instead of OnOneSideOf, which depends on the orientation of the curves)
120
+ {% endif %}
121
+
122
+ // Split into conducting and non-conducting domains
123
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes == 'None' or dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes == 0.0 %}
124
+ // Holes are treated as air -> non-conducting domain
125
+ LinOmegaC = Region[ {Matrix} ];
126
+ BndOmegaC = Region[ {BndMatrix, BndFilaments, BndFilaments_holes} ];
127
+ OmegaCC = Region[ {Air, Filament_holes} ];
128
+ {% else %}
129
+ // Holes are conducting -> part of the conducting domain
130
+ LinOmegaC = Region[ {Matrix, Filament_holes} ];
131
+ BndOmegaC = Region[ {BndMatrix, BndFilaments} ];
132
+ OmegaCC = Region[ {Air} ];
133
+ {% endif %}
134
+ NonLinOmegaC = Region[ {Filaments_SC} ];
135
+ OmegaC = Region[ {LinOmegaC, NonLinOmegaC} ];
136
+ Omega = Region[ {OmegaC, OmegaCC} ]; // the whole domain (only surfaces in 2D, or volumes in 3D)
137
+
138
+ OmegaC_AndBnd = Region[{OmegaC, BndOmegaC}]; // useful for function space definition
139
+ OmegaCC_AndBnd = Region[{OmegaCC, BndOmegaC, BndAir}]; // useful for function space definition
140
+
141
+ // Here we define points on the boundaries of the filaments and the outer matrix boundary.
142
+ // These points are used to fix the magnetic potential to zero on the boundaries.
143
+ MatrixPointOnBoundary = Region[<<rm.air.point.numbers[0]>>];
144
+ FilamentPointsOnBoundaries = Region[{<<rm.powered.Filaments.curve.numbers|join(', ')>>}];
145
+ ArbitraryPoints = Region[{MatrixPointOnBoundary, FilamentPointsOnBoundaries}];
146
+
147
+ }
148
+
149
+ Function{
150
+ // ------- GEOMETRY PARAMETERS -------
151
+ {% if len(rm.powered.Filaments.vol.numbers) % 6 == 0 %}
152
+ number_of_layers = <<len(rm.powered.Filaments.vol.numbers)>>/6;
153
+ {% elif len(rm.powered.Filaments.vol.numbers) % 6 == 1 %}
154
+ number_of_layers = (<<len(rm.powered.Filaments.vol.numbers)>>-1) / 6;
155
+ {% endif %}
156
+
157
+ // Set correction factor based on the periodicity length
158
+ {% if dm.magnet.solve.formulation_parameters.two_ell_periodicity %}
159
+ correctionFactor = 0.827; // to be automatized (equal to sin(x)/x with x = pi*ell/p, with ell = p/6 in the hexagonal lattice case)
160
+ ell = 2*correctionFactor * <<dm.conductors[dm.magnet.solve.conductor_name].strand.fil_twist_pitch>> / 6;
161
+ {% else %}
162
+ correctionFactor = 0.9549;
163
+ ell = correctionFactor * <<dm.conductors[dm.magnet.solve.conductor_name].strand.fil_twist_pitch>> / 6;
164
+ {% endif %}
165
+
166
+ // ------- MATERIAL PARAMETERS -------
167
+ temperature = <<dm.magnet.solve.general_parameters.temperature>>;
168
+ T[] = temperature; // this can be made a function of time if needed. Later on, T may also be a field we solve for.
169
+
170
+
171
+ mu0 = Pi*4e-7; // [H/m]
172
+ nu0 = 1.0/mu0; // [m/H]
173
+ mu[Omega] = mu0;
174
+ nu[Omega] = nu0;
175
+
176
+
177
+ // Assigning resistances associated with diffusion barriers:
178
+ {% if dm.magnet.solve.diffusion_barriers.enable %}
179
+ {% if dm.magnet.geometry.io_settings.load.load_from_yaml and dm.magnet.solve.diffusion_barriers.load_data_from_yaml %}
180
+ // If we load the geometry from a YAML file, we can load the resistances of the diffusion barriers from the YAML file.
181
+ {% for layer in range( int(len(rm.powered.Filaments.vol.numbers)/6 )) %}
182
+ {% for filament_index in range(6) %}
183
+ R[R_diffusion_barrier_<<layer+1>>_<<filament_index+1>>] = <<mp.DiffusionBarriers.FilamentResistances[layer*6 + filament_index]>>; // [Ohm]
184
+ {% endfor %}
185
+ {% endfor %}
186
+ {% else %}
187
+ // If we don't load the geometry from a YAML file, we can assign the resistances of the diffusion barriers from the material properties data structure.
188
+ {% for layer in range( int(len(rm.powered.Filaments.vol.numbers)/6 )) %}
189
+ {% for filament_index in range(6) %}
190
+ R[R_diffusion_barrier_<<layer+1>>_<<filament_index+1>>] = <<mp.DiffusionBarriers.FilamentResistances[layer*6 + filament_index]>>; // [Ohm]
191
+ {% endfor %}
192
+ {% endfor %}
193
+ {% endif %}
194
+ {% endif %}
195
+
196
+ {% if dm.magnet.solve.global_diffusion_barrier.enable %}
197
+ // Handling global diffusion barrier in the matrix between filament region and external region
198
+ rho_contact[GlobalDiffusionBarrier] = <<mp.GlobalDiffusionBarrier.ContactResistivity>>;
199
+ {% endif %}
200
+
201
+ // Copper-like Matrix
202
+ {% if isinstance(dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer, float) %} // If the matrix has constant resistivity we can assign it directly
203
+ rho[Matrix] = <<dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer>>;
204
+ {% elif dm.magnet.geometry.io_settings.load.load_from_yaml %}
205
+ // If we load the geometry from a YAML file, we also have material properties assigned in a MaterialProperties (mp) data structure.
206
+ // This is a temporary solution. It reads the material property functions from the input YAML file and only assigns the RRR value from the geometry YAML.
207
+ // In the future the material properties should be assigned directly from the geometry YAML.
208
+ {% for i, matrix_partition_material in zip(range(len(rm.induced.Matrix.vol.numbers)), rm.induced.Matrix.vol.names)%}
209
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu_T' %}
210
+ rho[Matrix_<<i>>] = CFUN_rhoCu_T[T[]]{0, << mp.Materials[matrix_partition_material].RRR >>};
211
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu' %}
212
+ rho[Matrix_<<i>>] = CFUN_rhoCu_T_B[T[], $1]{<< mp.Materials[matrix_partition_material].RRR >>};
213
+ {% endif %}
214
+ {%endfor%}
215
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.RRR is iterable %}
216
+ {% for i, RRR_i in enumerate(dm.conductors[dm.magnet.solve.conductor_name].strand.RRR) %}
217
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu_T' %}
218
+ rho[Matrix_<<i>>] = CFUN_rhoCu_T[T[]]{0, <<RRR_i>>};
219
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu' %}
220
+ rho[Matrix_<<i>>] = CFUN_rhoCu_T_B[T[], $1]{<<RRR_i>>};
221
+ {% endif %}
222
+ {%endfor%}
223
+ {% else %} // If we don't load the geometry from a YAML file, we can assign the material properties directly
224
+ RRR_matrix = <<dm.conductors[dm.magnet.solve.conductor_name].strand.RRR>>;
225
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu_T' %}
226
+ rho[Matrix] = CFUN_rhoCu_T[T[]]{0, RRR_matrix};
227
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_stabilizer == 'CFUN_rhoCu' %}
228
+ rho[Matrix] = CFUN_rhoCu_T_B[T[], $1]{RRR_matrix}; //1.81e-10; //
229
+ // rho[Matrix_0] = CFUN_rhoCu_T_B[T[], $1]{10}; //1.81e-10; //
230
+ // rho[Matrix_1] = CFUN_rhoCu_T_B[T[], $1]{100}; //1.81e-10; //
231
+ // rho[Matrix_2] = CFUN_rhoCu_T_B[T[], $1]{1000}; //1.81e-10; //
232
+ {% endif %}
233
+ {% endif %}
234
+
235
+ // Superconducting filaments (nonlinear now)
236
+ ec = <<dm.conductors[dm.magnet.solve.conductor_name].strand.ec_superconductor>>; // [V/m], the value 1e-4 V/m is a common convention
237
+
238
+ {% if dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Ic_A_NbTi' and dm.conductors[dm.magnet.solve.conductor_name].strand.material_superconductor == 'NbTi' %}
239
+ jc[] = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Jc_5T_4_2K>> / 1.70732393e9 * CFUN_IcNbTi_T_B_a[T[], $1, 1]; // [A/m2] critical current density as function of temperature and field amplitude
240
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Ic_A_Nb3Sn' and dm.conductors[dm.magnet.solve.conductor_name].strand.material_superconductor == 'Nb3Sn' %}
241
+ jc[] = CFUN_IcNb3Sn_T_B_a[T[], $1, 1]; // [A/m2] critical current density as function of temperature and field amplitude
242
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Bottura' and dm.conductors[dm.magnet.solve.conductor_name].strand.material_superconductor == 'Nb3Sn' %}
243
+ Tc0_Bottura = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Tc0_Bottura>>;
244
+ Bc20_Bottura = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Bc20_Bottura>>;
245
+ C0_Bottura = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.C0_Bottura>>;
246
+ jc[] = CFUN_Jc_Nb3Sn_Bottura_T_B[T[], $1]{Tc0_Bottura, Bc20_Bottura, C0_Bottura, 0.5, 2, 1, 1}; // p = 0.5, q = 2
247
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Bordini' %}
248
+ C0_Bordini = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.C0_Bordini>>;
249
+ Tc0_Bordini = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Tc0_Bordini>>;
250
+ Bc20_Bordini = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Bc20_Bordini>>;
251
+ alpha_Bordini = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.alpha_Bordini>>;
252
+ jc[] = CFUN_Jc_Bordini_T_B[T[], $1]{C0_Bordini, Tc0_Bordini, Bc20_Bordini, alpha_Bordini};
253
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Nb3Sn_HFM' %}
254
+ C0_Nb3Sn_HFM = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.C0_Nb3Sn_HFM>>;
255
+ Tc0_Nb3Sn_HFM = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Tc0_Nb3Sn_HFM>>;
256
+ Bc20_Nb3Sn_HFM = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Bc20_Nb3Sn_HFM>>;
257
+ alpha_Nb3Sn_HFM = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.alpha_Nb3Sn_HFM>>;
258
+ nu_Nb3Sn_HFM = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.nu_Nb3Sn_HFM>>;
259
+ p_Nb3Sn_HFM = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.p_Nb3Sn_HFM>>;
260
+ q_Nb3Sn_HFM = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.q_Nb3Sn_HFM>>;
261
+ jc[] = CFUN_Jc_Nb3Sn_HFM_T_B[T[], $1]{C0_Nb3Sn_HFM, Tc0_Nb3Sn_HFM, Bc20_Nb3Sn_HFM, alpha_Nb3Sn_HFM, nu_Nb3Sn_HFM, p_Nb3Sn_HFM, q_Nb3Sn_HFM};
262
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'ProDefined' %}
263
+ C0 = <<float(dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.C0)>>;
264
+ Tc0 = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Tc0>>;
265
+ Bc20 = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Bc20>> + <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.B0>>;
266
+ alpha = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.alpha>>;
267
+ p = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.p>>;
268
+ q = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.q>>;
269
+ v = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.v>>;
270
+ B0 = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.B0>>;
271
+ jc[] = (C0 * (1 - (T[] / Tc0)^v)^alpha * (1 - (T[] / Tc0)^2)^alpha) / (Abs[$1] + B0) * (Min[(Abs[$1] + B0) / (Bc20 * (1 - (T[] / Tc0)^v)), 1])^p * (1 - Min[(Abs[$1] + B0) / (Bc20 * (1 - (T[] / Tc0)^v)), 1])^q ;
272
+
273
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type == 'Constant Jc' %}
274
+ jc[] = <<dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.Jc_constant>>; // [A/m2] constant critical current density
275
+ {% endif %}
276
+
277
+ // Power law
278
+ 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>>];
279
+ n = <<dm.conductors[dm.magnet.solve.conductor_name].strand.n_value_superconductor>>; // [-] power law index, one key parameter for the power law
280
+ rho_power[] = ec / (Max[jc[$2],eps_jc[]]) * (Norm[$1] / (Max[jc[$2],eps_jc[]]))^(n - 1); // [Ohm m] power law resistivity
281
+ e_power[] = rho_power[$1, $2] * $1;
282
+ dedj_power[] = (
283
+ ec / ((Max[jc[$2],eps_jc[]])#1) * (Norm[$1]/#1)^(n - 1) * TensorDiag[1, 1, 1] +
284
+ ec / (#1)^3 * (n - 1) * (Norm[$1]/#1)^(n - 3) * SquDyadicProduct[$1]);
285
+
286
+ db = 0.005;
287
+ // outer_product[] = Tensor[CompX[$1]*CompX[$2], CompX[$1]*CompY[$2], CompX[$1]*CompZ[$2], CompY[$1]*CompX[$2], CompY[$1]*CompY[$2], CompY[$1]*CompZ[$2], CompZ[$1]*CompX[$2], CompZ[$1]*CompY[$2], CompZ[$1]*CompZ[$2]];
288
+ // dedb_power[] = -n*ec/jc[Norm[$2]]^2 * (Norm[$1]/jc[Norm[$2]])^(n-1) * ( jc[Norm[$2] + db] - jc[Max[0, Norm[$2] - db]] ) / (2*db) * outer_product[$1, $2]/(Norm[$2]+1e-10); // Is this one correct?
289
+ dedb_power[] = Tensor[ CompX[(e_power[$1, Norm[$2+Vector[db,0,0]]] - e_power[$1, Norm[$2+Vector[-db,0,0]]])#1],
290
+ CompX[(e_power[$1, Norm[$2+Vector[0,db,0]]] - e_power[$1, Norm[$2+Vector[0,-db,0]]])#2],
291
+ CompX[(e_power[$1, Norm[$2+Vector[0,0,db]]] - e_power[$1, Norm[$2+Vector[0,0,-db]]])#3],
292
+ CompY[#1], CompY[#2], CompY[#3],
293
+ CompZ[#1], CompZ[#2], CompZ[#3]] / (2*db);
294
+
295
+ // Trials for extending the power to overcritical regimes
296
+ rho_N = 1e-7; // Resistivity of the filaments in their normal state
297
+ // 1) This first option shows very poor convergence properties: fails to convergence at the transition from normal state to superconducting state (NaN)
298
+ // rho_power_normal[] = Min[rho_power[$1, $2], rho_N];
299
+ // dedj_power_normal[] = (rho_power[$1, $2] <= rho_N) ? dedj_power[$1, $2] : rho_N * TensorDiag[1, 1, 1];
300
+ // dedb_power_normal[] = (jc[Norm[$2]] > eps_jc) ? dedb_power[$1, $2] : TensorDiag[0, 0, 0];
301
+ // 2) The second option below also shows very poor convergence: iteration cycles
302
+ // rho_power_normal[] = 1. / (1./rho_power[$1,$2] + 1./rho_N);
303
+ rho_power_normal[] = rho_power[$1,$2] / (1 + rho_power[$1,$2]/rho_N);
304
+ dedj_power_normal[] = rho_power_normal[$1,$2] * TensorDiag[1, 1, 1] + ec*(n-1)*Norm[$1]^(n-3) /((Max[jc[$2],eps_jc[]])^n)/(1+rho_power[$1,$2]/rho_N)^2 * SquDyadicProduct[$1];
305
+ // dedb_power_normal[] = 1./(1+rho_power[$1,Norm[$2]]/rho_N)^2 * dedb_power[$1,$2];
306
+ // 3) Third option with log-sum-exp expression to describe the transition... to be tried.
307
+
308
+
309
+ {% if dm.magnet.solve.general_parameters.superconductor_linear %}
310
+ mult_copper_like = 1e-5;
311
+ // Set everything to copper
312
+ rho[Filaments_SC] = 1.81e-10;
313
+ dedj[Filaments_SC] = 1.81e-10;
314
+ {% else %}
315
+ rho[Filaments_SC] = rho_power[$1, $2];
316
+ dedj[Filaments_SC] = dedj_power[$1, $2];
317
+ // rho[Filaments_SC] = rho_power_normal[$1, $2]; // Power law in parallel with constant resistivity -> poor convergence is observed
318
+ // dedj[Filaments_SC] = dedj_power_normal[$1, $2];
319
+ {% endif %}
320
+ // The de/db Jacobian is not used for the moment
321
+ dedb[Filaments_SC] = dedb_power[$1, $2];
322
+ // dedb[Filaments_SC] = dedb_power_normal[$1, $2]; // Poor convergence observed (cycles)
323
+
324
+ {% if dm.magnet.geometry.io_settings.load.load_from_yaml %}
325
+ // We assign material properties to each filament hole. Currently we just assign the same properties as the matrix, but with a different RRR.
326
+ // This must be updated to actually use the correct material property read from the geometry YAML.
327
+ {% for i, hole_material in zip(range(len(rm.powered.Filaments.surf_in.numbers)), rm.powered.Filaments.surf_in.names)%}
328
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes == 'CFUN_rhoCu_T' %}
329
+ rho[FilamentHole_<<i>>] = CFUN_rhoCu_T[T[]]{0, << mp.Materials[hole_material].RRR >>};
330
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes == 'CFUN_rhoCu_T_B' %}
331
+ rho[FilamentHole_<<i>>] = CFUN_rhoCu_T_B[T[], $1]{<< mp.Materials[hole_material].RRR >>};
332
+ {% elif isinstance(dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes, float) %} // If the matrix has constant resistivity we can assign it directly
333
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes %} // non-zero
334
+ rho[FilamentHole_<<i>>] = <<dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes>>;
335
+ {% endif %}
336
+ {% endif %}
337
+ {% endfor %}
338
+ {% else %} // parametric geometry
339
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.filament_hole_diameter %} // only if holes are present
340
+ {% for i, hole_material in zip(range(len(rm.powered.Filaments.surf_in.numbers)), rm.powered.Filaments.surf_in.names)%}
341
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes == 'CFUN_rhoCu_T' %}
342
+ rho[FilamentHole_<<i>>] = CFUN_rhoCu_T[T[]]{0, 50}; // only temporary as the hole material should be air
343
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes == 'CFUN_rhoCu_T_B' %}
344
+ rho[FilamentHole_<<i>>] = CFUN_rhoCu_T_B[T[], $1]{50}; // only temporary as the hole material should be air
345
+ {% elif isinstance(dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes, float) %} // If the matrix has constant resistivity we can assign it directly
346
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes %} // non-zero
347
+ rho[FilamentHole_<<i>>] = <<dm.conductors[dm.magnet.solve.conductor_name].strand.rho_material_holes>>;
348
+ {% endif %}
349
+ {% endif %}
350
+ {% endfor %}
351
+ {% endif %}
352
+ {% endif %}
353
+
354
+ sigma[] = 1/rho[$1] ; // Can only be used in the matrix
355
+
356
+ // HEAT APPROXIMATION
357
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.Cv_material_superconductor == 'CFUN_CvNbTi' %}
358
+ filament_Cv[] = CFUN_CvNbTi_T_B[$1, $2]{0, 1, 0}; // Volumetric heat capacity [J/(m3 K)], as function of temperature and field magnitude.
359
+ {% elif dm.conductors[dm.magnet.solve.conductor_name].strand.Cv_material_superconductor == 'CFUN_CvNb3Sn' %}
360
+ filament_Cv[] = CFUN_CvNb3Sn_T_B[$1, $2]; // Volumetric heat capacity [J/(m3 K)], as function of temperature and field magnitude.
361
+ {% endif %}
362
+
363
+ {% if dm.conductors[dm.magnet.solve.conductor_name].strand.Cv_material_stabilizer == 'CFUN_CvCu' %}
364
+ matrix_Cv[] = CFUN_CvCu_T[$1]; // Volumetric heat capacity [J/(m3 K)], as function of temperature
365
+ {% endif %}
366
+
367
+ // ------- SOURCE PARAMETERS -------
368
+ directionApplied[] = Vector[Cos[<<dm.magnet.solve.source_parameters.field_angle>>*Pi/180], Sin[<<dm.magnet.solve.source_parameters.field_angle>>*Pi/180], 0.];
369
+ // same for piecewise and sine
370
+ {% if dm.magnet.solve.source_parameters.source_type == 'sine' %}
371
+ // Sine wave source (with DC component)
372
+ f = <<dm.magnet.solve.source_parameters.sine.frequency>>; // Frequency of applied field [Hz]
373
+
374
+ time_multiplier = 1; // Set to 1, as it is being used in the resolution
375
+
376
+ // ramp_duration = -5e-2 / f; // Ramp duration for constant source components
377
+ // quad_ramp_I[] = (Sqrt[<<dm.magnet.solve.source_parameters.sine.superimposed_DC.current_magnitude>>] / ramp_duration * $Time )^2;
378
+ // constant_I_transport[] = ($Time < ramp_duration ) ? InterpolationLinear[$Time]{List[{0,0,ramp_duration,<<dm.magnet.solve.source_parameters.sine.superimposed_DC.current_magnitude>>}]} : <<dm.magnet.solve.source_parameters.sine.superimposed_DC.current_magnitude>>;
379
+ constant_I_transport[] = <<dm.magnet.solve.source_parameters.sine.superimposed_DC.current_magnitude>>;
380
+ I_transport[] = constant_I_transport[$Time] + <<dm.magnet.solve.source_parameters.sine.current_amplitude>> * Sin[2*Pi*f * $Time];
381
+
382
+ constant_field_magnitude = <<dm.magnet.solve.source_parameters.sine.superimposed_DC.field_magnitude>>;
383
+ hsVal[] = nu0 * (<<dm.magnet.solve.source_parameters.sine.field_amplitude>> * Sin[2*Pi*f * $Time] + constant_field_magnitude) * directionApplied[];
384
+ hsVal_prev[] = nu0 *(<<dm.magnet.solve.source_parameters.sine.field_amplitude>> * Sin[2*Pi*f * ($Time-$DTime)] + constant_field_magnitude) * directionApplied[];
385
+
386
+ {% elif dm.magnet.solve.source_parameters.source_type == 'rotating' %}
387
+ // Rotating magnetic field
388
+ f = <<dm.magnet.solve.source_parameters.rotating.frequency>>;
389
+ ramp_duration = 1e-1 / f;
390
+
391
+ time_multiplier = 1;
392
+ I_transport[] = 0;
393
+ constant_field_magnitude[] = ($Time < ramp_duration ) ? InterpolationLinear[$Time]{List[{0,0,ramp_duration,<<dm.magnet.solve.source_parameters.rotating.field_magnitude>>}]} : <<dm.magnet.solve.source_parameters.rotating.field_magnitude>>;
394
+ constant_field_magnitude_prev[] = ($Time-$DTime < ramp_duration ) ? InterpolationLinear[$Time-$DTime]{List[{0,0,ramp_duration,<<dm.magnet.solve.source_parameters.rotating.field_magnitude>>}]} : <<dm.magnet.solve.source_parameters.rotating.field_magnitude>>;
395
+
396
+ hsVal[] = nu0 * constant_field_magnitude[] * Vector[Cos[2*Pi*f*$Time], Sin[2*Pi*f*$Time], 0.];
397
+ hsVal_prev[] = nu0 * constant_field_magnitude_prev[] * Vector[Cos[2*Pi*f*($Time-$DTime)], Sin[2*Pi*f*($Time-$DTime)], 0.];
398
+
399
+ {% elif dm.magnet.solve.source_parameters.source_type == 'piecewise' %}
400
+
401
+ time_multiplier = <<dm.magnet.solve.source_parameters.piecewise.time_multiplier>>;
402
+ applied_field_multiplier = <<dm.magnet.solve.source_parameters.piecewise.applied_field_multiplier>>;
403
+ transport_current_multiplier = <<dm.magnet.solve.source_parameters.piecewise.transport_current_multiplier>>;
404
+
405
+ {% if dm.magnet.solve.source_parameters.piecewise.source_csv_file %} // Source from CSV file
406
+ timeList() = {<<ed['time']|join(', ')>>};
407
+ valueList() = {<<ed['value']|join(', ')>>};
408
+ timeValuesList() = ListAlt[timeList(), valueList()];
409
+
410
+ hsVal[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{List[timeValuesList()]} * directionApplied[];
411
+ hsVal_prev[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time-$DTime)/time_multiplier]]{List[timeValuesList()]} * directionApplied[];
412
+ I_transport[] = transport_current_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{List[timeValuesList()]};
413
+
414
+ {% else %} // Source from parameters
415
+ times_source_piecewise_linear() = {<<dm.magnet.solve.source_parameters.piecewise.times|join(', ')>>};
416
+ transport_currents_relative_piecewise_linear() = {<<dm.magnet.solve.source_parameters.piecewise.transport_currents_relative|join(', ')>>};
417
+ applied_fields_relative_piecewise_linear() = {<<dm.magnet.solve.source_parameters.piecewise.applied_fields_relative|join(', ')>>};
418
+
419
+ hsVal[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{ListAlt[times_source_piecewise_linear(), applied_fields_relative_piecewise_linear()]} * directionApplied[];
420
+ hsVal_prev[] = nu0 * applied_field_multiplier * InterpolationLinear[Max[0,($Time-$DTime)/time_multiplier]]{ListAlt[times_source_piecewise_linear(), applied_fields_relative_piecewise_linear()]} * directionApplied[];
421
+ I_transport[] = transport_current_multiplier * InterpolationLinear[Max[0,($Time)/time_multiplier]]{ListAlt[times_source_piecewise_linear(), transport_currents_relative_piecewise_linear()]};
422
+ {% endif %}
423
+ {% endif %}
424
+
425
+ // For the natural boundary condition (restricted to fields of constant direction for the moment, should be generalized)
426
+ dbsdt[] = mu0 * (hsVal[] - hsVal_prev[]) / $DTime; // must be a finite difference to avoid error accumulation
427
+
428
+ // ------- NUMERICAL PARAMETERS -------
429
+ timeStart = 0.; // Initial time [s]
430
+
431
+ {% if dm.magnet.solve.source_parameters.source_type == 'sine'%}
432
+ timeFinal = <<dm.magnet.solve.numerical_parameters.sine.number_of_periods_to_simulate>>/f; // Final time for source definition (s)
433
+ dt = 1 / (f*<<dm.magnet.solve.numerical_parameters.sine.timesteps_per_period>>); // Time step (initial if adaptive) (s)
434
+ dt_max = dt; // Fixed maximum time step
435
+ dt_max_var[] = dt_max;
436
+ {% elif dm.magnet.solve.source_parameters.source_type == 'rotating'%}
437
+ timeFinal = <<dm.magnet.solve.numerical_parameters.rotating.number_of_periods_to_simulate>>/f; // Final time for source definition (s)
438
+ dt = 1 / (f*<<dm.magnet.solve.numerical_parameters.rotating.timesteps_per_period>>); // Time step (initial if adaptive) (s)
439
+ dt_max = dt; // Fixed maximum time step
440
+ dt_max_var[] = dt_max;
441
+ {% else %}
442
+ timeFinal = <<dm.magnet.solve.numerical_parameters.piecewise.time_to_simulate>>;
443
+
444
+ {% if dm.magnet.solve.numerical_parameters.piecewise.variable_max_timestep %}
445
+ times_max_timestep_piecewise_linear() = {<<dm.magnet.solve.numerical_parameters.piecewise.times_max_timestep_piecewise_linear|join(', ')>>};
446
+ max_timestep_piecewise_linear() = {<<dm.magnet.solve.numerical_parameters.piecewise.max_timestep_piecewise_linear|join(', ')>>};
447
+ dt = max_timestep_piecewise_linear(0);
448
+ dt_max_var[] = InterpolationLinear[Max[0,$Time]]{ListAlt[times_max_timestep_piecewise_linear(), max_timestep_piecewise_linear()]};
449
+ {% else %}
450
+ dt = timeFinal / <<dm.magnet.solve.numerical_parameters.piecewise.timesteps_per_time_to_simulate>>;
451
+ dt_max = dt; // Fixed maximum time step
452
+ dt_max_var[] = dt_max;
453
+ {% endif %}
454
+
455
+ {% if dm.magnet.solve.numerical_parameters.piecewise.force_stepping_at_times_piecewise_linear%}
456
+ control_time_instants_list() = {<<dm.magnet.solve.source_parameters.piecewise.times|join(', ')>>, 1e99}; // last one is just to avoid 'seg. fault' errors
457
+ {% endif %}
458
+
459
+ {% endif %}
460
+
461
+
462
+
463
+ iter_max = 60; // Maximum number of iterations (after which we exit the iterative loop)
464
+ extrapolationOrder = 1; // Extrapolation order for predictor
465
+ tol_energy = 1e-6; // Tolerance on the relative change of the power indicator
466
+ writeInterval = dt; // Time interval to save the solution [s]
467
+
468
+ // ------- SIMULATION NAME -------
469
+ name = "text_output";
470
+ resDirectory = StrCat["./",name];
471
+ infoResidualFile = StrCat[resDirectory,"/residual.txt"];
472
+ outputPower = StrCat[resDirectory,"/power.txt"]; // File updated during runtime
473
+ crashReportFile = StrCat[resDirectory,"/crash_report.txt"];
474
+ outputTemperature = StrCat[resDirectory,"/temperature.txt"];
475
+
476
+ {% if dm.magnet.solve.initial_conditions.init_type == 'pos_file' %}
477
+ 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[]
478
+ {% endif %}
479
+ }
480
+
481
+ Constraint {
482
+ { Name phi ;
483
+ Case {
484
+ // For natural boundary condition (in formulation) if dm.magnet.geometry.type == 'strand_only'
485
+ {Region ArbitraryPoints ; Value 0.0 ;} // Fix the magnetic potential to zero on the boundaries of the filaments and the outer matrix boundary
486
+ //{Region BndAir ; Type Assign ; Value XYZ[]*directionApplied[] ; TimeFunction hsVal[] * directionApplied[] ;} // Essential boundary condition (not compatible with transport current)
487
+ {% if dm.magnet.geometry.type == 'periodic_square' %}
488
+ // Link phi top to bottom
489
+ {
490
+ Region BndDomainBottom ;
491
+ Type Link ;
492
+ RegionRef BndDomainTop ;
493
+ Coefficient 1.0 ;
494
+ Function Vector[X[], Y[] + <<dm.magnet.geometry.air_radius*2>>, Z[]] ;
495
+ }
496
+ // Link phi left to right
497
+ {
498
+ Region BndDomainLeft ;
499
+ Type Link ;
500
+ RegionRef BndDomainRight ;
501
+ Coefficient 1.0 ;
502
+ Function Vector[X[] + <<dm.magnet.geometry.air_radius*2>>, Y[], Z[]] ;
503
+ }
504
+ {% endif %}
505
+ {% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
506
+ {Region Omega ; Type InitFromResolution ; NameOfResolution Projection_h_to_h ;}
507
+ {% endif %}
508
+ }
509
+ }
510
+ { Name Current ;
511
+ Case {
512
+ {% if dm.magnet.solve.formulation_parameters.formulation == "AI_uncoupled" %}
513
+ {Region Cuts ; Type Assign ; Value 0. ;} // For fully uncoupled filaments !without! transport current
514
+ {% endif %}
515
+ {% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
516
+ {Region Cuts ; Type InitFromResolution ; NameOfResolution Projection_h_to_h ;}
517
+ {% endif %}
518
+ }
519
+ }
520
+ { Name Current_t ;
521
+ Case {
522
+ {Region BndMatrixCut ; Type Assign ; Value 1.0 ; TimeFunction I_transport[] ;} // Contraint for the total transport current
523
+ }
524
+ }
525
+ { Name FieldCirculation ;
526
+ Case {
527
+ // impose a circulation of the h-field in omegaCC
528
+ // MAIN CHANGE: this one or the one below
529
+ // {Region CutAirVertical ; Type Assign ; Value <<dm.magnet.geometry.air_radius>>*2 ; TimeFunction CompX[hsVal[]] ;} // impose component in x direction
530
+ // {Region CutAirHorizontal ; Type Assign ; Value <<dm.magnet.geometry.air_radius>>*2 ; TimeFunction CompY[hsVal[]] ;} // impose component in y direction
531
+ }
532
+ }
533
+ { Name FluxChange ;
534
+ Case {
535
+ // impose the flux change connect through b in omegaCC
536
+ // MAIN CHANGE: this one or the one above (this one is for imposed flux, which maybe makes more sense physically)
537
+ {% if dm.magnet.geometry.type == 'periodic_square' %}
538
+ {Region CutAirHorizontal ; Type Assign ; Value -<<dm.magnet.geometry.air_radius>>*2 ; TimeFunction CompY[dbsdt[]] ;} // impose component in y direction
539
+ {Region CutAirVertical ; Type Assign ; Value -<<dm.magnet.geometry.air_radius>>*2 ; TimeFunction CompX[dbsdt[]] ;} // impose component in x direction
540
+ {% endif %}
541
+ }
542
+ }
543
+ { Name Voltage ; Case {} } // Empty to avoid warnings
544
+ { Name Voltage_t ; Case {} } // Empty to avoid warnings
545
+ { Name Current_plane ; Case {} } // Empty to avoid warnings
546
+ { Name Voltage_plane ;
547
+ Case {
548
+ // The constraint below can be useful for debugging (together with the Current one on Cuts, they uncouple the AI and TI problems in the linked-flux formulation)
549
+ //{Region filamentBnd_1_1 ; Type Assign ; Value 1. ;} // Put just one to non-zero voltage to see a non-trivial solution
550
+ //{Region BndFilaments ; Type Assign ; Value 0. ;} // All the other ones are forced to be zero
551
+ }
552
+ }
553
+ { Name Current_Cir ; Case {} } //
554
+ { Name Voltage_Cir ; Case {} } //
555
+
556
+ { Name v_plane ;
557
+ Case {
558
+ // {Region filamentBnd_1_1 ; Type Assign ; Value 1. ;}
559
+ // {Region BndFilaments ; Type Assign ; Value 0. ;}
560
+ {Region MatrixPointOnBoundary ; Type Assign ; Value 0. ;}
561
+ }
562
+ }
563
+
564
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
565
+ // As only the curl of the field is projected, the absolute value is unknown. We need to symmetrize the field
566
+ { Name hp_static ;
567
+ Case {
568
+ // {Region CenterPoint ; Value 0.0 ;} // replaced by a Lagrange multiplier in the formulation (more general, as there might be a filament at the center!)
569
+ }
570
+ }
571
+ {% endif %}
572
+
573
+ // This is the key constraint for coupling global quantities: it contains the links between the filaments
574
+ {Name ElectricalCircuit ; Type Network ;
575
+ Case circuit {
576
+ {% if len(rm.powered.Filaments.vol.numbers) % 6 == 1 %}
577
+ // A filament in the center is treated separately.
578
+ { Region Cut_0_0 ; Branch { 0, 0 } ; }
579
+ { Region filamentBnd_0_0 ; Branch {1000, 0} ; }
580
+ {% endif %}
581
+
582
+ {% if dm.magnet.solve.formulation_parameters.two_ell_periodicity %}
583
+ {% for layer in range(1, int( len(rm.powered.Filaments.vol.numbers)/6 ) + 1) %}
584
+ {% for filament in range(1, 4) %}
585
+ { Region Cut_<<layer>>_<<2*filament>> ; Branch { <<100*layer + 2*filament-1>>, <<100*layer + (2*filament+1)%6>> } ; }
586
+ { Region Cut_<<layer>>_<<(2*filament+1)%6>> ; Branch { <<100*layer + 2*filament>>, <<100*layer + (2*filament+1)%6+1>> } ; }
587
+
588
+ {% if dm.magnet.solve.diffusion_barriers.enable %}
589
+ { Region R_diffusion_barrier_<<layer>>_<<2*filament-1>> ; Branch { 1000, <<100*layer + 2*filament-1 + 10>>} ; }
590
+ { Region filamentBnd_<<layer>>_<<2*filament-1>> ; Branch {<<100*layer + 2*filament-1 + 10>>, <<100*layer + 2*filament-1>>} ; }
591
+
592
+ { Region R_diffusion_barrier_<<layer>>_<<2*filament>> ; Branch { 1000, <<100*layer + 2*filament + 10>>} ; }
593
+ { Region filamentBnd_<<layer>>_<<2*filament>> ; Branch {<<100*layer + 2*filament + 10>>, <<100*layer + 2*filament>>} ; }
594
+ {% else %}
595
+ { Region filamentBnd_<<layer>>_<<2*filament-1>> ; Branch {1000, <<100*layer + 2*filament-1>>} ; }
596
+ { Region filamentBnd_<<layer>>_<<2*filament>> ; Branch {1000, <<100*layer + 2*filament>>} ; }
597
+ {% endif %}
598
+ {%endfor%}
599
+ {%endfor%}
600
+
601
+ {% else %}
602
+ {% for layer in range(1, int( len(rm.powered.Filaments.vol.numbers)/6 ) + 1) %}
603
+ {% for filament in range(1, 7) %}
604
+ { Region Cut_<<layer>>_<<filament>> ; Branch { <<100*layer + filament>>, <<100*layer + filament%6+1>> } ; }
605
+ {% if dm.magnet.solve.diffusion_barriers.enable %}
606
+ { Region R_diffusion_barrier_<<layer>>_<<filament>> ; Branch { 1000, <<100*layer + filament + 10>>} ; }
607
+ { Region filamentBnd_<<layer>>_<<filament>> ; Branch {<<100*layer + filament + 10>>, <<100*layer + filament>>} ; }
608
+ {% else %}
609
+ { Region filamentBnd_<<layer>>_<<filament>> ; Branch {1000, <<100*layer + filament>>} ; }
610
+ {% endif %}
611
+ {%endfor%}
612
+ {%endfor%}
613
+ {% endif %}
614
+ }
615
+ }
616
+ { Name h ; Type Assign ;
617
+ Case {
618
+ {% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
619
+ {Region OmegaC ; Type InitFromResolution ; NameOfResolution Projection_h_to_h ;}
620
+ {% endif %}
621
+ }
622
+ }
623
+
624
+ }
625
+
626
+ FunctionSpace {
627
+ // Function space for magnetic field h in h-conform formulation. Main field for the magnetodynamic problem.
628
+ // h = sum phi_n * grad(psi_n) (nodes in Omega_CC with boundary)
629
+ // + sum h_e * psi_e (edges in Omega_C)
630
+ // + sum I_i * c_i (filament cuts - homology gmsh)
631
+ // + sum I_i2 * c_i2 (air cuts - manually computed)
632
+ { Name h_space; Type Form1;
633
+ BasisFunction {
634
+ { Name gradpsin; NameOfCoef phin; Function BF_GradNode;
635
+ Support OmegaCC_AndBnd; Entity NodesOf[OmegaCC]; } // Extend support to boundary for surface integration (e.g. useful for weak B.C.)
636
+ { Name gradpsin; NameOfCoef phin2; Function BF_GroupOfEdges;
637
+ Support OmegaC; Entity GroupsOfEdgesOnNodesOf[BndOmegaC]; } // To treat properly the Omega_CC-Omega_C boundary
638
+ { Name psie; NameOfCoef he; Function BF_Edge;
639
+ Support OmegaC_AndBnd; Entity EdgesOf[All, Not BndOmegaC]; }
640
+ { Name ci; NameOfCoef Ii; Function BF_GroupOfEdges;
641
+ Support Omega; Entity GroupsOfEdgesOf[Cuts]; } // The region Cuts contains the union of all the relevant cuts (cohomology basis function support)
642
+ { Name ct; NameOfCoef It; Function BF_GroupOfEdges;
643
+ Support Omega; Entity GroupsOfEdgesOf[BndMatrixCut]; } // The region Cuts contains the union of all the relevant cuts (cohomology basis function support)
644
+ {% if dm.magnet.geometry.type == 'periodic_square' %}
645
+ { Name ci2; NameOfCoef Ii2; Function BF_GradGroupOfNodes;
646
+ Support ElementsOf[OmegaCC, OnPositiveSideOf CutAirHorizontal];
647
+ Entity GroupsOfNodesOf[CutAirHorizontal]; }
648
+ { Name ci2; NameOfCoef Ii3; Function BF_GroupOfEdges;
649
+ Support OmegaC; Entity GroupsOfEdgesOf[CutAirHorizontal, InSupport CutAirHorizontalTL]; }
650
+ { Name ci3; NameOfCoef Ii4; Function BF_GradGroupOfNodes;
651
+ Support ElementsOf[OmegaCC, OnPositiveSideOf CutAirVertical];
652
+ Entity GroupsOfNodesOf[CutAirVertical]; }
653
+ { Name ci3; NameOfCoef Ii5; Function BF_GroupOfEdges;
654
+ Support OmegaC; Entity GroupsOfEdgesOf[CutAirVertical, InSupport CutAirVerticalTL]; }
655
+ {% endif %}
656
+ }
657
+ GlobalQuantity {
658
+ { Name I ; Type AliasOf ; NameOfCoef Ii ; }
659
+ { Name V ; Type AssociatedWith ; NameOfCoef Ii ; }
660
+ { Name It ; Type AliasOf ; NameOfCoef It ; }
661
+ { Name Vt ; Type AssociatedWith ; NameOfCoef It ; }
662
+ {% if dm.magnet.geometry.type == 'periodic_square'%}
663
+ { Name CirculationHorizontal ; Type AliasOf ; NameOfCoef Ii2 ; }
664
+ { Name FluxChangeHorizontal ; Type AssociatedWith ; NameOfCoef Ii2 ; }
665
+ { Name CirculationVertical ; Type AliasOf ; NameOfCoef Ii4 ; }
666
+ { Name FluxChangeVertical ; Type AssociatedWith ; NameOfCoef Ii4 ; }
667
+ {% endif %}
668
+ }
669
+ SubSpace {
670
+ { Name c ; NameOfBasisFunction ct ; }
671
+ }
672
+
673
+ Constraint {
674
+ { NameOfCoef he; EntityType EdgesOf; NameOfConstraint h; }
675
+ { NameOfCoef phin; EntityType NodesOf; NameOfConstraint phi; }
676
+ { NameOfCoef phin2; EntityType NodesOf; NameOfConstraint phi; }
677
+ { NameOfCoef Ii ;
678
+ EntityType GroupsOfEdgesOf ; NameOfConstraint Current ; }
679
+ { NameOfCoef It ;
680
+ EntityType GroupsOfEdgesOf ; NameOfConstraint Current_t ; }
681
+ {% if dm.magnet.geometry.type == 'periodic_square' %}
682
+ { NameOfCoef Ii2 ;
683
+ EntityType GroupsOfNodesOf ; NameOfConstraint FieldCirculation ; }
684
+ { NameOfCoef FluxChangeHorizontal ;
685
+ EntityType GroupsOfNodesOf ; NameOfConstraint FluxChange ; }
686
+ { NameOfCoef Ii4 ;
687
+ EntityType GroupsOfNodesOf ; NameOfConstraint FieldCirculation ; }
688
+ { NameOfCoef FluxChangeVertical ;
689
+ EntityType GroupsOfNodesOf ; NameOfConstraint FluxChange ; }
690
+ {% endif %}
691
+ { NameOfCoef V ;
692
+ EntityType GroupsOfNodesOf ; NameOfConstraint Voltage ; }
693
+ { NameOfCoef Vt ;
694
+ EntityType GroupsOfNodesOf ; NameOfConstraint Voltage_t ; }
695
+ }
696
+ }
697
+ // Function space for the transverse problem voltage field. Main field for the electrokinetics problem.
698
+ // The (transverse) coupling current derive from this voltage j_coupling = - sigma * grad(v).
699
+ { Name v_space_elKin ; Type Form0 ;
700
+ BasisFunction {
701
+ { Name vn ; NameOfCoef vn ; Function BF_Node ;
702
+ Support Matrix_partitions_for_TI ; Entity NodesOf[All, Not BndFilaments] ; }
703
+ { Name vi; NameOfCoef vi; Function BF_GroupOfNodes;
704
+ Support Matrix_partitions_for_TI; Entity GroupsOfNodesOf[BndFilaments]; }
705
+ {% if dm.magnet.solve.global_diffusion_barrier.enable %}
706
+ { Name vd ; NameOfCoef vd ; Function BF_Node ;
707
+ Support MatrixInternalSurface ; Entity NodesOf[GlobalDiffusionBarrier] ; }
708
+ { Name vd ; NameOfCoef vd2 ; Function BF_Node ;
709
+ Support GlobalDiffusionBarrier ; Entity NodesOf[GlobalDiffusionBarrier] ; } // Only the discontinuous contribution must be defined on the global diffusion barrier, and nothing else (the formulation relies on this).
710
+ {% endif %}
711
+ }
712
+ GlobalQuantity {
713
+ { Name V ; Type AliasOf ; NameOfCoef vi ; }
714
+ { Name I ; Type AssociatedWith ; NameOfCoef vi ; }
715
+ }
716
+ Constraint {
717
+ { NameOfCoef vn ; EntityType NodesOf ; NameOfConstraint v_plane ; }
718
+ { NameOfCoef V ;
719
+ EntityType Region ; NameOfConstraint Voltage_plane ; }
720
+ { NameOfCoef I ;
721
+ EntityType Region ; NameOfConstraint Current_plane ; }
722
+ }
723
+ }
724
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
725
+ // The curl of this space is the projection of the coupling current on this subspace (no net current from fil.)
726
+ { Name h_perp_space_static; Type Form1P;
727
+ BasisFunction {
728
+ { Name sn; NameOfCoef hn; Function BF_PerpendicularEdge;
729
+ Support Matrix_partitions_for_TI; Entity NodesOf[All]; }
730
+ }
731
+ Constraint {
732
+ { NameOfCoef hn; EntityType NodesOf; NameOfConstraint hp_static; }
733
+ }
734
+ }
735
+ // This is a Lagrange multplier for forcing the integral of hp_static to be zero in the matrix (avg field = 0)
736
+ { Name h_perp_space_static_lagrange; Type Vector;
737
+ BasisFunction {
738
+ { Name sn_lag; NameOfCoef hn_lag; Function BF_RegionZ;
739
+ Support Matrix_partitions_for_TI; Entity Matrix_partitions_for_TI; } // Ideally, should be only one function (not one per matrix part). To be double-checked!
740
+ }
741
+ }
742
+ // This space will correct the static space above with dynamic effects
743
+ { Name h_perp_space_dynamic; Type Form1P;
744
+ BasisFunction {
745
+ { Name sn; NameOfCoef hn; Function BF_PerpendicularEdge;
746
+ Support Matrix_partitions_for_TI; Entity NodesOf[All, Not TI_adjacent_region ]; }
747
+ }
748
+ }
749
+ {% endif %}
750
+
751
+ {% if dm.magnet.solve.diffusion_barriers.enable %}
752
+ // Function space for the circuit domain
753
+ { Name ElectricalCircuit; Type Scalar;
754
+ BasisFunction {
755
+ { Name sn; NameOfCoef Ir; Function BF_Region;
756
+ Support Resistors_diffusion_barrier; Entity Resistors_diffusion_barrier; }
757
+ }
758
+ GlobalQuantity {
759
+ { Name Iz ; Type AliasOf ; NameOfCoef Ir ; }
760
+ { Name Vz ; Type AssociatedWith ; NameOfCoef Ir ; }
761
+ }
762
+ Constraint {
763
+ { NameOfCoef Iz ; EntityType Region ; NameOfConstraint Current_Cir ; }
764
+ { NameOfCoef Vz ; EntityType Region ; NameOfConstraint Voltage_Cir ; }
765
+ }
766
+ }
767
+ {% endif %}
768
+ }
769
+
770
+ Jacobian {
771
+ { Name Vol ;
772
+ Case {
773
+ {Region All ; Jacobian Vol ;}
774
+ }
775
+ }
776
+ { Name Sur ;
777
+ Case {
778
+ { Region All ; Jacobian Sur ; }
779
+ }
780
+ }
781
+ }
782
+
783
+ Integration {
784
+ { Name Int ;
785
+ Case {
786
+ { Type Gauss ;
787
+ Case {
788
+ { GeoElement Point ; NumberOfPoints 1 ; }
789
+ { GeoElement Line ; NumberOfPoints 3 ; }
790
+ { GeoElement Triangle ; NumberOfPoints 3 ; }
791
+ }
792
+ }
793
+ }
794
+ }
795
+ }
796
+
797
+ Formulation{
798
+ // h-formulation
799
+ { Name MagDyn_hphi; Type FemEquation;
800
+ Quantity {
801
+ // Functions for the axial current (AI) problem
802
+ { Name h; Type Local; NameOfSpace h_space; }
803
+ { Name hp; Type Local; NameOfSpace h_space; }
804
+ { Name I; Type Global; NameOfSpace h_space[I]; }
805
+ { Name V; Type Global; NameOfSpace h_space[V]; }
806
+ { Name It; Type Global; NameOfSpace h_space[It]; }
807
+ { Name Vt; Type Global; NameOfSpace h_space[Vt]; }
808
+ { Name c; Type Local; NameOfSpace h_space[c]; }
809
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" %}
810
+ // Functions for the transverse current (TI) problem
811
+ { Name v; Type Local; NameOfSpace v_space_elKin; }
812
+ { Name Vp; Type Global; NameOfSpace v_space_elKin[V]; }
813
+ { Name Ip; Type Global; NameOfSpace v_space_elKin[I]; }
814
+ {% if dm.magnet.solve.diffusion_barriers.enable %}
815
+ { Name Iz; Type Global; NameOfSpace ElectricalCircuit[Iz]; }
816
+ { Name Vz; Type Global; NameOfSpace ElectricalCircuit[Vz]; }
817
+ {% endif %}
818
+ {% endif %}
819
+ {% if dm.magnet.geometry.type == 'periodic_square' %}
820
+ { Name CirculationHorizontal; Type Global; NameOfSpace h_space[CirculationHorizontal]; }
821
+ { Name FluxChangeHorizontal; Type Global; NameOfSpace h_space[FluxChangeHorizontal]; }
822
+ { Name CirculationVertical; Type Global; NameOfSpace h_space[CirculationVertical]; }
823
+ { Name FluxChangeVertical; Type Global; NameOfSpace h_space[FluxChangeVertical]; }
824
+ {% endif %}
825
+ }
826
+ Equation {
827
+ // --- AI problem ---
828
+ // Time derivative of b (NonMagnDomain)
829
+ Galerkin { [ ell* mu[] * Dof{h} / $DTime , {h} ];
830
+ In Omega; Integration Int; Jacobian Vol; }
831
+ Galerkin { [ - ell*mu[] * {h}[1] / $DTime , {h} ];
832
+ In Omega; Integration Int; Jacobian Vol; }
833
+ // Induced current (linear OmegaC)
834
+ Galerkin { [ ell*rho[mu0*Norm[{h}]] * Dof{d h} , {d h} ];
835
+ In LinOmegaC; Integration Int; Jacobian Vol; }
836
+ // Induced current (non-linear OmegaC)
837
+ Galerkin { [ ell*rho[{d h}, mu0*Norm[{h}]] * {d h} , {d h} ];
838
+ In NonLinOmegaC; Integration Int; Jacobian Vol; }
839
+
840
+ Galerkin { [ ell*dedj[{d h}, mu0*Norm[{h}]] * Dof{d h} , {d hp} ];
841
+ In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
842
+ Galerkin { [ - ell*dedj[{d h}, mu0*Norm[{h}]] * {d h} , {d hp} ];
843
+ In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
844
+
845
+ {% if 0 and dm.conductors[dm.magnet.solve.conductor_name].Jc_fit.type != 'Constant Jc' and dm.magnet.solve.general_parameters.superconductor_linear == False%}
846
+ // We don't use this N-R for the moment (does not seem to decrease number of iterations, but is expensive for assembly -> to double-check)
847
+ Galerkin { [ ell*dedb[{d h}, mu0*{h}] * mu0*Dof{h} , {d hp} ];
848
+ In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
849
+ Galerkin { [ -ell*dedb[{d h}, mu0*{h}] * mu0*{h} , {d hp} ];
850
+ In NonLinOmegaC; Integration Int; Jacobian Vol; } // For Newton-Raphson method
851
+ {% endif %}
852
+
853
+ // Natural boundary condition for normal flux density (useful when transport current is an essential condition)
854
+ {% if dm.magnet.geometry.type == 'coil' or dm.magnet.geometry.type == 'strand_only'%}
855
+ Galerkin { [ - ell*dbsdt[] * Normal[] , {dInv h} ];
856
+ In BndAir; Integration Int; Jacobian Sur; }
857
+ {% endif %}
858
+
859
+ // Global terms for imposing flux changes
860
+ {% if dm.magnet.geometry.type == 'periodic_square' %}
861
+ GlobalTerm { [ ell*Dof{FluxChangeHorizontal} , {CirculationHorizontal} ] ; In CutAirHorizontal ; }
862
+ GlobalTerm { [ ell*Dof{FluxChangeVertical} , {CirculationVertical} ] ; In CutAirVertical ; }
863
+ {% endif %}
864
+
865
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" %}
866
+ // --- TI problem ---
867
+ Galerkin { [ ell * sigma[mu0*Norm[{h}]] * Dof{d v} , {d v} ];
868
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; } // Matrix
869
+ {% if dm.magnet.solve.global_diffusion_barrier.enable %}
870
+ Galerkin { [ ell * 1/rho_contact[] * Dof{v} , {v} ];
871
+ In GlobalDiffusionBarrier; Integration Int; Jacobian Sur; } // This requires that only the discontinuous component of {v} is defined on the global diffusion barrier
872
+ {% endif %}
873
+ GlobalTerm { [ Dof{Ip} , {Vp} ] ; In BndFilaments ; }
874
+
875
+ {% if dm.magnet.solve.diffusion_barriers.enable %}
876
+ // Diffusion barriers
877
+ GlobalTerm{ [ Dof{Vz}, {Iz} ]; In Resistors_diffusion_barrier; }
878
+ GlobalTerm{ [ R[] * Dof{Iz}, {Iz} ]; In Resistors_diffusion_barrier; }
879
+ // --- Coupling between AI and TI problems via circuit equations ---
880
+ GlobalEquation {
881
+ Type Network ; NameOfConstraint ElectricalCircuit ;
882
+ { Node {I}; Loop {V}; Equation {V}; In Cuts ; }
883
+ { Node {Ip}; Loop {Vp}; Equation {Ip}; In BndFilaments ; }
884
+ { Node {Iz}; Loop {Vz}; Equation {Vz}; In Resistors_diffusion_barrier; }
885
+ }
886
+ {% else %}
887
+ // --- Coupling between AI and TI problems via circuit equations ---
888
+ GlobalEquation {
889
+ Type Network ; NameOfConstraint ElectricalCircuit ;
890
+ { Node {I}; Loop {V}; Equation {V}; In Cuts ; }
891
+ { Node {Ip}; Loop {Vp}; Equation {Ip}; In BndFilaments ; }
892
+ }
893
+ {% endif %}
894
+ {% endif %}
895
+ // Global term (these terms apparently must be placed at the end, it is not clear why)
896
+ GlobalTerm { [ Dof{V} , {I} ] ; In Cuts ; }
897
+ GlobalTerm { [ Dof{Vt} , {It} ] ; In BndMatrixCut ; }
898
+ }
899
+ }
900
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
901
+ // h-formulation
902
+ { Name MagDyn_hphi_dynCorr; Type FemEquation;
903
+ Quantity {
904
+ // Functions for the AI and TI problems, that are just used here (and not solved for)
905
+ { Name h; Type Local; NameOfSpace h_space; }
906
+ { Name v; Type Local; NameOfSpace v_space_elKin; }
907
+ // Functions for the dynamic correction of the TI problem
908
+ { Name hp_static; Type Local; NameOfSpace h_perp_space_static; }
909
+ { Name hp_static_lagrange; Type Local; NameOfSpace h_perp_space_static_lagrange; }
910
+ { Name hp_dynamic; Type Local; NameOfSpace h_perp_space_dynamic; }
911
+ }
912
+ Equation {
913
+ // --- Dynamic correction of the TI problem ---
914
+ // Projection of the static current flow on the curl of a "static" magnetic field
915
+ Galerkin { [ sigma[mu0*Norm[{h}]] * {d v} , {d hp_static} ]; // No DOF! Just take the solution of the previously solved v-based formulation
916
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
917
+ Galerkin { [ Dof{d hp_static} , {d hp_static} ];
918
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
919
+ Galerkin { [ Dof{hp_static_lagrange} , {hp_static} ];
920
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
921
+ Galerkin { [ Dof{hp_static} , {hp_static_lagrange} ];
922
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; } // ensure the magnetic field is averaged to zero over the matrix
923
+ // Introduce a dynamic component to the magnetic field
924
+ Galerkin { [ mu[] * Dof{hp_static} / $DTime , {hp_dynamic} ];
925
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
926
+ Galerkin { [ mu[] * Dof{hp_dynamic} / $DTime , {hp_dynamic} ];
927
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
928
+ Galerkin { [ - mu[] * {hp_static}[1] / $DTime , {hp_dynamic} ];
929
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
930
+ Galerkin { [ - mu[] * {hp_dynamic}[1] / $DTime , {hp_dynamic} ];
931
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; }
932
+ //Galerkin { [ - Dof{d v} , {d hp_dynamic} ];
933
+ // In Matrix; Integration Int; Jacobian Vol; } // Static field is curl-free so this contribution is unnecessary!
934
+ Galerkin { [ rho[mu0*Norm[{h}]] * Dof{d hp_dynamic} , {d hp_dynamic} ];
935
+ In Matrix_partitions_for_TI; Integration Int; Jacobian Vol; } // Only the dynamic correction is an eddy current
936
+ }
937
+ }
938
+ {% endif %}
939
+ {% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
940
+ // Projection formulation for initial condition
941
+ { Name Projection_h_to_h; Type FemEquation;
942
+ Quantity {
943
+ { Name h; Type Local; NameOfSpace h_space; }
944
+ }
945
+ Equation{
946
+ // 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).
947
+ // Validity of this to be checked again if we go to different meshes between the initial condition and the following simulation
948
+ Galerkin { [ Dof{h}, {h} ] ;
949
+ In Omega ; Jacobian Vol ; Integration Int ; }
950
+ {% if dm.magnet.solve.initial_conditions.init_type == 'pos_file' %}
951
+ Galerkin { [ - h_from_file[], {h} ] ;
952
+ In Omega ; Jacobian Vol ; Integration Int ; }
953
+ {% elif dm.magnet.solve.initial_conditions.init_type == 'uniform_field' %}
954
+ Galerkin { [ - hsVal[], {h} ] ;
955
+ In Omega ; Jacobian Vol ; Integration Int ; }
956
+ {% endif %}
957
+ }
958
+ }
959
+ {% endif %}
960
+ }
961
+
962
+ Macro CustomIterativeLoop
963
+ // Compute first solution guess and residual at step $TimeStep
964
+ Generate[A];
965
+ Solve[A]; Evaluate[ $syscount = $syscount + 1 ];
966
+ Generate[A]; GetResidual[A, $res0];
967
+ Evaluate[ $res = $res0 ];
968
+ Evaluate[ $iter = 0 ];
969
+ Evaluate[ $convCrit = 1e99 ];
970
+ PostOperation[MagDyn_energy];
971
+ Print[{$iter, $res, $res / $res0, $indicFilamentLoss},
972
+ Format "%g %14.12e %14.12e %14.12e", File infoResidualFile];
973
+ // ----- Enter the iterative loop (hand-made) -----
974
+ While[$convCrit > 1 && $res / $res0 <= 1e10 && $iter < iter_max]{
975
+ Solve[A]; Evaluate[ $syscount = $syscount + 1 ];
976
+ Generate[A]; GetResidual[A, $res];
977
+ Evaluate[ $iter = $iter + 1 ];
978
+ Evaluate[ $indicFilamentLossOld = $indicFilamentLoss];
979
+ PostOperation[MagDyn_energy];
980
+ Print[{$iter, $res, $res / $res0, $indicFilamentLoss},
981
+ Format "%g %14.12e %14.12e %14.12e", File infoResidualFile];
982
+ // Evaluate the convergence indicator
983
+ Evaluate[ $relChangeACLoss = Abs[($indicFilamentLossOld - $indicFilamentLoss)/((Abs[$indicFilamentLossOld]>1e-7 || $iter < 10) ? $indicFilamentLossOld:1e-7)] ];
984
+ Evaluate[ $convCrit = $relChangeACLoss/tol_energy];
985
+ }
986
+ Return
987
+
988
+ Resolution {
989
+ { Name MagDyn;
990
+ System {
991
+ {Name A; NameOfFormulation MagDyn_hphi;}
992
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
993
+ {Name A_dynCorr; NameOfFormulation MagDyn_hphi_dynCorr;}
994
+ {% endif %}
995
+ }
996
+ Operation {
997
+ // Initialize directories
998
+ CreateDirectory[resDirectory];
999
+ DeleteFile[outputPower];
1000
+ DeleteFile[infoResidualFile];
1001
+ // Initialize the solution (initial condition)
1002
+ SetTime[ timeStart ];
1003
+ SetDTime[ dt ];
1004
+ SetTimeStep[ 0 ];
1005
+ InitSolution[A];
1006
+ SaveSolution[A]; // Saves the solution x (from Ax = B) to .res file
1007
+ Evaluate[ $syscount = 0 ];
1008
+ Evaluate[ $saved = 1 ];
1009
+ Evaluate[ $elapsedCTI = 1 ]; // Number of control time instants already treated
1010
+ Evaluate[ $isCTI = 0 ];
1011
+
1012
+ {% if dm.magnet.solve.formulation_parameters.compute_temperature %}
1013
+ Evaluate[ $cumulative_temperature = 0 ];
1014
+ Evaluate[ $dT_old = 0 ];
1015
+ {% endif %}
1016
+
1017
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
1018
+ InitSolution[A_dynCorr];
1019
+ SaveSolution[A_dynCorr];
1020
+ {% else %}
1021
+ Evaluate[ $indicTotalLoss_dyn = 0 ]; // Put it to zero to avoid warnings
1022
+ Evaluate[ $indicCouplingLoss_dyn = 0 ]; // Put it to zero to avoid warnings
1023
+ {% if dm.magnet.solve.formulation_parameters.formulation != "CATI" %}
1024
+ Evaluate[ $indicCouplingLoss = 0 ]; // For conventional 2D formulation, no coupling currents and hence no coupling loss
1025
+ {% endif %}
1026
+ {% endif %}
1027
+ // ----- Enter implicit Euler time integration loop (hand-made) -----
1028
+ // Avoid too close steps at the end. Stop the simulation if the step becomes ridiculously small
1029
+ SetExtrapolationOrder[ extrapolationOrder ];
1030
+ While[$Time < timeFinal] {
1031
+ SetTime[ $Time + $DTime ]; // Time instant at which we are looking for the solution
1032
+ SetTimeStep[ $TimeStep + 1 ];
1033
+ {% if dm.magnet.solve.numerical_parameters.piecewise.force_stepping_at_times_piecewise_linear and dm.magnet.solve.source_parameters.source_type == 'piecewise'%}
1034
+ // Make sure all CTI are exactly chosen
1035
+ Evaluate[ $isCTI = 0 ];
1036
+ Test[$Time >= time_multiplier*AtIndex[$elapsedCTI]{List[control_time_instants_list]} - 1e-7 ]{
1037
+ Evaluate[ $isCTI = 1, $prevDTime = $DTime ]; // Also save the previous time step to restart from it after the CTI
1038
+ SetDTime[ time_multiplier*AtIndex[$elapsedCTI]{List[control_time_instants_list]} - $Time + $DTime ];
1039
+ SetTime[ time_multiplier*AtIndex[$elapsedCTI]{List[control_time_instants_list]} ]; // To compute exactly at the asked time instant
1040
+ Print[{$Time}, Format "*** Control time instant: %g s."];
1041
+ }
1042
+ {% endif %}
1043
+ // Iterative loop defined as a macro above
1044
+ Print[{$Time, $DTime, $TimeStep}, Format "Start new time step. Time: %g s. Time step: %g s. Step: %g."];
1045
+ Call CustomIterativeLoop;
1046
+ // Has it converged? If yes, save solution and possibly increase the time step...
1047
+ Test[ $iter < iter_max && ($res / $res0 <= 1e10 || $res0 == 0)]{
1048
+ Print[{$Time, $DTime, $iter}, Format "Converged time %g s with time step %g s in %g iterations."];
1049
+ // Save the solution of few time steps (small correction to avoid bad rounding)
1050
+ // Test[ $Time >= $saved * writeInterval - 1e-7 || $Time + $DTime >= timeFinal]{
1051
+ Test[ 1 ]{
1052
+ // Test[ $Time >= $saved * $DTime - 1e-7 || $Time + $DTime >= timeFinal]{
1053
+ SaveSolution[A];
1054
+ // Post-operation for power loss quantities
1055
+ PostOperation[test_Losses];
1056
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
1057
+ Generate[A_dynCorr]; Solve[A_dynCorr]; SaveSolution[A_dynCorr];
1058
+ PostOperation[test_Losses_dynCorr];
1059
+ {% endif %}
1060
+ Print[{$Time, $saved}, Format "Saved time %g s (saved solution number %g). Output power infos:"];
1061
+ Print[{$Time, $indicFilamentLoss, $indicCouplingLoss, $indicEddyLoss, $indicTotalLoss, $indicCouplingLoss_dyn, $indicTotalLoss_dyn},
1062
+ Format "%g %14.12e %14.12e %14.12e %14.12e %14.12e %14.12e", File outputPower];
1063
+
1064
+ // Compute the temperature
1065
+ {% if dm.magnet.solve.formulation_parameters.compute_temperature == True %}
1066
+ PostOperation[heat_capacity];
1067
+ Evaluate[$dT = $DTime * $indicTotalLoss/$heat_capacity_per_unit_length];
1068
+ Evaluate[ $cumulative_temperature = $cumulative_temperature + ($dT_old + $dT)/2 ];
1069
+ Print[{$Time, $dT, $cumulative_temperature}, Format "%g %14.12e %14.12e", File outputTemperature];
1070
+ Evaluate[ $dT_old = $dT ];
1071
+ {% endif %}
1072
+
1073
+ Evaluate[$saved = $saved + 1];
1074
+ }
1075
+ {% if dm.magnet.solve.numerical_parameters.piecewise.force_stepping_at_times_piecewise_linear and dm.magnet.solve.source_parameters.source_type == 'piecewise' %}
1076
+ // Consider the time step before the control time instant (if relevant) and increment $elapsedCTI
1077
+ Test[ $isCTI == 1 ]{
1078
+ Evaluate[ $elapsedCTI = $elapsedCTI + 1 ];
1079
+ SetDTime[ $prevDTime ];
1080
+ }
1081
+ {% endif %}
1082
+ // Increase the step if we converged sufficiently "fast" (and not a control time instant)
1083
+ Test[ $iter < iter_max / 4 && $DTime < dt_max_var[] && $isCTI == 0 ]{
1084
+ Evaluate[ $dt_new = Min[$DTime * 2, dt_max_var[]] ];
1085
+ Print[{$dt_new}, Format "*** Fast convergence: increasing time step to %g"];
1086
+ SetDTime[$dt_new];
1087
+ }
1088
+ Test[ $DTime > dt_max_var[]]{
1089
+ Evaluate[ $dt_new = dt_max_var[] ];
1090
+ Print[{$dt_new}, Format "*** Variable maximum time-stepping: reducing time step to %g"];
1091
+ SetDTime[$dt_new];
1092
+ }
1093
+ }
1094
+ // ...otherwise, reduce the time step and try again
1095
+ {
1096
+ Evaluate[ $dt_new = $DTime / 2 ];
1097
+ Print[{$iter, $dt_new},
1098
+ Format "*** Non convergence (iter %g): recomputing with reduced step %g"];
1099
+ RemoveLastSolution[A];
1100
+ SetTime[$Time - $DTime];
1101
+ SetTimeStep[$TimeStep - 1];
1102
+ SetDTime[$dt_new];
1103
+ // If it gets ridicoulously small, end the simulation, and report the information in crash file.
1104
+ Test[ $dt_new < dt_max_var[]/1000000 ]{
1105
+ Print[{$iter, $dt_new, $Time},
1106
+ Format "*** Non convergence (iter %g): time step %g too small, stopping the simulation at time %g s.", File crashReportFile];
1107
+ // Print[A];
1108
+ Exit;
1109
+ }
1110
+ }
1111
+ } // ----- End time loop -----
1112
+ // Print information about the resolution and the nonlinear iterations
1113
+ Print[{$syscount}, Format "Total number of linear systems solved: %g"];
1114
+ }
1115
+ }
1116
+ {% if dm.magnet.solve.initial_conditions.init_type != 'virgin' %}
1117
+ { Name Projection_h_to_h;
1118
+ System {
1119
+ {Name Projection_h_to_h; NameOfFormulation Projection_h_to_h; DestinationSystem A ;}
1120
+ }
1121
+ Operation {
1122
+ {% if dm.magnet.solve.initial_conditions.init_type == 'pos_file' %}
1123
+ 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)
1124
+ {% endif %}
1125
+ Generate[Projection_h_to_h]; Solve[Projection_h_to_h];
1126
+ TransferSolution[Projection_h_to_h];
1127
+ }
1128
+ }
1129
+ {% endif %}
1130
+ }
1131
+
1132
+ PostProcessing {
1133
+ { Name MagDyn_hphi; NameOfFormulation MagDyn_hphi;
1134
+ Quantity {
1135
+ { Name phi; Value{ Local{ [ {dInv h} ] ;
1136
+ In OmegaCC_AndBnd; Jacobian Vol; } } }
1137
+ { Name h; Value{ Local{ [ {h} ] ;
1138
+ In Omega; Jacobian Vol; } } }
1139
+ { Name b; Value{ Local{ [ mu[] * {h} ] ;
1140
+ In Omega; Jacobian Vol; } } }
1141
+ { Name b_reaction; Value{ Local{ [ mu[] * ({h} - hsVal[]) ] ;
1142
+ In Omega; Jacobian Vol; } } }
1143
+ { Name j; Value{ Local{ [ {d h} ] ;
1144
+ In OmegaC; Jacobian Vol; } } }
1145
+ { Name jz; Value{ Local{ [ {d h} * Vector[0,0,1]] ;
1146
+ In OmegaC; Jacobian Vol; } } }
1147
+ { Name jc; Value{ Local{ [ jc[mu0*Norm[{h}]] ] ;
1148
+ In NonLinOmegaC; Jacobian Vol; } } }
1149
+ { Name rho; Value{ Local{ [ rho_power[{d h}, mu0*Norm[{h}]] ] ;
1150
+ In NonLinOmegaC; Jacobian Vol; } } }
1151
+ { Name power_filaments; Value{ Local{ [ rho[{d h}, mu0*Norm[{h}]] * {d h} * {d h} ] ; // j*e : Power (only for filaments)
1152
+ In NonLinOmegaC; Jacobian Vol; } } }
1153
+ { Name selffield_dfluxdt; Value{ Integral{ [ CompZ[rho[{d h}, mu0*Norm[{h}]] * {d h}] ] ;
1154
+ In OmegaC; Integration Int; Jacobian Vol; } } }
1155
+ { Name sigma_matrix; Value{ Local{ [ sigma[mu0*Norm[{h}]]] ;
1156
+ In Matrix; Jacobian Vol; } } }
1157
+ { Name flux_external; Value{ Integral{ [ (mu[] * {h} * {c} / I_transport[]) ] ;
1158
+ In OmegaCC; Integration Int ; Jacobian Vol; } } }
1159
+ { Name flux_internal; Value{ Integral{ [ mu[] * {h} * (UnitVectorZ[] /\ XYZ[]/Norm[XYZ[]]) / (2 * Pi * Norm[XYZ[]]) ] ;
1160
+ In OmegaC; Integration Int; Jacobian Vol; } } }
1161
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" %}
1162
+ // TI model quantities
1163
+ { Name j_plane; Value{ Local{ [ -1/rho[mu0*Norm[{h}]] * {d v} ] ;
1164
+ In Matrix; Jacobian Vol; } } }
1165
+ { Name v_plane; Value{ Local{ [ {v} ] ;
1166
+ In Matrix; Jacobian Vol; } } }
1167
+ { Name Ip; Value { Term{ [ {Ip} ] ; In BndFilaments; } } }
1168
+ { Name Vp; Value { Term{ [ {Vp} ] ; In BndFilaments; } } }
1169
+ { Name power_matrix;
1170
+ Value{
1171
+ Local{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ; // j*e = rho*j^2 in matrix (eddy)
1172
+ In Matrix ; Integration Int ; Jacobian Vol; }
1173
+ Local{ [ (sigma[mu0*Norm[{h}]] * {d v}) * {d v}] ; // j*e = sigma*e^2 in matrix (coupling)
1174
+ In Matrix ; Integration Int ; Jacobian Vol; }
1175
+ {% if dm.magnet.solve.global_diffusion_barrier.enable %}
1176
+ Local { [ 1/rho_contact[] * {v} * {v} ];
1177
+ In GlobalDiffusionBarrier; Integration Int; Jacobian Sur; } // This requires that only the discontinuous component of {v} is defined on the diffusion barrier
1178
+ {% endif %}
1179
+ }
1180
+ }
1181
+ { Name couplingLoss; // Does not include (global) contribution from possible diffusion barriers around filaments
1182
+ Value{
1183
+ Integral{ [ (sigma[mu0*Norm[{h}]] * {d v}) * {d v}] ; // j*e = sigma*e^2 in matrix
1184
+ In Matrix ; Integration Int ; Jacobian Vol; }
1185
+ {% if dm.magnet.solve.global_diffusion_barrier.enable %}
1186
+ Integral { [ 1/rho_contact[] * {v} * {v} ];
1187
+ In GlobalDiffusionBarrier; Integration Int; Jacobian Sur; } // This requires that only the discontinuous component of {v} is defined on the diffusion barrier
1188
+ {% endif %}
1189
+ }
1190
+ }
1191
+ {% if dm.magnet.solve.diffusion_barriers.enable %}
1192
+ { Name diffusion_barrier_loss; // Loss per unit length (W/m)
1193
+ Value{
1194
+ Term{ [ 1/ell * R[] * {Iz} * {Iz}] ; // P = 1/ell * R*I^2 in diffusion barriers
1195
+ In Resistors_diffusion_barrier ; }
1196
+ }
1197
+ }
1198
+ {% endif %}
1199
+ {% endif %}
1200
+ { Name eddyLoss; // NEW. Eddyloss was computed as totalLoss[matrix], which combines eddy and couplingLoss
1201
+ Value{
1202
+ Integral{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ; // EddyLoss = rho*j^2 in matrix
1203
+ In Matrix ; Integration Int ; Jacobian Vol; }
1204
+ }
1205
+ }
1206
+ { Name totalLoss; // Does not include (global) contribution from possible diffusion barriers around filaments
1207
+ Value{
1208
+ // Separate OmegaC into Matrix and nonlinear (resistivities take different argument types)
1209
+ Integral{ [rho[{d h}, mu0*Norm[{h}]] * {d h} * {d h}] ; // j*e = rho*j^2 (filaments)
1210
+ In NonLinOmegaC ; Integration Int ; Jacobian Vol; }
1211
+ Integral{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ; // j*e = rho*j^2 (eddy)
1212
+ In Matrix ; Integration Int ; Jacobian Vol; }
1213
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" %}
1214
+ Integral{ [ (sigma[mu0*Norm[{h}]]*{d v}) * {d v}] ; // j*e = sigma*e^2 in matrix (coupling)
1215
+ In Matrix ; Integration Int ; Jacobian Vol; }
1216
+ {% if dm.magnet.solve.global_diffusion_barrier.enable %}
1217
+ Integral { [ 1/rho_contact[] * {v} * {v} ];
1218
+ In GlobalDiffusionBarrier; Integration Int; Jacobian Sur; } // This requires that only the discontinuous component of {v} is defined on the diffusion barrier
1219
+ {% endif %}
1220
+ {% endif %}
1221
+ }
1222
+ }
1223
+ { Name heat_capacity;
1224
+ Value{
1225
+ Integral{ [(filament_Cv[T[] + $cumulative_temperature, mu0*Norm[{h}]] )] ; // j*e = rho*j^2 in filaments (?)
1226
+ In NonLinOmegaC ; Integration Int ; Jacobian Vol; }
1227
+ Integral{ [(matrix_Cv[T[] + $cumulative_temperature]) ] ; // j*e = rho*j^2 in filaments (?)
1228
+ In NonLinOmegaC ; Integration Int ; Jacobian Vol; }
1229
+ }
1230
+ }
1231
+ { Name I; Value { Term{ [ {I} ] ; In Cuts; } } }
1232
+ { Name V; Value { Term{ [ {V} ] ; In Cuts; } } }
1233
+ { Name V_unitlen; Value { Term{ [ {V} / ell ] ; In Cuts; } } }
1234
+ { Name It; Value { Term{ [ {It} ] ; In BndMatrixCut; } } }
1235
+ { Name Vt; Value { Term{ [ {Vt} ] ; In BndMatrixCut; } } }
1236
+ { Name Vt_unitlen; Value { Term{ [ {Vt} / ell ] ; In BndMatrixCut; } } }
1237
+ { Name I_integral;
1238
+ Value{
1239
+ Integral{ [ {d h} * Vector[0,0,1]] ;
1240
+ In OmegaC ; Integration Int ; Jacobian Vol; }
1241
+ }
1242
+ }
1243
+ { Name I_abs_integral;
1244
+ Value{
1245
+ Integral{ [ Fabs[{d h} * Vector[0,0,1]]] ;
1246
+ In OmegaC ; Integration Int ; Jacobian Vol; }
1247
+ }
1248
+ }
1249
+ { Name area;
1250
+ Value{
1251
+ Integral{ [ 1 ] ;
1252
+ In Omega ; Integration Int ; Jacobian Vol; }
1253
+ }
1254
+ }
1255
+ // Applied field (useful for magnetization plots)
1256
+ { Name hsVal; Value{ Term { [ hsVal[] ]; In Omega; } } }
1257
+ // Magnetization: integral of 1/2 * (r /\ j) in a conducting (sub-)domain
1258
+ { Name magnetization; Value{ Integral{ [ 0.5 * XYZ[] /\ {d h} ] ;
1259
+ In OmegaC; Integration Int; Jacobian Vol; } } }
1260
+ // Magnetic energy
1261
+ { Name magnetic_energy; Value{ Integral{ [ 0.5 * mu[] * {h} * {h} ] ;
1262
+ In Omega; Integration Int; Jacobian Vol; } } }
1263
+ { Name b_integral; Value{ Integral{ [ mu[] * {h} ] ;
1264
+ In Omega; Integration Int; Jacobian Vol; } } }
1265
+ }
1266
+ }
1267
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
1268
+ { Name MagDyn_hphi_dynCorr; NameOfFormulation MagDyn_hphi_dynCorr;
1269
+ Quantity {
1270
+ { Name hp_static; Value{ Local{ [ {hp_static} ] ;
1271
+ In Matrix_partitions_for_TI; Jacobian Vol; } } }
1272
+ { Name hp_static_lagrange; Value{ Local{ [ {hp_static_lagrange} ] ;
1273
+ In Matrix_partitions_for_TI; Jacobian Vol; } } }
1274
+ { Name j_static; Value{ Local{ [ {d hp_static} ] ;
1275
+ In Matrix_partitions_for_TI; Jacobian Vol; } } }
1276
+ { Name hp_dynamic; Value{ Local{ [ {hp_dynamic} ] ;
1277
+ In Matrix_partitions_for_TI; Jacobian Vol; } } }
1278
+ { Name j_dynamic; Value{ Local{ [ {d hp_dynamic} ] ;
1279
+ In Matrix_partitions_for_TI; Jacobian Vol; } } }
1280
+ { Name j_stadyn; Value{ Local{ [ {d hp_static} + {d hp_dynamic} ] ;
1281
+ In Matrix_partitions_for_TI; Jacobian Vol; } } }
1282
+ { Name j_stadyn_mixed; Value{ Local{ [ - sigma[mu0*Norm[{h}]] * {d v} + {d hp_dynamic} ] ;
1283
+ In Matrix_partitions_for_TI; Jacobian Vol; } } }
1284
+ { Name totalLoss_dyn; // Does not include (global) contribution from possible diffusion barriers around filaments
1285
+ Value{
1286
+ // Integral{ [rho[{d h}, mu0*Norm[{h}]] * {d h} * {d h}] ;
1287
+ // In OmegaC ; Integration Int ; Jacobian Vol; }
1288
+ // NEW separate OmegaC into Matrix and nonlinear
1289
+ Integral{ [rho[{d h}, mu0*Norm[{h}]] * {d h} * {d h}] ; // j*e = rho*j^2 in filaments
1290
+ In NonLinOmegaC ; Integration Int ; Jacobian Vol; }
1291
+ Integral{ [rho[mu0*Norm[{h}]] * {d h} * {d h}] ; // Eddy
1292
+ In Matrix ; Integration Int ; Jacobian Vol; }
1293
+ Integral{ [ (- sigma[mu0*Norm[{h}]]*{d v} + {d hp_dynamic}) * (- {d v} + rho[mu0*Norm[{h}]] * {d hp_dynamic})] ;
1294
+ In Matrix_partitions_for_TI ; Integration Int ; Jacobian Vol; } // Attention to signs!
1295
+ {% if dm.magnet.solve.global_diffusion_barrier.enable %}
1296
+ Integral { [ 1/rho_contact[] * {v} * {v} ];
1297
+ In GlobalDiffusionBarrier; Integration Int; Jacobian Sur; } // This requires that only the discontinuous component of {v} is defined on the diffusion barrier
1298
+ {% endif %}
1299
+ }
1300
+ }
1301
+ { Name couplingLoss_dyn; // Does not include (global) contribution from possible diffusion barriers around filaments
1302
+ Value{
1303
+ Integral{ [(- sigma[mu0*Norm[{h}]]*{d v} + {d hp_dynamic}) * (- {d v} + rho[mu0*Norm[{h}]] * {d hp_dynamic})] ;
1304
+ In Matrix_partitions_for_TI ; Integration Int ; Jacobian Vol; } // Attention to signs!
1305
+ {% if dm.magnet.solve.global_diffusion_barrier.enable %}
1306
+ Integral { [ 1/rho_contact[] * {v} * {v} ];
1307
+ In GlobalDiffusionBarrier; Integration Int; Jacobian Sur; } // This requires that only the discontinuous component of {v} is defined on the diffusion barrier
1308
+ {% endif %}
1309
+ }
1310
+ }
1311
+ }
1312
+ }
1313
+ {% endif %}
1314
+ }
1315
+
1316
+ PostOperation {
1317
+ { Name MagDyn;
1318
+ NameOfPostProcessing MagDyn_hphi;
1319
+ Operation {
1320
+ {% set units_dict = {
1321
+ "b": "T",
1322
+ "b_reaction": "T",
1323
+ "h": "A/m",
1324
+ "hsVal": "A/m",
1325
+ "phi": "A",
1326
+ "power_filaments": "W",
1327
+ "power_matrix": "W",
1328
+ "j": "A/m2",
1329
+ "jz": "A/m2",
1330
+ "jc": "A/m2",
1331
+ "jPlane": "A/m2",
1332
+ "vPlane": "V/m",
1333
+ "sigma_matrix": "S/m",
1334
+ "j_dynamic": "A/m2",
1335
+ "j_stadyn_mixed": "A/m2",
1336
+ "hp_dynamic": "A/m",
1337
+ "hp_static": "A/m",
1338
+ "hp_static_lagrange": "A/m",
1339
+ } %}
1340
+ // Print[ phi, OnElementsOf OmegaCC , File StrCat["phi_f.pos"], Name "phi [A]" ];
1341
+ // Print[ b_reaction, OnElementsOf Omega , File StrCat["br.pos"], Name "br [T]" ];
1342
+ // Print[ j, OnElementsOf OmegaC , File StrCat["j.pos"], Name "j [A/m2]" ];
1343
+ // Print[ jz, OnElementsOf OmegaC , File StrCat["jz.pos"], Name "jz [A/m2]" ];
1344
+ //Print[ jc, OnElementsOf NonLinOmegaC , File StrCat["jc.pos"], Name "jc [A/m2]" ];
1345
+ //Print[ rho, OnElementsOf NonLinOmegaC , File StrCat["rho.pos"], Name "rho [Ohm.m]" ];
1346
+ // Print[ j_plane, OnElementsOf Matrix , File StrCat["jPlane.pos"], Name "j_plane [A/m2]" ];
1347
+ // Print[ v_plane, OnElementsOf Matrix , File StrCat["vPlane.pos"], Name "v_plane [V/m]" ];
1348
+ // Print[ power_filaments, OnElementsOf NonLinOmegaC , File StrCat["powFil_f.pos"], Name "powerFilaments [W]" ];
1349
+ // Print[ power_matrix, OnElementsOf Matrix , File StrCat["powMat_f.pos"], Name "powerMatrix [W]" ];
1350
+ // Print[ sigma_matrix, OnElementsOf Matrix , File StrCat["sigmaMat_f.pos"], Name "sigmaMatrix [S/m]" ];
1351
+ // Print[ b, OnElementsOf Omega , File StrCat["b.pos"], Name "b [T]" ];
1352
+ // Global solutions
1353
+ Print[ b_integral[Omega], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/b_integral.txt"]];
1354
+ Print[ It, OnRegion BndMatrixCut, File StrCat[resDirectory,"/I_transport.txt"], Format SimpleTable];
1355
+ Print[ Vt, OnRegion BndMatrixCut, File StrCat[resDirectory,"/V_transport.txt"], Format SimpleTable];
1356
+ Print[ Vt_unitlen, OnRegion BndMatrixCut, File StrCat[resDirectory,"/V_transport_unitlen.txt"], Format SimpleTable];
1357
+ Print[ hsVal[Omega], OnRegion Matrix, Format TimeTable, File StrCat[resDirectory, "/hs_val.txt"]];
1358
+ Print[ area[NonLinOmegaC], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/area_sc_filaments.txt"]];
1359
+ Print[ area[Filament_holes], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/area_filament_holes.txt"]];
1360
+ Print[ area[Matrix], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/area_matrix.txt"]];
1361
+ {% for i, matrix_partition_material in zip(range(len(rm.induced.Matrix.vol.numbers)), rm.induced.Matrix.vol.names)%}
1362
+ Print[ area[Matrix_<<i>>], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/area_matrix_<<i>>.txt"]];
1363
+ {%endfor%}
1364
+ Print[ magnetization[Filaments_SC], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/magn_fil.txt"]];
1365
+ Print[ magnetization[Matrix], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/magn_matrix.txt"]];
1366
+ Print[ magnetization[OmegaC], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/magn_total.txt"]];
1367
+ Print[ flux_external[OmegaCC], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/flux_external.txt"]];
1368
+ Print[ flux_internal[OmegaC], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/flux_internal.txt"]];
1369
+ Print[ magnetic_energy[OmegaC], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/magnetic_energy_internal.txt"]];
1370
+ Print[ magnetic_energy[OmegaCC], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/magnetic_energy_external.txt"]];
1371
+ Print[ selffield_dfluxdt[Filaments_SC], OnGlobal, Format TimeTable, File StrCat[resDirectory, "/selffield_dfluxdt.txt"]];
1372
+ // Local field solutions
1373
+ {% for quantity, region in zip(dm.magnet.postproc.pos_files.quantities, dm.magnet.postproc.pos_files.regions) %}
1374
+ Print[ <<quantity>>, OnElementsOf <<region>> , File StrCat["<<quantity>>_<<region>>.pos"], Name "<<quantity>> [<<units_dict[quantity]>>]" ];
1375
+ {% endfor %}
1376
+ {% if dm.magnet.postproc.compute_current_per_filament == True %}
1377
+ // Integrals of local quantities
1378
+ Print[I_integral[filament_1_1], OnGlobal, File StrCat[resDirectory,"/I_integral.txt"], Format SimpleTable];
1379
+ For i In {1:number_of_layers}
1380
+ For j In {((i==1)?2:1):6}
1381
+ Print[I_integral[filament~{i}~{j}], OnGlobal, File > StrCat[resDirectory,"/I_integral.txt"], Format SimpleTable];
1382
+ EndFor
1383
+ EndFor
1384
+ Print[I_abs_integral[filament_1_1], OnGlobal, File StrCat[resDirectory,"/I_abs_integral.txt"], Format SimpleTable];
1385
+ For i In {1:number_of_layers}
1386
+ For j In {((i==1)?2:1):6}
1387
+ Print[I_abs_integral[filament~{i}~{j}], OnGlobal, File > StrCat[resDirectory,"/I_abs_integral.txt"], Format SimpleTable];
1388
+ EndFor
1389
+ EndFor
1390
+ // Global quantities (axial current problem)
1391
+ Print[I, OnRegion Cut_1_1, File StrCat[resDirectory,"/I.txt"], Format SimpleTable];
1392
+ For i In {1:number_of_layers}
1393
+ For j In {((i==1)?2:1):6}
1394
+ Print[I, OnRegion Cut~{i}~{j}, File > StrCat[resDirectory,"/I.txt"], Format SimpleTable];
1395
+ EndFor
1396
+ EndFor
1397
+ Print[V, OnRegion Cut_1_1, File StrCat[resDirectory,"/V.txt"], Format SimpleTable];
1398
+ For i In {1:number_of_layers}
1399
+ For j In {((i==1)?2:1):6}
1400
+ Print[V, OnRegion Cut~{i}~{j}, File > StrCat[resDirectory,"/V.txt"], Format SimpleTable];
1401
+ EndFor
1402
+ EndFor
1403
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" %}
1404
+ // Global quantities (transverse current problem)
1405
+ Print[Ip, OnRegion filamentBnd_1_1, File StrCat[resDirectory,"/Ip.txt"], Format SimpleTable];
1406
+ For i In {1:number_of_layers}
1407
+ For j In {((i==1)?2:1):6}
1408
+ Print[Ip, OnRegion filamentBnd~{i}~{j}, File > StrCat[resDirectory,"/Ip.txt"], Format SimpleTable];
1409
+ EndFor
1410
+ EndFor
1411
+ Print[Vp, OnRegion filamentBnd_1_1, File StrCat[resDirectory,"/Vp.txt"], Format SimpleTable];
1412
+ For i In {1:number_of_layers}
1413
+ For j In {((i==1)?2:1):6}
1414
+ Print[Vp, OnRegion filamentBnd~{i}~{j}, File > StrCat[resDirectory,"/Vp.txt"], Format SimpleTable];
1415
+ EndFor
1416
+ EndFor
1417
+ {% endif %}
1418
+ {% endif %}
1419
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.diffusion_barriers.enable %}
1420
+ // Gives loss per unit length, first column for time, then one column per filament boundary
1421
+ Print[ diffusion_barrier_loss, OnRegion Resistors_diffusion_barrier, Format TimeTable, File StrCat[resDirectory, "/power_diffusion_barrier.txt"]];
1422
+ {% endif %}
1423
+
1424
+ // Last magnetic field solution for projection. Always saved. Note the special format GmshParsed required for proper GmshRead[] operation in the later pre-resolution.
1425
+ Print[ h, OnElementsOf Omega, Format GmshParsed , File "last_magnetic_field.pos", Name "h [A/m]", LastTimeStepOnly ];
1426
+ }
1427
+ }
1428
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
1429
+ { Name MagDyn_dynCorr;
1430
+ NameOfPostProcessing MagDyn_hphi_dynCorr;
1431
+ Operation {
1432
+ // Print[ j_dynamic, OnElementsOf Matrix_partitions_for_TI , File StrCat["j_dynamic.pos"], Name "j_dynamic [A/m2]" ];
1433
+ // Print[ j_stadyn_mixed, OnElementsOf Matrix_partitions_for_TI , File StrCat["j_stadyn_mixed.pos"], Name "j_stadyn_mixed [A/m2]" ];
1434
+ // Print[ hp_dynamic, OnElementsOf Matrix_partitions_for_TI , File StrCat["hp_dynamic.pos"], Name "hp_dynamic [A/m]" ];
1435
+ // Print[ hp_static, OnElementsOf Matrix , File StrCat["hp_static.pos"], Name "hp_static [A/m]" ];
1436
+ // Print[ hp_static_lagrange, OnElementsOf Matrix , File StrCat["hp_static_lagrange.pos"], Name "hp_static_lagrange [A/m]" ];
1437
+ }
1438
+ }
1439
+ {% endif %}
1440
+ { Name MagDyn_energy; LastTimeStepOnly 1 ;
1441
+ NameOfPostProcessing MagDyn_hphi;
1442
+ Operation {
1443
+ Print[ totalLoss[NonLinOmegaC], OnGlobal, Format Table, StoreInVariable $indicFilamentLoss, File StrCat[resDirectory,"/dummy.txt"] ];
1444
+ }
1445
+ }
1446
+ { Name test_Losses; LastTimeStepOnly 1 ;
1447
+ NameOfPostProcessing MagDyn_hphi;
1448
+ Operation {
1449
+ Print[ totalLoss[OmegaC], OnGlobal, Format Table, StoreInVariable $indicTotalLoss, File > StrCat[resDirectory,"/dummy.txt"] ];
1450
+ Print[ totalLoss[NonLinOmegaC], OnGlobal, Format Table, StoreInVariable $indicFilamentLoss, File StrCat[resDirectory,"/dummy.txt"] ];
1451
+ Print[ eddyLoss[Matrix], OnGlobal, Format Table, StoreInVariable $indicEddyLoss, File > StrCat[resDirectory,"/dummy.txt"] ];
1452
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" %}
1453
+ Print[ couplingLoss[Matrix], OnGlobal, Format Table, StoreInVariable $indicCouplingLoss, File > StrCat[resDirectory,"/dummy.txt"] ];
1454
+ {% endif %}
1455
+ }
1456
+ }
1457
+
1458
+ { Name heat_capacity; LastTimeStepOnly 1 ;
1459
+ NameOfPostProcessing MagDyn_hphi;
1460
+ Operation {
1461
+ Print[ heat_capacity[OmegaC], OnGlobal, Format Table, StoreInVariable $heat_capacity_per_unit_length, File StrCat[resDirectory,"/dummy.txt"] ];
1462
+ }
1463
+ }
1464
+
1465
+ {% if dm.magnet.solve.formulation_parameters.formulation == "CATI" and dm.magnet.solve.formulation_parameters.dynamic_correction == True %}
1466
+ { Name test_Losses_dynCorr; LastTimeStepOnly 1 ;
1467
+ NameOfPostProcessing MagDyn_hphi_dynCorr;
1468
+ Operation {
1469
+ Print[ totalLoss_dyn[OmegaC], OnGlobal, Format Table, StoreInVariable $indicTotalLoss_dyn, File > StrCat[resDirectory,"/dummy.txt"] ];
1470
+ Print[ couplingLoss_dyn[Matrix_partitions_for_TI], OnGlobal, Format Table, StoreInVariable $indicCouplingLoss_dyn, File > StrCat[resDirectory,"/dummy.txt"] ];
1471
+ }
1472
+ }
1473
+ {% endif %}
1474
+ }