fiqus 2024.7.0__py3-none-any.whl → 2024.12.1__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 (69) hide show
  1. fiqus/MainFiQuS.py +290 -134
  2. fiqus/data/DataConductor.py +301 -301
  3. fiqus/data/DataFiQuS.py +128 -84
  4. fiqus/data/DataFiQuSCCT.py +150 -150
  5. fiqus/data/DataFiQuSConductor.py +84 -84
  6. fiqus/data/DataFiQuSConductorAC_Strand.py +565 -565
  7. fiqus/data/DataFiQuSMultipole.py +716 -42
  8. fiqus/data/DataFiQuSPancake3D.py +737 -278
  9. fiqus/data/DataMultipole.py +180 -15
  10. fiqus/data/DataRoxieParser.py +90 -51
  11. fiqus/data/DataSettings.py +121 -0
  12. fiqus/data/DataWindingsCCT.py +37 -37
  13. fiqus/data/RegionsModelFiQuS.py +18 -6
  14. fiqus/geom_generators/GeometryCCT.py +905 -905
  15. fiqus/geom_generators/GeometryConductorAC_Strand.py +1391 -1391
  16. fiqus/geom_generators/GeometryMultipole.py +1827 -227
  17. fiqus/geom_generators/GeometryPancake3D.py +316 -117
  18. fiqus/geom_generators/GeometryPancake3DUtils.py +549 -0
  19. fiqus/getdp_runners/RunGetdpCCT.py +4 -4
  20. fiqus/getdp_runners/RunGetdpConductorAC_Strand.py +201 -201
  21. fiqus/getdp_runners/RunGetdpMultipole.py +115 -42
  22. fiqus/getdp_runners/RunGetdpPancake3D.py +28 -6
  23. fiqus/mains/MainCCT.py +2 -2
  24. fiqus/mains/MainConductorAC_Strand.py +132 -132
  25. fiqus/mains/MainMultipole.py +113 -62
  26. fiqus/mains/MainPancake3D.py +63 -23
  27. fiqus/mesh_generators/MeshCCT.py +209 -209
  28. fiqus/mesh_generators/MeshConductorAC_Strand.py +656 -656
  29. fiqus/mesh_generators/MeshMultipole.py +1243 -181
  30. fiqus/mesh_generators/MeshPancake3D.py +275 -192
  31. fiqus/parsers/ParserCOND.py +825 -0
  32. fiqus/parsers/ParserDAT.py +16 -16
  33. fiqus/parsers/ParserGetDPOnSection.py +212 -212
  34. fiqus/parsers/ParserGetDPTimeTable.py +134 -134
  35. fiqus/parsers/ParserMSH.py +53 -53
  36. fiqus/parsers/ParserPOS.py +214 -214
  37. fiqus/parsers/ParserRES.py +142 -142
  38. fiqus/plotters/PlotPythonCCT.py +133 -133
  39. fiqus/plotters/PlotPythonConductorAC.py +855 -855
  40. fiqus/plotters/PlotPythonMultipole.py +18 -18
  41. fiqus/post_processors/PostProcessCCT.py +440 -440
  42. fiqus/post_processors/PostProcessConductorAC.py +49 -49
  43. fiqus/post_processors/PostProcessMultipole.py +353 -229
  44. fiqus/post_processors/PostProcessPancake3D.py +8 -13
  45. fiqus/pre_processors/PreProcessCCT.py +175 -175
  46. fiqus/pro_assemblers/ProAssembler.py +14 -6
  47. fiqus/pro_material_functions/ironBHcurves.pro +246 -246
  48. fiqus/pro_templates/combined/CCT_template.pro +274 -274
  49. fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -1025
  50. fiqus/pro_templates/combined/Multipole_template.pro +1694 -126
  51. fiqus/pro_templates/combined/Pancake3D_template.pro +2294 -1103
  52. fiqus/pro_templates/combined/TSA_materials.pro +162 -0
  53. fiqus/pro_templates/combined/materials.pro +36 -18
  54. fiqus/utils/Utils.py +508 -110
  55. fiqus/utils/update_data_settings.py +33 -0
  56. fiqus-2024.12.1.dist-info/METADATA +132 -0
  57. fiqus-2024.12.1.dist-info/RECORD +84 -0
  58. {fiqus-2024.7.0.dist-info → fiqus-2024.12.1.dist-info}/WHEEL +1 -1
  59. tests/test_FiQuS.py +1 -1
  60. tests/test_geometry_generators.py +101 -2
  61. tests/test_mesh_generators.py +154 -1
  62. tests/test_solvers.py +115 -21
  63. tests/utils/fiqus_test_classes.py +85 -21
  64. tests/utils/generate_reference_files_ConductorAC.py +57 -57
  65. tests/utils/generate_reference_files_Pancake3D.py +4 -5
  66. tests/utils/helpers.py +97 -97
  67. fiqus-2024.7.0.dist-info/METADATA +0 -103
  68. fiqus-2024.7.0.dist-info/RECORD +0 -79
  69. {fiqus-2024.7.0.dist-info → fiqus-2024.12.1.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- from typing import Literal, Optional, Annotated
1
+ from typing import Literal, Optional, Annotated, Union
2
2
  from contextvars import ContextVar
3
3
  import logging
4
4
  import math
@@ -26,9 +26,9 @@ logger = logging.getLogger(__name__)
26
26
  # ======================================================================================
27
27
  # Available materials: =================================================================
28
28
  NormalMaterialName = Literal[
29
- "Copper", "Hastelloy", "Silver", "Indium", "Stainless Steel"
29
+ "Copper", "Hastelloy", "Silver", "Indium", "Stainless Steel", "Kapton", "G10"
30
30
  ]
31
- SuperconductingMaterialName = Literal["HTSSuperPower", "HTSFujikura"]
31
+ SuperconductingMaterialName = Literal["HTSSuperPower", "HTSFujikura", "HTSSucci"]
32
32
  # ======================================================================================
33
33
  # ======================================================================================
34
34
 
@@ -47,6 +47,8 @@ thermalConductivityMacroNames = {
47
47
  "Silver": "MATERIAL_ThermalConductivity_Silver_T",
48
48
  "Indium": "MATERIAL_ThermalConductivity_Indium_T",
49
49
  "Stainless Steel": "MATERIAL_ThermalConductivity_SSteel_T",
50
+ "Kapton": "MATERIAL_ThermalConductivity_Kapton_T",
51
+ "G10": "MATERIAL_ThermalConductivity_G10_T",
50
52
  }
51
53
  heatCapacityMacroNames = {
52
54
  "Copper": "MATERIAL_SpecificHeatCapacity_Copper_T",
@@ -54,30 +56,29 @@ heatCapacityMacroNames = {
54
56
  "Silver": "MATERIAL_SpecificHeatCapacity_Silver_T",
55
57
  "Indium": "MATERIAL_SpecificHeatCapacity_Indium_T",
56
58
  "Stainless Steel": "MATERIAL_SpecificHeatCapacity_SSteel_T",
59
+ "Kapton": "MATERIAL_SpecificHeatCapacity_Kapton_T",
60
+ "G10": "MATERIAL_SpecificHeatCapacity_G10_T",
57
61
  }
58
- getdpTSAOnlyResistivityFunctions = {
59
- "Indium": "TSA_CFUN_rhoIn_T_constantThickness_fct_only",
60
- "Stainless Steel": None,
61
- }
62
- getdpTSAMassResistivityFunctions = {
63
- "Indium": "TSA_CFUN_rhoIn_T_constantThickness_mass",
64
- "Stainless Steel": None,
62
+ getdpTSAStiffnessThermalConductivityMacroNames = {
63
+ "Indium": "MATERIAL_ThermalConductivity_Indium_TSAStiffness_T",
64
+ "Stainless Steel": "MATERIAL_ThermalConductivity_SSteel_TSAStiffness_T",
65
+ "Kapton": "MATERIAL_ThermalConductivity_Kapton_TSAStiffness_T",
66
+ "G10": "MATERIAL_ThermalConductivity_G10_TSAStiffness_T",
67
+ "Copper": "MATERIAL_ThermalConductivity_Copper_TSAStiffness_T",
65
68
  }
66
- getdpTSAStiffnessResistivityFunctions = {
67
- "Indium": "TSA_CFUN_rhoIn_T_constantThickness_stiffness",
68
- "Stainless Steel": None,
69
- }
70
- getdpTSAMassThermalConductivityFunctions = {
71
- "Indium": "TSA_CFUN_kIn_constantThickness_mass",
72
- "Stainless Steel": "TSA_CFUN_kSteel_T_constantThickness_mass",
73
- }
74
- getdpTSAStiffnessThermalConductivityFunctions = {
75
- "Indium": "TSA_CFUN_kIn_constantThickness_stiffness",
76
- "Stainless Steel": "TSA_CFUN_kSteel_T_constantThickness_stiffness",
69
+ getdpTSAMassThermalConductivityMacroNames = {
70
+ "Indium": "MATERIAL_ThermalConductivity_Indium_TSAMass_T",
71
+ "Stainless Steel": "MATERIAL_ThermalConductivity_SSteel_TSAMass_T",
72
+ "Kapton": "MATERIAL_ThermalConductivity_Kapton_TSAMass_T",
73
+ "G10": "MATERIAL_ThermalConductivity_G10_TSAMass_T",
74
+ "Copper": "MATERIAL_ThermalConductivity_Copper_TSAMass_T",
77
75
  }
78
- getdpTSAMassHeatCapacityFunctions = {
79
- "Indium": "TSA_CFUN_CvIn_constantThickness_mass",
80
- "Stainless Steel": "TSA_CFUN_CvSteel_T_constantThickness_mass",
76
+ getdpTSAMassHeatCapacityMacroNames = {
77
+ "Indium": "MATERIAL_SpecificHeatCapacity_Indium_TSAMass_T",
78
+ "Stainless Steel": "MATERIAL_SpecificHeatCapacity_SSteel_TSAMass_T",
79
+ "Kapton": "MATERIAL_SpecificHeatCapacity_Kapton_TSAMass_T",
80
+ "G10": "MATERIAL_SpecificHeatCapacity_G10_TSAMass_T",
81
+ "Copper": "MATERIAL_SpecificHeatCapacity_Copper_TSAMass_T",
81
82
  }
82
83
  getdpTSARHSFunctions = {
83
84
  "Indium": "TSA_CFUN_rhoIn_T_constantThickness_rhs",
@@ -87,9 +88,33 @@ getdpTSATripleFunctions = {
87
88
  "Indium": "TSA_CFUN_rhoIn_T_constantThickness_triple",
88
89
  "Stainless Steel": None,
89
90
  }
91
+ getdpTSAOnlyResistivityFunctions = {
92
+ "Indium": "TSA_CFUN_rhoIn_T_constantThickness_fct_only",
93
+ "Stainless Steel": None,
94
+ }
95
+ getdpTSAMassResistivityMacroNames = {
96
+ "Indium": "MATERIAL_Resistivity_Indium_TSAMass_T",
97
+ "Stainless Steel": None,
98
+ "Copper": "MATERIAL_Resistivity_Copper_TSAMass_T",
99
+ }
100
+ getdpTSAStiffnessResistivityMacroNames = {
101
+ "Indium": "MATERIAL_Resistivity_Indium_TSAStiffness_T",
102
+ "Stainless Steel": None,
103
+ "Copper": "MATERIAL_Resistivity_Copper_TSAStiffness_T",
104
+ }
90
105
  getdpCriticalCurrentDensityFunctions = {
91
106
  "HTSSuperPower": "CFUN_HTS_JcFit_SUPERPOWER_T_B_theta",
92
107
  "HTSFujikura": "CFUN_HTS_JcFit_Fujikura_T_B_theta",
108
+ "HTSSucci": "CFUN_HTS_JcFit_Succi_T_B",
109
+ }
110
+ getdpNormalMaterialNames = {
111
+ "Copper": "Copper",
112
+ "Hastelloy": "Hastelloy",
113
+ "Silver": "Silver",
114
+ "Indium": "Indium",
115
+ "Stainless Steel": "StainlessSteel",
116
+ "Kapton": "Kapton",
117
+ "G10": "G10",
93
118
  }
94
119
  # ======================================================================================
95
120
  # ======================================================================================
@@ -112,6 +137,10 @@ PositionRequiredQuantityName = Literal[
112
137
  "criticalCurrent",
113
138
  "axialComponentOfTheMagneticField",
114
139
  "debug",
140
+ "jHTS",
141
+ "currentSharingIndex",
142
+ "arcLength",
143
+ "turnNumber"
115
144
  ]
116
145
  PositionNotRequiredQuantityName = Literal[
117
146
  "currentThroughCoil",
@@ -120,7 +149,9 @@ PositionNotRequiredQuantityName = Literal[
120
149
  "timeConstant",
121
150
  "totalResistiveHeating",
122
151
  "magneticEnergy",
123
- "maximumTemperature"
152
+ "maximumTemperature",
153
+ "cryocoolerAveragePower",
154
+ "cryocoolerAverageTemperature"
124
155
  ]
125
156
  # ======================================================================================
126
157
  # ======================================================================================
@@ -145,6 +176,10 @@ EMQuantities = [
145
176
  "totalResistiveHeating",
146
177
  "magneticEnergy",
147
178
  "axialComponentOfTheMagneticField",
179
+ "jHTS",
180
+ "currentSharingIndex",
181
+ "arcLength",
182
+ "turnNumber"
148
183
  ]
149
184
  ThermalQuantities = [
150
185
  "temperature",
@@ -153,6 +188,8 @@ ThermalQuantities = [
153
188
  "specificHeatCapacity",
154
189
  "maximumTemperature",
155
190
  "debug",
191
+ "cryocoolerAveragePower",
192
+ "cryocoolerAverageTemperature"
156
193
  ]
157
194
  quantityProperNames = {
158
195
  "magneticField": "Magnetic Field",
@@ -177,6 +214,12 @@ quantityProperNames = {
177
214
  "timeConstant": "Time Constant",
178
215
  "axialComponentOfTheMagneticField": "Axial Component of the Magnetic Field",
179
216
  "maximumTemperature": "Maximum Temperature",
217
+ "jHTS": "Current Density in HTS Layer",
218
+ "currentSharingIndex": "Current Sharing Index",
219
+ "cryocoolerAveragePower": "Cryocooler Average Power",
220
+ "arcLength": "Arc Length",
221
+ "turnNumber": "Turn Number",
222
+ "cryocoolerAverageTemperature": "Cryocooler Average Temperature"
180
223
  }
181
224
 
182
225
  quantityUnits = {
@@ -195,13 +238,19 @@ quantityUnits = {
195
238
  "resistivity": "Ohm*m",
196
239
  "thermalConductivity": "W/m*K",
197
240
  "specificHeatCapacity": "J/kg*K",
198
- "jHTSOverjCritical": "A/A",
241
+ "jHTSOverjCritical": "-",
199
242
  "criticalCurrent": "A",
200
243
  "debug": "1",
201
244
  "inductance": "H",
202
245
  "timeConstant": "s",
203
246
  "axialComponentOfTheMagneticField": "T",
204
247
  "maximumTemperature": "K",
248
+ "jHTS": "A/m^2",
249
+ "currentSharingIndex": "-",
250
+ "cryocoolerAveragePower": "W",
251
+ "arcLength": "m",
252
+ "turnNumber": "-",
253
+ "cryocoolerAverageTemperature": "K"
205
254
  }
206
255
 
207
256
  getdpQuantityNames = {
@@ -227,6 +276,12 @@ getdpQuantityNames = {
227
276
  "timeConstant": "RESULT_timeConstant",
228
277
  "axialComponentOfTheMagneticField": "RESULT_axialComponentOfTheMagneticField",
229
278
  "maximumTemperature": "RESULT_maximumTemperature",
279
+ "jHTS": "RESULT_jHTS",
280
+ "currentSharingIndex": "RESULT_currentSharingIndex",
281
+ "cryocoolerAveragePower": "RESULT_cryocoolerAveragePower",
282
+ "arcLength": "RESULT_arcLength",
283
+ "turnNumber": "RESULT_turnNumber",
284
+ "cryocoolerAverageTemperature": "RESULT_cryocoolerAverageTemperature"
230
285
  }
231
286
 
232
287
  getdpPostOperationNames = {
@@ -252,6 +307,12 @@ getdpPostOperationNames = {
252
307
  "timeConstant": "POSTOP_timeConstant",
253
308
  "axialComponentOfTheMagneticField": "POSTOP_axialComponentOfTheMagneticField",
254
309
  "maximumTemperature": "POSTOP_maximumTemperature",
310
+ "jHTS": "POSTOP_jHTS",
311
+ "currentSharingIndex": "POSTOP_currentSharingIndex",
312
+ "cryocoolerAveragePower": "POSTOP_cryocoolerAveragePower",
313
+ "arcLength": "POSTOP_arcLength",
314
+ "turnNumber": "POSTOP_turnNumber",
315
+ "cryocoolerAverageTemperature": "POSTOP_cryocoolerAverageTemperature"
255
316
  }
256
317
 
257
318
  # ======================================================================================
@@ -541,24 +602,20 @@ Pancake3DPosition = Pancake3DPositionInCoordinates | Pancake3DPositionInTurnNumb
541
602
  # ======================================================================================
542
603
  class Pancake3DGeometryWinding(BaseModel):
543
604
  # Mandatory:
544
- r_i: PositiveFloat = Field(
545
- alias="innerRadius",
605
+ innerRadius: PositiveFloat = Field(
546
606
  title="Inner Radius",
547
607
  description="Inner radius of the winding.",
548
608
  )
549
- t: PositiveFloat = Field(
550
- alias="thickness",
609
+ thickness: PositiveFloat = Field(
551
610
  title="Winding Thickness",
552
611
  description="Thickness of the winding.",
553
612
  )
554
- N: float = Field(
555
- alias="numberOfTurns",
613
+ numberOfTurns: float = Field(
556
614
  ge=3,
557
615
  title="Number of Turns",
558
616
  description="Number of turns of the winding.",
559
617
  )
560
- h: PositiveFloat = Field(
561
- alias="height",
618
+ height: PositiveFloat = Field(
562
619
  title="Winding Height",
563
620
  description="Height/width of the winding.",
564
621
  )
@@ -570,41 +627,40 @@ class Pancake3DGeometryWinding(BaseModel):
570
627
  description="The The name to be used in the mesh..",
571
628
  examples=["winding", "myWinding"],
572
629
  )
573
- NofVolPerTurn: int = Field(
630
+ numberOfVolumesPerTurn: int = Field(
574
631
  default=2,
575
632
  validate_default=True,
576
- alias="numberOfVolumesPerTurn",
577
633
  ge=2,
578
634
  title="Number of Volumes Per Turn (Advanced Input)",
579
635
  description="The number of volumes per turn (CAD related, not physical).",
580
636
  )
581
637
 
582
- @field_validator("NofVolPerTurn")
638
+ @field_validator("numberOfVolumesPerTurn")
583
639
  @classmethod
584
- def check_NofVolPerTurn(cls, NofVolPerTurn):
640
+ def check_numberOfVolumesPerTurn(cls, numberOfVolumesPerTurn):
585
641
  geometry = geometry_input.get()
586
642
  mesh = mesh_input.get()
587
643
 
588
- # Check if the NofVolPerTurn is compatible swith the azimuthal number of
644
+ # Check if the numberOfVolumesPerTurn is compatible swith the azimuthal number of
589
645
  # elements per turn:
590
646
  for i, ane in enumerate(mesh["winding"]["azimuthalNumberOfElementsPerTurn"]):
591
- if ane % NofVolPerTurn != 0:
647
+ if ane % numberOfVolumesPerTurn != 0:
592
648
  raise ValueError(
593
649
  "The azimuthal number of elements per turn for the pancake coil"
594
650
  f" number {i+1} is ({ane}), but it must be divisible by the number"
595
- f" of volumes per turn ({geometry['winding']['NofVolPerTurn']})!"
651
+ f" of volumes per turn ({geometry['winding']['numberOfVolumesPerTurn']})!"
596
652
  " So it needs to be rounded to"
597
- f" {math.ceil(ane/NofVolPerTurn)*NofVolPerTurn:.5f} or"
598
- f" {math.floor(ane/NofVolPerTurn)*NofVolPerTurn:.5f}."
653
+ f" {math.ceil(ane/numberOfVolumesPerTurn)*numberOfVolumesPerTurn:.5f} or"
654
+ f" {math.floor(ane/numberOfVolumesPerTurn)*numberOfVolumesPerTurn:.5f}."
599
655
  )
600
656
 
601
657
  structured = checkIfAirOrTerminalMeshIsStructured()
602
658
 
603
659
  if structured:
604
660
  # If the mesh is structured, the number of volumes per turn must be 4:
605
- NofVolPerTurn = 4
661
+ numberOfVolumesPerTurn = 4
606
662
 
607
- return NofVolPerTurn
663
+ return numberOfVolumesPerTurn
608
664
 
609
665
  @computed_field
610
666
  @cached_property
@@ -720,8 +776,7 @@ class Pancake3DGeometryWinding(BaseModel):
720
776
 
721
777
  class Pancake3DGeometryContactLayer(BaseModel):
722
778
  # Mandatory:
723
- tsa: bool = Field(
724
- alias="thinShellApproximation",
779
+ thinShellApproximation: bool = Field(
725
780
  title="Use Thin Shell Approximation",
726
781
  description=(
727
782
  "If True, the contact layer will be modeled with 2D shell elements (thin"
@@ -729,10 +784,13 @@ class Pancake3DGeometryContactLayer(BaseModel):
729
784
  " with 3D elements."
730
785
  ),
731
786
  )
732
- t: PositiveFloat = Field(
733
- alias="thickness",
787
+ thickness: PositiveFloat = Field(
734
788
  title="Contact Layer Thickness",
735
- description="Thickness of the contact layer.",
789
+ description=("Thickness of the contact layer."
790
+ "It is the total thickness of the contact or insulation layer."
791
+ "In particular, for perfect insulation this would be the sum of the insulation layer of the two adjacent CC with an insulation layer of "
792
+ "thickness t/2 on each side."
793
+ ),
736
794
  )
737
795
 
738
796
  # Optionals:
@@ -746,24 +804,23 @@ class Pancake3DGeometryContactLayer(BaseModel):
746
804
 
747
805
  class Pancake3DGeometryTerminalBase(BaseModel):
748
806
  # Mandatory:
749
- t: PositiveFloat = Field(
750
- alias="thickness",
807
+ thickness: PositiveFloat = Field(
751
808
  title="Terminal Thickness",
752
809
  description="Thickness of the terminal's tube.",
753
810
  ) # thickness
754
811
 
755
- @field_validator("t")
812
+ @field_validator("thickness")
756
813
  @classmethod
757
- def check_t(cls, t):
814
+ def check_t(cls, thickness):
758
815
  geometry = geometry_input.get()
759
816
 
760
- if t < geometry["winding"]["thickness"] / 2:
817
+ if thickness < geometry["winding"]["thickness"] / 2:
761
818
  raise ValueError(
762
819
  "Terminal's thickness is smaller than half of the winding's thickness!"
763
820
  " Please increase the terminal's thickness."
764
821
  )
765
822
 
766
- return t
823
+ return thickness
767
824
 
768
825
 
769
826
  class Pancake3DGeometryInnerTerminal(Pancake3DGeometryTerminalBase):
@@ -780,7 +837,7 @@ class Pancake3DGeometryInnerTerminal(Pancake3DGeometryTerminalBase):
780
837
  """Return inner radius of the inner terminal."""
781
838
  geometry = geometry_input.get()
782
839
 
783
- innerRadius = geometry["winding"]["innerRadius"] - 2 * self.t
840
+ innerRadius = geometry["winding"]["innerRadius"] - 2 * self.thickness
784
841
  if innerRadius < 0:
785
842
  raise ValueError(
786
843
  "Inner terminal's radius is smaller than 0! Please decrease the inner"
@@ -802,15 +859,15 @@ class Pancake3DGeometryOuterTerminal(Pancake3DGeometryTerminalBase):
802
859
  @cached_property
803
860
  def r(self) -> float:
804
861
  """Return outer radius of the outer terminal."""
805
- outerRadius = getWindingOuterRadius() + 2 * self.t
862
+ outerRadius = getWindingOuterRadius() + 2 * self.thickness
806
863
 
807
864
  return outerRadius
808
865
 
809
866
 
810
867
  class Pancake3DGeometryTerminals(BaseModel):
811
868
  # 1) User inputs:
812
- i: Pancake3DGeometryInnerTerminal = Field(alias="inner")
813
- o: Pancake3DGeometryOuterTerminal = Field(alias="outer")
869
+ inner: Pancake3DGeometryInnerTerminal = Field()
870
+ outer: Pancake3DGeometryOuterTerminal = Field()
814
871
 
815
872
  # Optionals:
816
873
  firstName: str = Field(
@@ -829,8 +886,7 @@ class Pancake3DGeometryTerminals(BaseModel):
829
886
 
830
887
  class Pancake3DGeometryAirBase(BaseModel):
831
888
  # Mandatory:
832
- margin: PositiveFloat = Field(
833
- alias="axialMargin",
889
+ axialMargin: PositiveFloat = Field(
834
890
  title="Axial Margin of the Air",
835
891
  description=(
836
892
  "Axial margin between the ends of the air and first/last pancake coils."
@@ -846,7 +902,6 @@ class Pancake3DGeometryAirBase(BaseModel):
846
902
  )
847
903
  shellTransformation: bool = Field(
848
904
  default=False,
849
- alias="shellTransformation",
850
905
  title="Use Shell Transformation",
851
906
  description=(
852
907
  "Generate outer shell air to apply shell transformation if True (GetDP"
@@ -856,7 +911,6 @@ class Pancake3DGeometryAirBase(BaseModel):
856
911
  shellTransformationMultiplier: float = Field(
857
912
  default=1.2,
858
913
  gt=1.1,
859
- alias="shellTransformationMultiplier",
860
914
  title="Shell Transformation Multiplier (Advanced Input)",
861
915
  description=(
862
916
  "multiply the air's outer dimension by this value to get the shell's outer"
@@ -875,9 +929,8 @@ class Pancake3DGeometryAirBase(BaseModel):
875
929
  description="name of the shell volume to be used in the mesh",
876
930
  examples=["air-Shell", "myAirShell"],
877
931
  )
878
- fragment: bool = Field(
932
+ generateGapAirWithFragment: bool = Field(
879
933
  default=False,
880
- alias="generateGapAirWithFragment",
881
934
  title="Generate Gap Air with Fragment (Advanced Input)",
882
935
  description=(
883
936
  "generate the gap air with gmsh/model/occ/fragment if true (CAD related,"
@@ -885,19 +938,19 @@ class Pancake3DGeometryAirBase(BaseModel):
885
938
  ),
886
939
  )
887
940
 
888
- @field_validator("margin")
941
+ @field_validator("axialMargin")
889
942
  @classmethod
890
- def check_margin(cls, margin):
943
+ def check_axialMargin(cls, axialMargin):
891
944
  geometry = geometry_input.get()
892
945
  windingHeight = geometry["winding"]["height"]
893
946
 
894
- if margin < windingHeight / 2:
947
+ if axialMargin < windingHeight / 2:
895
948
  raise ValueError(
896
949
  "Axial margin is smaller than half of the winding's height! Please"
897
950
  " increase the axial margin."
898
951
  )
899
952
 
900
- return margin
953
+ return axialMargin
901
954
 
902
955
  @computed_field
903
956
  @cached_property
@@ -910,69 +963,67 @@ class Pancake3DGeometryAirBase(BaseModel):
910
963
 
911
964
  class Pancake3DGeometryAirCylinder(Pancake3DGeometryAirBase):
912
965
  type: Literal["cylinder"] = Field(default="cylinder", title="Air Type")
913
- r: PositiveFloat = Field(
966
+ radius: PositiveFloat = Field(
914
967
  default=None,
915
- alias="radius",
916
968
  title="Air Radius",
917
969
  description="Radius of the air (for cylinder type air).",
918
970
  )
919
971
 
920
- @field_validator("r")
972
+ @field_validator("radius")
921
973
  @classmethod
922
- def check_r(cls, r):
974
+ def check_r(cls, radius):
923
975
  geometry = geometry_input.get()
924
976
  outerTerminalOuterRadius = (
925
977
  getWindingOuterRadius() + 2 * geometry["terminals"]["outer"]["thickness"]
926
978
  )
927
979
 
928
- if r < outerTerminalOuterRadius * 1.5:
980
+ if radius < outerTerminalOuterRadius * 1.5:
929
981
  raise ValueError(
930
982
  "Radius of the air must be at least 1.5 times the outer radius of the"
931
983
  " winding! Please increase the radius of the air."
932
984
  )
933
985
 
934
- return r
986
+ return radius
935
987
 
936
988
  @computed_field
937
989
  @cached_property
938
990
  def shellOuterRadius(self) -> float:
939
991
  """Return outer radius of the air."""
940
- shellOuterRadius = self.shellTransformationMultiplier * self.r
992
+ shellOuterRadius = self.shellTransformationMultiplier * self.radius
941
993
 
942
994
  return shellOuterRadius
943
995
 
944
996
 
945
997
  class Pancake3DGeometryAirCuboid(Pancake3DGeometryAirBase):
946
998
  type: Literal["cuboid"] = Field(default="cuboid", title="Air Type")
947
- a: PositiveFloat = Field(
999
+ sideLength: PositiveFloat = Field(
948
1000
  default=None,
949
- alias="sideLength",
950
1001
  title="Air Side Length",
951
1002
  description="Side length of the air (for cuboid type air).",
952
1003
  )
953
1004
 
954
- @field_validator("a")
1005
+ @field_validator("sideLength")
955
1006
  @classmethod
956
- def check_a(cls, a):
1007
+ def check_a(cls, sideLength):
957
1008
  geometry = geometry_input.get()
958
1009
  outerTerminalOuterRadius = (
959
1010
  getWindingOuterRadius() + 2 * geometry["terminals"]["outer"]["thickness"]
960
1011
  )
961
1012
 
962
- if a / 2 < outerTerminalOuterRadius * 1.5:
1013
+ if sideLength / 2 < outerTerminalOuterRadius * 1.5:
963
1014
  raise ValueError(
964
1015
  "Half of the side length of the air must be at least 1.5 times the"
965
1016
  " outer radius of the winding! Please increase the side length of the"
966
1017
  " air."
967
1018
  )
968
1019
 
969
- return a
1020
+ return sideLength
970
1021
 
971
1022
  @computed_field
972
1023
  @cached_property
973
1024
  def shellSideLength(self) -> float:
974
1025
  """Return outer radius of the air."""
975
- shellSideLength = self.shellTransformationMultiplier * self.a
1026
+ shellSideLength = self.shellTransformationMultiplier * self.sideLength
976
1027
 
977
1028
  return shellSideLength
978
1029
 
@@ -991,8 +1042,7 @@ Pancake3DGeometryAir = Annotated[
991
1042
  # ======================================================================================
992
1043
  class Pancake3DMeshWinding(BaseModel):
993
1044
  # Mandatory:
994
- axne: list[PositiveInt] | PositiveInt = Field(
995
- alias="axialNumberOfElements",
1045
+ axialNumberOfElements: list[PositiveInt] | PositiveInt = Field(
996
1046
  title="Axial Number of Elements",
997
1047
  description=(
998
1048
  "The number of axial elements for the whole height of the coil. It can be"
@@ -1001,8 +1051,7 @@ class Pancake3DMeshWinding(BaseModel):
1001
1051
  ),
1002
1052
  )
1003
1053
 
1004
- ane: list[PositiveInt] | PositiveInt = Field(
1005
- alias="azimuthalNumberOfElementsPerTurn",
1054
+ azimuthalNumberOfElementsPerTurn: list[PositiveInt] | PositiveInt = Field(
1006
1055
  title="Azimuthal Number of Elements Per Turn",
1007
1056
  description=(
1008
1057
  "The number of azimuthal elements per turn of the coil. It can be either a"
@@ -1011,8 +1060,7 @@ class Pancake3DMeshWinding(BaseModel):
1011
1060
  ),
1012
1061
  )
1013
1062
 
1014
- rne: list[PositiveInt] | PositiveInt = Field(
1015
- alias="radialNumberOfElementsPerTurn",
1063
+ radialNumberOfElementsPerTurn: list[PositiveInt] | PositiveInt = Field(
1016
1064
  title="Winding Radial Number of Elements Per Turn",
1017
1065
  description=(
1018
1066
  "The number of radial elements per tape of the winding. It can be either a"
@@ -1022,9 +1070,8 @@ class Pancake3DMeshWinding(BaseModel):
1022
1070
  )
1023
1071
 
1024
1072
  # Optionals:
1025
- axbc: list[PositiveFloat] | PositiveFloat = Field(
1073
+ axialDistributionCoefficient: list[PositiveFloat] | PositiveFloat = Field(
1026
1074
  default=[1],
1027
- alias="axialDistributionCoefficient",
1028
1075
  title="Axial Bump Coefficients",
1029
1076
  description=(
1030
1077
  "If 1, it won't affect anything. If smaller than 1, elements will get finer"
@@ -1049,7 +1096,7 @@ class Pancake3DMeshWinding(BaseModel):
1049
1096
  ),
1050
1097
  )
1051
1098
 
1052
- @field_validator("axne", "ane", "rne", "axbc", "elementType")
1099
+ @field_validator("axialNumberOfElements", "azimuthalNumberOfElementsPerTurn", "radialNumberOfElementsPerTurn", "axialDistributionCoefficient", "elementType")
1053
1100
  @classmethod
1054
1101
  def check_inputs(cls, value, info: ValidationInfo):
1055
1102
  geometry = geometry_input.get()
@@ -1101,8 +1148,7 @@ class Pancake3DMeshWinding(BaseModel):
1101
1148
 
1102
1149
  class Pancake3DMeshContactLayer(BaseModel):
1103
1150
  # Mandatory:
1104
- rne: list[PositiveInt] = Field(
1105
- alias="radialNumberOfElementsPerTurn",
1151
+ radialNumberOfElementsPerTurn: list[PositiveInt] = Field(
1106
1152
  title="Contact Layer Radial Number of Elements Per Turn",
1107
1153
  description=(
1108
1154
  "The number of radial elements per tape of the contact layer. It can be"
@@ -1111,7 +1157,7 @@ class Pancake3DMeshContactLayer(BaseModel):
1111
1157
  ),
1112
1158
  )
1113
1159
 
1114
- @field_validator("rne")
1160
+ @field_validator("radialNumberOfElementsPerTurn")
1115
1161
  @classmethod
1116
1162
  def check_inputs(cls, value):
1117
1163
  geometry = geometry_input.get()
@@ -1179,7 +1225,7 @@ class Pancake3DSolveAir(BaseModel):
1179
1225
  )
1180
1226
 
1181
1227
 
1182
- class Pancake3DSolveIcVsLength(BaseModel):
1228
+ class Pancake3DSolveIcVsLengthList(BaseModel):
1183
1229
  lengthValues: list[float] = Field(
1184
1230
  title="Tape Length Values",
1185
1231
  description="Tape length values that corresponds to criticalCurrentValues.",
@@ -1188,15 +1234,39 @@ class Pancake3DSolveIcVsLength(BaseModel):
1188
1234
  title="Critical Current Values",
1189
1235
  description="Critical current values that corresponds to lengthValues.",
1190
1236
  )
1237
+ lengthUnit: str = Field(
1238
+ title="Unit",
1239
+ description=(
1240
+ "Unit of the critical current values. "
1241
+ "It can be either the arc length in meter or "
1242
+ "the number of turns."
1243
+ ),
1244
+ examples=["meter", "turnNumber"],
1245
+ )
1246
+
1247
+ class Pancake3DSolveIcVsLengthCSV(BaseModel):
1248
+ csvFile: str = Field(
1249
+ title="CSV File",
1250
+ description="The path of the CSV file that contains the critical current values.",
1251
+ )
1252
+
1253
+ lengthUnit: str = Field(
1254
+ title="Unit",
1255
+ description=(
1256
+ "Unit of the critical current values. "
1257
+ "It can be either the arc length in meter or "
1258
+ "the number of turns."
1259
+ ),
1260
+ examples=["meter", "turnNumber"],
1261
+ )
1191
1262
 
1192
1263
 
1193
1264
  class Pancake3DSolveMaterialBase(BaseModel):
1194
1265
  name: str
1195
1266
 
1196
1267
  # Optionals:
1197
- rrr: PositiveFloat = Field(
1268
+ RRR: PositiveFloat = Field(
1198
1269
  default=100,
1199
- alias="residualResistanceRatio",
1200
1270
  title="Residual Resistance Ratio",
1201
1271
  description=(
1202
1272
  "Residual-resistivity ratio (also known as Residual-resistance ratio or"
@@ -1204,9 +1274,8 @@ class Pancake3DSolveMaterialBase(BaseModel):
1204
1274
  " temperature and at 0 K."
1205
1275
  ),
1206
1276
  )
1207
- rrrRefT: PositiveFloat = Field(
1277
+ RRRRefTemp: PositiveFloat = Field(
1208
1278
  default=295,
1209
- alias="residualResistanceRatioReferenceTemperature",
1210
1279
  title="Residual Resistance Ratio Reference Temperature",
1211
1280
  description="Reference temperature for residual resistance ratio",
1212
1281
  )
@@ -1249,48 +1318,48 @@ class Pancake3DSolveMaterialBase(BaseModel):
1249
1318
 
1250
1319
  @computed_field
1251
1320
  @cached_property
1252
- def getdpTSAMassResistivityFunction(self) -> str:
1321
+ def getdpTSAMassResistivityMacroName(self) -> str:
1253
1322
  """Return the GetDP function name of the material's mass resistivity."""
1254
- if self.name not in getdpTSAMassResistivityFunctions:
1323
+ if self.name not in getdpTSAMassResistivityMacroNames:
1255
1324
  return "NOT_DEFINED_IN_DATA_FIQUS_PANCAKE3D"
1256
1325
 
1257
- return getdpTSAMassResistivityFunctions[self.name]
1326
+ return getdpTSAMassResistivityMacroNames[self.name]
1258
1327
 
1259
1328
  @computed_field
1260
1329
  @cached_property
1261
- def getdpTSAStiffnessResistivityFunction(self) -> str:
1330
+ def getdpTSAStiffnessResistivityMacroName(self) -> str:
1262
1331
  """Return the GetDP function name of the material's stiffness resistivity."""
1263
- if self.name not in getdpTSAStiffnessResistivityFunctions:
1332
+ if self.name not in getdpTSAStiffnessResistivityMacroNames:
1264
1333
  return "NOT_DEFINED_IN_DATA_FIQUS_PANCAKE3D"
1265
1334
 
1266
- return getdpTSAStiffnessResistivityFunctions[self.name]
1335
+ return getdpTSAStiffnessResistivityMacroNames[self.name]
1267
1336
 
1268
1337
  @computed_field
1269
1338
  @cached_property
1270
- def getdpTSAMassThermalConductivityFunction(self) -> str:
1339
+ def getdpTSAMassThermalConductivityMacroName(self) -> str:
1271
1340
  """Return the GetDP function name of the material's mass thermal conductivity."""
1272
- if self.name not in getdpTSAMassThermalConductivityFunctions:
1341
+ if self.name not in getdpTSAMassThermalConductivityMacroNames:
1273
1342
  return "NOT_DEFINED_IN_DATA_FIQUS_PANCAKE3D"
1274
1343
 
1275
- return getdpTSAMassThermalConductivityFunctions[self.name]
1344
+ return getdpTSAMassThermalConductivityMacroNames[self.name]
1276
1345
 
1277
1346
  @computed_field
1278
1347
  @cached_property
1279
- def getdpTSAStiffnessThermalConductivityFunction(self) -> str:
1348
+ def getdpTSAStiffnessThermalConductivityMacroName(self) -> str:
1280
1349
  """Return the GetDP function name of the material's stiffness thermal conductivity."""
1281
- if self.name not in getdpTSAStiffnessThermalConductivityFunctions:
1350
+ if self.name not in getdpTSAStiffnessThermalConductivityMacroNames:
1282
1351
  return "NOT_DEFINED_IN_DATA_FIQUS_PANCAKE3D"
1283
1352
 
1284
- return getdpTSAStiffnessThermalConductivityFunctions[self.name]
1353
+ return getdpTSAStiffnessThermalConductivityMacroNames[self.name]
1285
1354
 
1286
1355
  @computed_field
1287
1356
  @cached_property
1288
- def getdpTSAMassHeatCapacityFunction(self) -> str:
1357
+ def getdpTSAMassHeatCapacityMacroName(self) -> str:
1289
1358
  """Return the GetDP function name of the material's mass heat capacity."""
1290
- if self.name not in getdpTSAMassHeatCapacityFunctions:
1359
+ if self.name not in getdpTSAMassHeatCapacityMacroNames:
1291
1360
  return "NOT_DEFINED_IN_DATA_FIQUS_PANCAKE3D"
1292
1361
 
1293
- return getdpTSAMassHeatCapacityFunctions[self.name]
1362
+ return getdpTSAMassHeatCapacityMacroNames[self.name]
1294
1363
 
1295
1364
  @computed_field
1296
1365
  @cached_property
@@ -1317,33 +1386,43 @@ class Pancake3DSolveNormalMaterial(Pancake3DSolveMaterialBase):
1317
1386
  title="Material Name",
1318
1387
  )
1319
1388
 
1389
+ @computed_field
1390
+ @cached_property
1391
+ def getdpNormalMaterialGetDPName(self) -> str:
1392
+ """Return the GetDP material name of the normal conducting material. To make names with spaces like 'Stainless Steel' more robust."""
1393
+ if self.name not in getdpNormalMaterialNames:
1394
+ raise ValueError(
1395
+ f"Normal conducting material '{self.name}' is not defined"
1396
+ " in FiQuS!"
1397
+ )
1398
+
1399
+ return getdpNormalMaterialNames[self.name]
1400
+
1320
1401
 
1321
1402
  class Pancake3DSolveSuperconductingMaterial(Pancake3DSolveMaterialBase):
1322
1403
  # Mandatory:
1323
1404
  name: SuperconductingMaterialName = Field(
1324
- title="Superconduncting Material Name",
1405
+ title="Superconducting Material Name",
1325
1406
  )
1326
1407
  nValue: PositiveFloat = Field(
1327
1408
  default=30,
1328
- alias="N-Value for E-J Power Law",
1329
1409
  description="N-value for E-J power law.",
1330
1410
  )
1331
- IcAtTAndBref: PositiveFloat | str | Pancake3DSolveIcVsLength = Field(
1332
- alias="criticalCurrentAtReferenceTemperatureAndField",
1333
- title="Critical Current at Reference Temperature and Field",
1411
+ IcAtTAndBref: PositiveFloat | Pancake3DSolveIcVsLengthCSV | Pancake3DSolveIcVsLengthList = Field(
1412
+ title="Critical Current at Reference Temperature and Field in A",
1334
1413
  description=(
1335
- "Critical current at reference temperature and magnetic field."
1414
+ "Critical current in A at reference temperature and magnetic field."
1336
1415
  "The critical current value will"
1337
- " change with temperature depending on the superconductor material.\nEither"
1416
+ " change with temperature depending on the superconductor material.Either"
1338
1417
  " the same critical current for the whole tape or the critical current with"
1339
1418
  " respect to the tape length can be specified. To specify the same critical"
1340
1419
  " current for the entire tape, just use a scalar. To specify critical"
1341
1420
  " current with respect to the tape length: a CSV file can be used, or"
1342
1421
  " lengthValues and criticalCurrentValues can be given as lists. The data"
1343
- " will be linearly interpolated.\nIf a CSV file is to be used, the input"
1422
+ " will be linearly interpolated.If a CSV file is to be used, the input"
1344
1423
  " should be the name of a CSV file (which is in the same folder as the"
1345
1424
  " input file) instead of a scalar. The first column of the CSV file will be"
1346
- " the tape length, and the second column will be the critical current."
1425
+ " the tape length in m, and the second column will be the critical current in A. "
1347
1426
  ),
1348
1427
  examples=[230, "IcVSlength.csv"],
1349
1428
  )
@@ -1367,40 +1446,21 @@ class Pancake3DSolveSuperconductingMaterial(Pancake3DSolveMaterialBase):
1367
1446
  " A factor of 1 means no scaling such that the HTS layer is isotropic."
1368
1447
  ),
1369
1448
  )
1370
- minimumPossibleResistivity: NonNegativeFloat = Field(
1371
- default=0,
1372
- title="Minimum Possible Resistivity",
1373
- description=(
1374
- "The resistivity of the winding won't be lower than this value, no matter"
1375
- " what."
1376
- ),
1377
- )
1378
- maximumPossibleResistivity: PositiveFloat = Field(
1379
- default=1,
1380
- title="Maximum Possible Resistivity",
1381
- description=(
1382
- "The resistivity of the winding won't be higher than this value, no matter"
1383
- " what."
1384
- ),
1385
- )
1386
1449
 
1387
1450
  IcReferenceTemperature: PositiveFloat = Field(
1388
1451
  default=77,
1389
- alias="criticalCurrentReferenceTemperature",
1390
1452
  title="Critical Current Reference Temperature",
1391
1453
  description="Critical current reference temperature in Kelvin.",
1392
1454
  )
1393
1455
 
1394
1456
  IcReferenceBmagnitude: NonNegativeFloat = Field(
1395
1457
  default=0.0,
1396
- alias="criticalCurrentReferenceFieldMagnitude",
1397
1458
  title="Critical Current Reference Magnetic Field Magnitude",
1398
1459
  description="Critical current reference magnetic field magnitude in Tesla.",
1399
1460
  )
1400
1461
 
1401
1462
  IcReferenceBangle: NonNegativeFloat = Field(
1402
1463
  default=90.0,
1403
- alias="criticalCurrentReferenceFieldAngle",
1404
1464
  title="Critical Current Reference Magnetic Field Angle",
1405
1465
  description= (
1406
1466
  "Critical current reference magnetic field angle in degrees."
@@ -1416,8 +1476,8 @@ class Pancake3DSolveSuperconductingMaterial(Pancake3DSolveMaterialBase):
1416
1476
  """Return the critical current values of the material."""
1417
1477
  if hasattr(self.IcAtTAndBref, "criticalCurrentValues"):
1418
1478
  return self.IcAtTAndBref.criticalCurrentValues
1419
- elif isinstance(self.IcAtTAndBref, str):
1420
- csv_file_path = pathlib.Path(input_file_path.get()).parent / self.IcAtTAndBref
1479
+ elif hasattr(self.IcAtTAndBref, "csvFile"):
1480
+ csv_file_path = pathlib.Path(input_file_path.get()).parent / self.IcAtTAndBref.csvFile
1421
1481
  # return the second column:
1422
1482
  IcValues = list(pd.read_csv(csv_file_path, header=None).iloc[:, 1])
1423
1483
  for Ic in IcValues:
@@ -1435,8 +1495,8 @@ class Pancake3DSolveSuperconductingMaterial(Pancake3DSolveMaterialBase):
1435
1495
  """Return the length values of the material."""
1436
1496
  if hasattr(self.IcAtTAndBref, "lengthValues"):
1437
1497
  return self.IcAtTAndBref.lengthValues
1438
- elif isinstance(self.IcAtTAndBref, str):
1439
- csv_file_path = pathlib.Path(input_file_path.get()).parent / self.IcAtTAndBref
1498
+ elif hasattr(self.IcAtTAndBref, "csvFile"):
1499
+ csv_file_path = pathlib.Path(input_file_path.get()).parent / self.IcAtTAndBref.csvFile
1440
1500
  # return the first column:
1441
1501
  lengthValues = list(pd.read_csv(csv_file_path, header=None).iloc[:, 0])
1442
1502
  for length in lengthValues:
@@ -1445,6 +1505,21 @@ class Pancake3DSolveSuperconductingMaterial(Pancake3DSolveMaterialBase):
1445
1505
  return lengthValues
1446
1506
  else:
1447
1507
  return [1]
1508
+
1509
+ @computed_field
1510
+ @cached_property
1511
+ def IcInArcLength(self) -> int:
1512
+ """Return 1 if Ic is given as a function of arc length in meters.
1513
+ Return 0 if Ic is given as a function of number of turns.
1514
+ Return -1 if Ic is given as a scalar value.
1515
+ """
1516
+ if hasattr(self.IcAtTAndBref, "lengthUnit"):
1517
+ if self.IcAtTAndBref.lengthUnit == "meter":
1518
+ return 1
1519
+ else:
1520
+ return 0
1521
+ else:
1522
+ return -1
1448
1523
 
1449
1524
  @computed_field
1450
1525
  @cached_property
@@ -1457,8 +1532,7 @@ class Pancake3DSolveSuperconductingMaterial(Pancake3DSolveMaterialBase):
1457
1532
  )
1458
1533
 
1459
1534
  return getdpCriticalCurrentDensityFunctions[self.name]
1460
-
1461
-
1535
+
1462
1536
  class Pancake3DSolveHTSMaterialBase(BaseModel):
1463
1537
  relativeThickness: float = Field(
1464
1538
  le=1,
@@ -1466,7 +1540,7 @@ class Pancake3DSolveHTSMaterialBase(BaseModel):
1466
1540
  description=(
1467
1541
  "Winding tapes generally consist of more than one material. Therefore, when"
1468
1542
  " materials are given as a list in winding, their relative thickness,"
1469
- " (thickness of the material) / (thickness of the winding), should be"
1543
+ " (thickness of the material) / (thickness of the bare conductor), should be"
1470
1544
  " specified."
1471
1545
  ),
1472
1546
  )
@@ -1622,6 +1696,33 @@ class Pancake3DSolveWindingMaterial(Pancake3DSolveMaterial):
1622
1696
  description="Material properties of the shunt layer.",
1623
1697
  )
1624
1698
 
1699
+ isotropic: Optional[bool] = Field(
1700
+ default=False,
1701
+ title="Isotropic Material",
1702
+ description=(
1703
+ "If True, resistivity and thermal conductivity are isotropic. If False, they are anisotropic. "
1704
+ "The default is anisotropic material."
1705
+ ),
1706
+ )
1707
+
1708
+ minimumPossibleResistivity: NonNegativeFloat = Field(
1709
+ default=1e-20,
1710
+ title="Minimum Possible Resistivity",
1711
+ description=(
1712
+ "The resistivity of the winding won't be lower than this value, no matter"
1713
+ " what."
1714
+ ),
1715
+ )
1716
+
1717
+ maximumPossibleResistivity: PositiveFloat = Field(
1718
+ default=0.01,
1719
+ title="Maximum Possible Resistivity",
1720
+ description=(
1721
+ "The resistivity of the winding won't be higher than this value, no matter"
1722
+ " what."
1723
+ ),
1724
+ )
1725
+
1625
1726
  @field_validator("material")
1626
1727
  @classmethod
1627
1728
  def checkIfRelativeThicknessesSumToOne(cls, material):
@@ -1698,6 +1799,58 @@ class Pancake3DSolveWindingMaterial(Pancake3DSolveMaterial):
1698
1799
  "There should be only one superconductor in the winding!"
1699
1800
  )
1700
1801
 
1802
+ class Pancake3DTerminalCryocoolerLumpedMass(Pancake3DSolveMaterial):
1803
+
1804
+ material: Optional[Pancake3DSolveNormalMaterial] = Field(
1805
+ default=Pancake3DSolveNormalMaterial(name="Copper", RRR=295),
1806
+ title="Material",
1807
+ description="Material from STEAM material library.",
1808
+ )
1809
+
1810
+ volume: NonNegativeFloat = Field(
1811
+ default=0,
1812
+ title="Cryocooler Lumped Block Volume",
1813
+ description=(
1814
+ "Volume of the lumped thermal mass between second stage of the cryocooler and pancake coil in m^3. "
1815
+ "A zero value effectively disables the lumped thermal mass between second stage of the cryocooler and pancake coil."
1816
+ )
1817
+ )
1818
+
1819
+ numberOfThinShellElements: PositiveInt = Field(
1820
+ default=1,
1821
+ title="Number of Thin Shell Elements for Cryocooler Lumped Mass",
1822
+ description=(
1823
+ "Number of thin shell elements in the FE formulation (GetDP related, not"
1824
+ " physical and only used when TSA is set to True)"
1825
+ ),
1826
+ )
1827
+
1828
+ class Pancake3DTerminalCryocoolerBoundaryCondition(BaseModel):
1829
+
1830
+ coolingPowerMultiplier: NonNegativeFloat = Field(
1831
+ default=1,
1832
+ title="Cooling Power Multiplier",
1833
+ description=(
1834
+ "Multiplier for the cooling power. It can be used to scale"
1835
+ " the cooling power given by the coldhead capacity map by a non-negative float factor."
1836
+ ),
1837
+ )
1838
+
1839
+ staticHeatLoadPower: NonNegativeFloat = Field(
1840
+ default=0,
1841
+ title="Static Heat Load Power",
1842
+ description=(
1843
+ "Static heat load power in W. It can be used to add a static heat load"
1844
+ " to the cryocooler, i.e., decrease the power available for cooling. "
1845
+ " The actual cooling power is P(t) = P_cryocooler(T) - P_staticLoad."
1846
+ ),
1847
+ )
1848
+
1849
+ lumpedMass: Pancake3DTerminalCryocoolerLumpedMass = Field(
1850
+ default = Pancake3DTerminalCryocoolerLumpedMass(),
1851
+ title="Cryocooler Lumped Mass",
1852
+ description="Thermal lumped mass between second stage of the cryocooler and pancake coil modeled via TSA.",
1853
+ )
1701
1854
 
1702
1855
  class Pancake3DSolveTerminalMaterialAndBoundaryCondition(Pancake3DSolveMaterial):
1703
1856
  cooling: Literal["adiabatic", "fixedTemperature", "cryocooler"] = Field(
@@ -1708,6 +1861,13 @@ class Pancake3DSolveTerminalMaterialAndBoundaryCondition(Pancake3DSolveMaterial)
1708
1861
  " temperature, or cryocooler."
1709
1862
  ),
1710
1863
  )
1864
+
1865
+ cryocoolerOptions: Optional[Pancake3DTerminalCryocoolerBoundaryCondition] = Field(
1866
+ default=Pancake3DTerminalCryocoolerBoundaryCondition(),
1867
+ title="Cryocooler Boundary Condition",
1868
+ description="Additional inputs for the cryocooler boundary condition.",
1869
+ )
1870
+
1711
1871
  transitionNotch: Pancake3DSolveMaterial = Field(
1712
1872
  title="Transition Notch Properties",
1713
1873
  description="Material properties of the transition notch volume.",
@@ -1736,7 +1896,6 @@ class Pancake3DSolveToleranceBase(BaseModel):
1736
1896
  normType: Literal["L1Norm", "MeanL1Norm", "L2Norm", "MeanL2Norm", "LinfNorm"] = (
1737
1897
  Field(
1738
1898
  default="L2Norm",
1739
- alias="normType",
1740
1899
  title="Norm Type",
1741
1900
  description=(
1742
1901
  "Sometimes, tolerances return a vector instead of a scalar (ex,"
@@ -1799,7 +1958,7 @@ class Pancake3DSolveSettingsWithTolerances(BaseModel):
1799
1958
  if "SolutionVector" not in tolerance.quantity
1800
1959
  ]
1801
1960
  return tolerances
1802
-
1961
+
1803
1962
 
1804
1963
  @computed_field
1805
1964
  @cached_property
@@ -1811,17 +1970,15 @@ class Pancake3DSolveSettingsWithTolerances(BaseModel):
1811
1970
  if "SolutionVector" in tolerance.quantity
1812
1971
  ]
1813
1972
  return tolerances
1814
-
1973
+
1815
1974
 
1816
1975
  class Pancake3DSolveAdaptiveTimeLoopSettings(Pancake3DSolveSettingsWithTolerances):
1817
1976
  # Mandatory:
1818
1977
  initialStep: PositiveFloat = Field(
1819
- alias="initialStep",
1820
1978
  title="Initial Step for Adaptive Time Stepping",
1821
1979
  description="Initial step for adaptive time stepping",
1822
1980
  )
1823
1981
  minimumStep: PositiveFloat = Field(
1824
- alias="minimumStep",
1825
1982
  title="Minimum Step for Adaptive Time Stepping",
1826
1983
  description=(
1827
1984
  "The simulation will be aborted if a finer time step is required than this"
@@ -1829,7 +1986,6 @@ class Pancake3DSolveAdaptiveTimeLoopSettings(Pancake3DSolveSettingsWithTolerance
1829
1986
  ),
1830
1987
  )
1831
1988
  maximumStep: PositiveFloat = Field(
1832
- alias="maximumStep",
1833
1989
  description="Bigger steps than this won't be allowed",
1834
1990
  )
1835
1991
 
@@ -1838,13 +1994,11 @@ class Pancake3DSolveAdaptiveTimeLoopSettings(Pancake3DSolveSettingsWithTolerance
1838
1994
  "Euler", "Gear_2", "Gear_3", "Gear_4", "Gear_5", "Gear_6"
1839
1995
  ] = Field(
1840
1996
  default="Euler",
1841
- alias="integrationMethod",
1842
1997
  title="Integration Method",
1843
1998
  description="Integration method for transient analysis",
1844
1999
  )
1845
2000
  breakPoints_input: list[float] = Field(
1846
2001
  default=[0],
1847
- alias="breakPoints",
1848
2002
  title="Break Points for Adaptive Time Stepping",
1849
2003
  description="Make sure to solve the system for these times.",
1850
2004
  )
@@ -1951,7 +2105,6 @@ class Pancake3DSolveTimeBase(BaseModel):
1951
2105
  # Optionals:
1952
2106
  extrapolationOrder: Literal[0, 1, 2, 3] = Field(
1953
2107
  default=1,
1954
- alias="extrapolationOrder",
1955
2108
  title="Extrapolation Order",
1956
2109
  description=(
1957
2110
  "Before solving for the next time steps, the previous solutions can be"
@@ -1974,8 +2127,7 @@ class Pancake3DSolveTimeBase(BaseModel):
1974
2127
 
1975
2128
  class Pancake3DSolveTimeAdaptive(Pancake3DSolveTimeBase):
1976
2129
  timeSteppingType: Literal["adaptive"] = "adaptive"
1977
- adaptive: Pancake3DSolveAdaptiveTimeLoopSettings = Field(
1978
- alias="adaptiveSteppingSettings",
2130
+ adaptiveSteppingSettings: Pancake3DSolveAdaptiveTimeLoopSettings = Field(
1979
2131
  title="Adaptive Time Loop Settings",
1980
2132
  description=(
1981
2133
  "Adaptive time loop settings (only used if stepping type is adaptive)."
@@ -1988,7 +2140,7 @@ class Pancake3DSolveTimeAdaptive(Pancake3DSolveTimeBase):
1988
2140
  """
1989
2141
  Checks if the time steps are consistent.
1990
2142
  """
1991
- if model.adaptive.initialStep > model.end:
2143
+ if model.adaptiveSteppingSettings.initialStep > model.end:
1992
2144
  raise ValueError("Initial time step cannot be greater than the end time!")
1993
2145
 
1994
2146
  return model
@@ -1996,10 +2148,9 @@ class Pancake3DSolveTimeAdaptive(Pancake3DSolveTimeBase):
1996
2148
 
1997
2149
  class Pancake3DSolveTimeFixed(Pancake3DSolveTimeBase):
1998
2150
  timeSteppingType: Literal["fixed"] = "fixed"
1999
- fixed: (
2151
+ fixedSteppingSettings: (
2000
2152
  list[Pancake3DSolveFixedLoopInterval] | Pancake3DSolveFixedTimeLoopSettings
2001
2153
  ) = Field(
2002
- alias="fixedSteppingSettings",
2003
2154
  title="Fixed Time Loop Settings",
2004
2155
  description="Fixed time loop settings (only used if stepping type is fixed).",
2005
2156
  )
@@ -2007,27 +2158,27 @@ class Pancake3DSolveTimeFixed(Pancake3DSolveTimeBase):
2007
2158
  @model_validator(mode="after")
2008
2159
  @classmethod
2009
2160
  def check_time_steps(cls, model: "Pancake3DSolveTimeFixed"):
2010
- if isinstance(model.fixed, list):
2011
- for i in range(len(model.fixed) - 1):
2012
- if model.fixed[i].endTime != model.fixed[i + 1].startTime:
2161
+ if isinstance(model.fixedSteppingSettings, list):
2162
+ for i in range(len(model.fixedSteppingSettings) - 1):
2163
+ if model.fixedSteppingSettings[i].endTime != model.fixedSteppingSettings[i + 1].startTime:
2013
2164
  raise ValueError(
2014
2165
  "End time of the previous interval must be equal to the start"
2015
2166
  " time of the next interval!"
2016
2167
  )
2017
2168
 
2018
- if model.fixed[0].startTime != model.start:
2169
+ if model.fixedSteppingSettings[0].startTime != model.start:
2019
2170
  raise ValueError(
2020
2171
  "Start time of the first interval must be equal to the start time"
2021
2172
  " of the simulation!"
2022
2173
  )
2023
2174
 
2024
2175
  else:
2025
- if model.fixed.step > model.end:
2176
+ if model.fixedSteppingSettings.step > model.end:
2026
2177
  raise ValueError("Time step cannot be greater than the end time!")
2027
2178
 
2028
2179
  if not (
2029
2180
  math.isclose(
2030
- (model.end - model.start) % model.fixed.step, 0, abs_tol=1e-8
2181
+ (model.end - model.start) % model.fixedSteppingSettings.step, 0, abs_tol=1e-8
2031
2182
  )
2032
2183
  ):
2033
2184
  raise ValueError("Time interval must be a multiple of the time step!")
@@ -2041,16 +2192,14 @@ Pancake3DSolveTime = Annotated[
2041
2192
 
2042
2193
  class Pancake3DSolveNonlinearSolverSettings(Pancake3DSolveSettingsWithTolerances):
2043
2194
  # Optionals:
2044
- maxIter: PositiveInt = Field(
2195
+ maximumNumberOfIterations: PositiveInt = Field(
2045
2196
  default=100,
2046
- alias="maximumNumberOfIterations",
2047
2197
  title="Maximum Number of Iterations",
2048
2198
  description="Maximum number of iterations allowed for the nonlinear solver.",
2049
2199
  )
2050
2200
  relaxationFactor: float = Field(
2051
2201
  default=0.7,
2052
2202
  gt=0,
2053
- alias="relaxationFactor",
2054
2203
  title="Relaxation Factor",
2055
2204
  description=(
2056
2205
  "Calculated step changes of the solution vector will be multiplied with"
@@ -2063,33 +2212,34 @@ class Pancake3DSolveInitialConditions(BaseModel):
2063
2212
  # 1) User inputs:
2064
2213
 
2065
2214
  # Mandatory:
2066
- T: PositiveFloat = Field(
2067
- alias="temperature",
2215
+ temperature: PositiveFloat = Field(
2068
2216
  title="Initial Temperature",
2069
2217
  description="Initial temperature of the pancake coils.",
2070
2218
  )
2071
2219
 
2220
+ class Pancake3DSolveImposedField(BaseModel):
2221
+
2222
+ imposedAxialField: float = Field(
2223
+ title="Imposed Axial Magnetic Field",
2224
+ description="Imposed axial magnetic field in Tesla. Only constant, purely axial magnetic fields are supported at the moment.",
2225
+ )
2072
2226
 
2073
2227
  class Pancake3DSolveLocalDefect(BaseModel):
2074
2228
  # Mandatory:
2075
2229
  value: NonNegativeFloat = Field(
2076
- alias="value",
2077
2230
  title="Value",
2078
2231
  description="Value of the local defect.",
2079
2232
  )
2080
2233
  startTurn: NonNegativeFloat = Field(
2081
- alias="startTurn",
2082
2234
  title="Start Turn",
2083
2235
  description="Start turn of the local defect.",
2084
2236
  )
2085
2237
  endTurn: PositiveFloat = Field(
2086
- alias="endTurn",
2087
2238
  title="End Turn",
2088
2239
  description="End turn of the local defect.",
2089
2240
  )
2090
2241
 
2091
2242
  startTime: NonNegativeFloat = Field(
2092
- alias="startTime",
2093
2243
  title="Start Time",
2094
2244
  description="Start time of the local defect.",
2095
2245
  )
@@ -2161,7 +2311,7 @@ class Pancake3DSolveLocalDefect(BaseModel):
2161
2311
  "whickPancakeCoil (pancake coil number) should be given if there"
2162
2312
  " are more than one pancake coil!"
2163
2313
  )
2164
-
2314
+
2165
2315
  return whichPancakeCoil
2166
2316
 
2167
2317
  @computed_field
@@ -2194,17 +2344,16 @@ class Pancake3DSolveLocalDefect(BaseModel):
2194
2344
  class Pancake3DSolveLocalDefects(BaseModel):
2195
2345
  # 1) User inputs:
2196
2346
 
2197
- jCritical: Optional[Pancake3DSolveLocalDefect] = Field(
2347
+ criticalCurrentDensity: Optional[Pancake3DSolveLocalDefect] = Field(
2198
2348
  default=None,
2199
- alias="criticalCurrentDensity",
2200
2349
  title="Local Defect for Critical Current Density",
2201
2350
  description="Set critical current density locally.",
2202
2351
  )
2203
2352
 
2204
- @field_validator("jCritical")
2353
+ @field_validator("criticalCurrentDensity")
2205
2354
  @classmethod
2206
- def check_jCritical_local_defect(cls, jCritical):
2207
- if jCritical is not None:
2355
+ def check_criticalCurrentDensity_local_defect(cls, criticalCurrentDensity):
2356
+ if criticalCurrentDensity is not None:
2208
2357
  solve = solve_input.get()
2209
2358
 
2210
2359
  if "material" in solve["winding"]:
@@ -2214,15 +2363,164 @@ class Pancake3DSolveLocalDefects(BaseModel):
2214
2363
  else:
2215
2364
  windingMaterials = []
2216
2365
 
2217
- superconducting_material_is_used = "HTSSuperPower" in windingMaterials or "HTSFujikura" in windingMaterials
2366
+ superconducting_material_is_used = "HTSSuperPower" in windingMaterials or "HTSFujikura" in windingMaterials or "HTSSucci" in windingMaterials
2218
2367
 
2219
2368
  if not superconducting_material_is_used:
2220
2369
  raise ValueError(
2221
2370
  "Local defects can only be set if superconducting material is used!"
2222
2371
  )
2223
2372
 
2224
- return jCritical
2373
+ return criticalCurrentDensity
2374
+
2375
+
2376
+ class Pancake3DSolveConvectiveCooling(BaseModel):
2377
+
2378
+ heatTransferCoefficient: Optional[NonNegativeFloat] = Field(
2379
+ default=0,
2380
+ title="Heat Transfer Coefficient",
2381
+ description=(
2382
+ "The heat transfer coefficient for the heat transfer between the winding and the air. "
2383
+ "If zero, no heat transfer to the air is considered."
2384
+ "This feature is only implemented for the thin shell approximation."
2385
+ "At the moment, only constant values are supported."
2386
+ ),
2387
+ )
2388
+
2389
+ exteriorBathTemperature: Optional[NonNegativeFloat] = Field(
2390
+ default=4.2,
2391
+ title="Exterior Bath Temperature",
2392
+ description=(
2393
+ "The temperature of the exterior bath for convective cooling boundary condition. "
2394
+ ),
2395
+ )
2396
+
2397
+ class Pancake3DSolveEECircuit(BaseModel):
2398
+
2399
+ inductanceInSeriesWithPancakeCoil: Optional[NonNegativeFloat] = Field(
2400
+ default=0,
2401
+ title="Inductance in Series with Pancake Coil",
2402
+ description=(
2403
+ "A lumped inductance in series with the pancake coil to model a bigger coil. "
2404
+ ),
2405
+ )
2225
2406
 
2407
+ enable: bool = Field(
2408
+ default=False,
2409
+ title="Enable Detection Circuit",
2410
+ description=(
2411
+ "Enable the detection circuit for the pancake coil. "
2412
+ ),
2413
+ )
2414
+
2415
+ ResistanceEnergyExtractionOpenSwitch: Optional[NonNegativeFloat] = Field(
2416
+ default=1E6,
2417
+ title="Resistance of Energy Extraction Open Switch",
2418
+ description=(
2419
+ "The resistance of the energy extraction switch when modeled as open. "
2420
+ ),
2421
+ )
2422
+
2423
+ ResistanceEnergyExtractionClosedSwitch: Optional[NonNegativeFloat] = Field(
2424
+ default=1E-6,
2425
+ title="Resistance of Energy Extraction Closed Switch",
2426
+ description=(
2427
+ "The resistance of the energy extraction switch when modeled as closed. "
2428
+ ),
2429
+ )
2430
+
2431
+ ResistanceCrowbarOpenSwitch: Optional[NonNegativeFloat] = Field(
2432
+ default=1E6,
2433
+ title="Resistance of Crowbar Open Switch",
2434
+ description=(
2435
+ "The resistance of the crowbar switch when modeled as open. "
2436
+ ),
2437
+ )
2438
+
2439
+ ResistanceCrowbarClosedSwitch: Optional[NonNegativeFloat] = Field(
2440
+ default=1E-6,
2441
+ title="Resistance of Crowbar Closed Switch",
2442
+ description=(
2443
+ "The resistance of the crowbar switch when modeled as closed. "
2444
+ ),
2445
+ )
2446
+
2447
+ stopSimulationAtCurrent: Optional[NonNegativeFloat] = Field(
2448
+ default=0,
2449
+ title="Stop Simulation at Current",
2450
+ description=(
2451
+ "If a quench is detected and the current reaches this value, the simulation will be stopped after. "
2452
+ "stopSimulationWaitingTime seconds."
2453
+ ),
2454
+ )
2455
+
2456
+ stopSimulationWaitingTime: Optional[NonNegativeFloat] = Field(
2457
+ default=0,
2458
+ title="Stop Simulation Waiting Time",
2459
+ description=(
2460
+ "The time to wait after a quench is detected and the current reaches stopSimulationAtCurrent before stopping the simulation."
2461
+ ),
2462
+ )
2463
+
2464
+ # or use t_off from power supply? I don't like it since it's used as time value to turn off and not time interval.
2465
+ TurnOffDeltaTimePowerSupply: Optional[NonNegativeFloat] = Field(
2466
+ default=0,
2467
+ title="Time Interval Between Quench Detection and Power Supply Turn Off",
2468
+ description=(
2469
+ "The time it takes for the power supply to be turned off after quench detection. "
2470
+ "A linear ramp-down is assumed between the time of quench detection and the time of power supply turn off."
2471
+ ),
2472
+ )
2473
+
2474
+ class Pancake3DSolvePowerDensity(BaseModel):
2475
+ power: Optional[NonNegativeFloat] = Field(
2476
+ default=0,
2477
+ title="Power Density",
2478
+ description=(
2479
+ "The power in W for an imposed power density in the winding. "
2480
+ "'startTime', 'endTime', 'startTurn', and 'endTurn' "
2481
+ "are also required to be set."
2482
+ ),
2483
+ )
2484
+
2485
+ startTime: Optional[NonNegativeFloat] = Field(
2486
+ default=0,
2487
+ title="Power Density Start Time",
2488
+ description=(
2489
+ "The start time for the imposed power density in the winding. "
2490
+ "'power', 'endTime', 'startTurn', and 'endTurn' "
2491
+ "are also required to be set."
2492
+ ),
2493
+ )
2494
+
2495
+ endTime: Optional[NonNegativeFloat] = Field(
2496
+ default=0,
2497
+ title="Power Density End Time",
2498
+ description=(
2499
+ "The end time for the imposed power density in the winding. "
2500
+ "'power', 'startTime', 'startTurn', and 'endTurn' "
2501
+ "are also required to be set."
2502
+ ),
2503
+ )
2504
+
2505
+ startArcLength: Optional[NonNegativeFloat] = Field(
2506
+ default=0,
2507
+ title="Power Density Start Arc Length",
2508
+ description=(
2509
+ "The start arc length in m for the imposed power density in the winding. "
2510
+ "'power', 'startTime', 'endTime', and 'endArcLength' "
2511
+ "are also required to be set."
2512
+ ),
2513
+ )
2514
+
2515
+ endArcLength: Optional[NonNegativeFloat] = Field(
2516
+ default=0,
2517
+ title="Power Density End Arc Length",
2518
+ description=(
2519
+ "The end arc length in m for the imposed power density in the winding. "
2520
+ "'power', 'startTime', 'endTime', and 'startArcLength' "
2521
+ "are also required to be set."
2522
+ ),
2523
+ )
2226
2524
 
2227
2525
  class Pancake3DSolveQuantityBase(BaseModel):
2228
2526
  # Mandatory:
@@ -2252,7 +2550,7 @@ class Pancake3DSolveQuantityBase(BaseModel):
2252
2550
 
2253
2551
  class Pancake3DSolveSaveQuantity(Pancake3DSolveQuantityBase):
2254
2552
  # Optionals:
2255
- timesToBeSaved: list[float] = Field(
2553
+ timesToBeSaved: Union[list[float], None] = Field(
2256
2554
  default=None,
2257
2555
  title="Times to be Saved",
2258
2556
  description=(
@@ -2264,7 +2562,8 @@ class Pancake3DSolveSaveQuantity(Pancake3DSolveQuantityBase):
2264
2562
  @field_validator("timesToBeSaved")
2265
2563
  @classmethod
2266
2564
  def updateGlobalBreakPointsList(cls, timesToBeSaved):
2267
- all_break_points.extend(timesToBeSaved)
2565
+ if timesToBeSaved is not None:
2566
+ all_break_points.extend(timesToBeSaved)
2268
2567
  return timesToBeSaved
2269
2568
 
2270
2569
  @field_validator("timesToBeSaved")
@@ -2274,11 +2573,12 @@ class Pancake3DSolveSaveQuantity(Pancake3DSolveQuantityBase):
2274
2573
  start_time = solve["time"]["start"]
2275
2574
  end_time = solve["time"]["end"]
2276
2575
 
2277
- for time in timesToBeSaved:
2278
- if time < start_time or time > end_time:
2279
- raise ValueError(
2280
- "Times to be saved should be between the start and end times!"
2281
- )
2576
+ if timesToBeSaved is not None:
2577
+ for time in timesToBeSaved:
2578
+ if time < start_time or time > end_time:
2579
+ raise ValueError(
2580
+ "Times to be saved should be between the start and end times!"
2581
+ )
2282
2582
 
2283
2583
 
2284
2584
  # ======================================================================================
@@ -2352,11 +2652,11 @@ class Pancake3DPostprocessTimeSeriesPlotPositionNotRequired(
2352
2652
  )
2353
2653
 
2354
2654
 
2355
- Pancake3DPostprocessTimeSeriesPlot = Annotated[
2655
+ Pancake3DPostprocessTimeSeriesPlot = Optional[Annotated[
2356
2656
  Pancake3DPostprocessTimeSeriesPlotPositionRequired
2357
2657
  | Pancake3DPostprocessTimeSeriesPlotPositionNotRequired,
2358
2658
  Field(discriminator="quantity"),
2359
- ]
2659
+ ]]
2360
2660
 
2361
2661
 
2362
2662
  class Pancake3DPostprocessMagneticFieldOnPlane(BaseModel):
@@ -2379,12 +2679,12 @@ class Pancake3DPostprocessMagneticFieldOnPlane(BaseModel):
2379
2679
  default="linear",
2380
2680
  title="Interpolation Method",
2381
2681
  description=(
2382
- "Interpolation type for the plot.\nBecause of the FEM basis function"
2682
+ "Interpolation type for the plot.Because of the FEM basis function"
2383
2683
  " selections of FiQuS, each mesh element has a constant magnetic field"
2384
2684
  " vector. Therefore, for smooth 2D plots, interpolation can be"
2385
- " used.\nTypes:\nnearest: it will plot the nearest magnetic field value to"
2386
- " the plotting point.\nlinear: it will do linear interpolation to the"
2387
- " magnetic field values.\ncubic: it will do cubic interpolation to the"
2685
+ " used.Types:nearest: it will plot the nearest magnetic field value to"
2686
+ " the plotting point.linear: it will do linear interpolation to the"
2687
+ " magnetic field values.cubic: it will do cubic interpolation to the"
2388
2688
  " magnetic field values."
2389
2689
  ),
2390
2690
  )
@@ -2616,48 +2916,40 @@ class Pancake3DGeometry(BaseModel):
2616
2916
  )
2617
2917
 
2618
2918
  # Mandatory:
2619
- N: PositiveInt = Field(
2919
+ numberOfPancakes: PositiveInt = Field(
2620
2920
  ge=1,
2621
- alias="numberOfPancakes",
2622
2921
  title="Number of Pancakes",
2623
2922
  description="Number of pancake coils stacked on top of each other.",
2624
2923
  )
2625
2924
 
2626
- gap: PositiveFloat = Field(
2627
- alias="gapBetweenPancakes",
2925
+ gapBetweenPancakes: PositiveFloat = Field(
2628
2926
  title="Gap Between Pancakes",
2629
2927
  description="Gap distance between the pancake coils.",
2630
2928
  )
2631
2929
 
2632
- wi: Pancake3DGeometryWinding = Field(
2633
- alias="winding",
2930
+ winding: Pancake3DGeometryWinding = Field(
2634
2931
  title="Winding Geometry",
2635
2932
  description="This dictionary contains the winding geometry information.",
2636
2933
  )
2637
2934
 
2638
- ii: Pancake3DGeometryContactLayer = Field(
2639
- alias="contactLayer",
2935
+ contactLayer: Pancake3DGeometryContactLayer = Field(
2640
2936
  title="Contact Layer Geometry",
2641
2937
  description="This dictionary contains the contact layer geometry information.",
2642
2938
  )
2643
2939
 
2644
- ti: Pancake3DGeometryTerminals = Field(
2645
- alias="terminals",
2940
+ terminals: Pancake3DGeometryTerminals = Field(
2646
2941
  title="Terminals Geometry",
2647
2942
  description="This dictionary contains the terminals geometry information.",
2648
2943
  )
2649
2944
 
2650
- ai: Pancake3DGeometryAir = Field(
2651
- alias="air",
2945
+ air: Pancake3DGeometryAir = Field(
2652
2946
  title="Air Geometry",
2653
2947
  description="This dictionary contains the air geometry information.",
2654
2948
  )
2655
2949
 
2656
2950
  # Optionals:
2657
- dimTol: PositiveFloat = Field(
2951
+ dimensionTolerance: PositiveFloat = Field(
2658
2952
  default=1e-8,
2659
- gt=1e-5,
2660
- alias="dimensionTolerance",
2661
2953
  description="dimension tolerance (CAD related, not physical)",
2662
2954
  )
2663
2955
  pancakeBoundaryName: str = Field(
@@ -2677,34 +2969,41 @@ class Pancake3DGeometry(BaseModel):
2677
2969
 
2678
2970
  class Pancake3DMesh(BaseModel):
2679
2971
  # Mandatory:
2680
- wi: Pancake3DMeshWinding = Field(
2681
- alias="winding",
2972
+ winding: Pancake3DMeshWinding = Field(
2682
2973
  title="Winding Mesh",
2683
2974
  description="This dictionary contains the winding mesh information.",
2684
2975
  )
2685
- ii: Pancake3DMeshContactLayer = Field(
2686
- alias="contactLayer",
2976
+ contactLayer: Pancake3DMeshContactLayer = Field(
2687
2977
  title="Contact Layer Mesh",
2688
2978
  description="This dictionary contains the contact layer mesh information.",
2689
2979
  )
2690
2980
 
2691
2981
  # Optionals:
2692
- ti: Pancake3DMeshAirAndTerminals = Field(
2982
+ terminals: Pancake3DMeshAirAndTerminals = Field(
2693
2983
  default=Pancake3DMeshAirAndTerminals(),
2694
- alias="terminals",
2695
2984
  title="Terminal Mesh",
2696
2985
  description="This dictionary contains the terminal mesh information.",
2697
2986
  )
2698
- ai: Pancake3DMeshAirAndTerminals = Field(
2987
+ air: Pancake3DMeshAirAndTerminals = Field(
2699
2988
  default=Pancake3DMeshAirAndTerminals(),
2700
- alias="air",
2701
2989
  title="Air Mesh",
2702
2990
  description="This dictionary contains the air mesh information.",
2703
2991
  )
2704
2992
 
2993
+ computeCohomologyForInsulating: bool = Field(
2994
+ default=True,
2995
+ title="Compute Cohomology for Insulating",
2996
+ description=(
2997
+ "Expert option only. "
2998
+ "If False, the cohomology regions needed for simulating an insulating coil"
2999
+ "will not be computed. This will reduce the time spent for the meshing "
3000
+ "or more accurately the cohomology computing phase. BEWARE: The simulation "
3001
+ "will fail if set to False and a perfectlyInsulating coil is simulated."
3002
+ ),
3003
+ )
3004
+
2705
3005
  # Mandatory:
2706
- relSizeMin: PositiveFloat = Field(
2707
- alias="minimumElementSize",
3006
+ minimumElementSize: PositiveFloat = Field(
2708
3007
  title="Minimum Element Size",
2709
3008
  description=(
2710
3009
  "The minimum mesh element size in terms of the largest mesh size in the"
@@ -2713,8 +3012,7 @@ class Pancake3DMesh(BaseModel):
2713
3012
  " size as it gets away from the winding."
2714
3013
  ),
2715
3014
  )
2716
- relSizeMax: PositiveFloat = Field(
2717
- alias="maximumElementSize",
3015
+ maximumElementSize: PositiveFloat = Field(
2718
3016
  title="Maximum Element Size",
2719
3017
  description=(
2720
3018
  "The maximum mesh element size in terms of the largest mesh size in the"
@@ -2724,16 +3022,16 @@ class Pancake3DMesh(BaseModel):
2724
3022
  ),
2725
3023
  )
2726
3024
 
2727
- @field_validator("relSizeMax")
3025
+ @field_validator("maximumElementSize")
2728
3026
  @classmethod
2729
- def check_rel_size_max(cls, relSizeMax, info: ValidationInfo):
2730
- if relSizeMax < info.data["relSizeMin"]:
3027
+ def check_rel_size_max(cls, maximumElementSize, info: ValidationInfo):
3028
+ if maximumElementSize < info.data["minimumElementSize"]:
2731
3029
  raise ValueError(
2732
3030
  "Maximum mesh element size (maximumElementSize) cannot be smaller than"
2733
3031
  " the minimum mesh element size (minimumElementSize)!"
2734
3032
  )
2735
3033
 
2736
- return relSizeMax
3034
+ return maximumElementSize
2737
3035
 
2738
3036
  @computed_field
2739
3037
  @cached_property
@@ -2744,7 +3042,7 @@ class Pancake3DMesh(BaseModel):
2744
3042
  meshLengthsPerElement = []
2745
3043
 
2746
3044
  # azimuthal elements:
2747
- for numberOfElements in self.wi.ane:
3045
+ for numberOfElements in self.winding.azimuthalNumberOfElementsPerTurn:
2748
3046
  terminalOuterRadius = (
2749
3047
  getWindingOuterRadius()
2750
3048
  + 2 * geometry["terminals"]["outer"]["thickness"]
@@ -2754,25 +3052,25 @@ class Pancake3DMesh(BaseModel):
2754
3052
  )
2755
3053
 
2756
3054
  # radial elements:
2757
- for numberOfElements in self.wi.rne:
3055
+ for numberOfElements in self.winding.radialNumberOfElementsPerTurn:
2758
3056
  meshLengthsPerElement.append(
2759
3057
  geometry["winding"]["thickness"] / numberOfElements
2760
3058
  )
2761
3059
 
2762
3060
  if not geometry["contactLayer"]["thinShellApproximation"]:
2763
3061
  # radial elements:
2764
- for numberOfElements in self.ii.rne:
3062
+ for numberOfElements in self.contactLayer.radialNumberOfElementsPerTurn:
2765
3063
  meshLengthsPerElement.append(
2766
3064
  geometry["contactLayer"]["thickness"] / numberOfElements
2767
3065
  )
2768
3066
 
2769
3067
  # axial elements:
2770
- for numberOfElements in self.wi.axne:
3068
+ for numberOfElements in self.winding.axialNumberOfElements:
2771
3069
  meshLengthsPerElement.append(
2772
3070
  geometry["winding"]["height"] / numberOfElements
2773
3071
  )
2774
3072
 
2775
- return max(meshLengthsPerElement) * self.relSizeMin
3073
+ return max(meshLengthsPerElement) * self.minimumElementSize
2776
3074
 
2777
3075
  @computed_field
2778
3076
  @cached_property
@@ -2783,7 +3081,7 @@ class Pancake3DMesh(BaseModel):
2783
3081
  meshLengthsPerElement = []
2784
3082
 
2785
3083
  # azimuthal elements:
2786
- for numberOfElements in self.wi.ane:
3084
+ for numberOfElements in self.winding.azimuthalNumberOfElementsPerTurn:
2787
3085
  terminalOuterRadius = (
2788
3086
  getWindingOuterRadius()
2789
3087
  + 2 * geometry["terminals"]["outer"]["thickness"]
@@ -2793,25 +3091,25 @@ class Pancake3DMesh(BaseModel):
2793
3091
  )
2794
3092
 
2795
3093
  # radial elements:
2796
- for numberOfElements in self.wi.rne:
3094
+ for numberOfElements in self.winding.radialNumberOfElementsPerTurn:
2797
3095
  meshLengthsPerElement.append(
2798
3096
  geometry["winding"]["thickness"] / numberOfElements
2799
3097
  )
2800
3098
 
2801
3099
  if not geometry["contactLayer"]["thinShellApproximation"]:
2802
3100
  # radial elements:
2803
- for numberOfElements in self.ii.rne:
3101
+ for numberOfElements in self.contactLayer.radialNumberOfElementsPerTurn:
2804
3102
  meshLengthsPerElement.append(
2805
3103
  geometry["contactLayer"]["thickness"] / numberOfElements
2806
3104
  )
2807
3105
 
2808
3106
  # axial elements:
2809
- for numberOfElements in self.wi.axne:
3107
+ for numberOfElements in self.winding.axialNumberOfElements:
2810
3108
  meshLengthsPerElement.append(
2811
3109
  geometry["winding"]["height"] / numberOfElements
2812
3110
  )
2813
3111
 
2814
- return max(meshLengthsPerElement) * self.relSizeMax
3112
+ return max(meshLengthsPerElement) * self.maximumElementSize
2815
3113
 
2816
3114
  @computed_field
2817
3115
  @cached_property
@@ -2849,50 +3147,48 @@ class Pancake3DMesh(BaseModel):
2849
3147
 
2850
3148
  class Pancake3DSolve(BaseModel):
2851
3149
  # 1) User inputs:
2852
- t: Pancake3DSolveTime = Field(
2853
- alias="time",
3150
+ time: Pancake3DSolveTime = Field(
2854
3151
  title="Time Settings",
2855
3152
  description="All the time related settings for transient analysis.",
2856
3153
  )
2857
3154
 
2858
- nls: Optional[Pancake3DSolveNonlinearSolverSettings] = Field(
2859
- alias="nonlinearSolver",
3155
+ nonlinearSolver: Optional[Pancake3DSolveNonlinearSolverSettings] = Field(
2860
3156
  title="Nonlinear Solver Settings",
2861
3157
  description="All the nonlinear solver related settings.",
2862
3158
  )
2863
3159
 
2864
- wi: Pancake3DSolveWindingMaterial = Field(
2865
- alias="winding",
3160
+ winding: Pancake3DSolveWindingMaterial = Field(
2866
3161
  title="Winding Properties",
2867
3162
  description="This dictionary contains the winding material properties.",
2868
3163
  )
2869
- ii: Pancake3DSolveContactLayerMaterial = Field(
2870
- alias="contactLayer",
3164
+ contactLayer: Pancake3DSolveContactLayerMaterial = Field(
2871
3165
  title="Contact Layer Properties",
2872
3166
  description="This dictionary contains the contact layer material properties.",
2873
3167
  )
2874
- ti: Pancake3DSolveTerminalMaterialAndBoundaryCondition = Field(
2875
- alias="terminals",
3168
+ terminals: Pancake3DSolveTerminalMaterialAndBoundaryCondition = Field(
2876
3169
  title="Terminals Properties",
2877
3170
  description=(
2878
3171
  "This dictionary contains the terminals material properties and cooling"
2879
3172
  " condition."
2880
3173
  ),
2881
3174
  )
2882
- ai: Pancake3DSolveAir = Field(
2883
- alias="air",
3175
+ air: Pancake3DSolveAir = Field(
2884
3176
  title="Air Properties",
2885
3177
  description="This dictionary contains the air material properties.",
2886
3178
  )
2887
3179
 
2888
- ic: Pancake3DSolveInitialConditions = Field(
2889
- alias="initialConditions",
3180
+ initialConditions: Pancake3DSolveInitialConditions = Field(
2890
3181
  title="Initial Conditions",
2891
3182
  description="Initial conditions of the problem.",
2892
3183
  )
2893
3184
 
2894
- save: list[Pancake3DSolveSaveQuantity] = Field(
2895
- alias="quantitiesToBeSaved",
3185
+ boundaryConditions: Union[Literal["vanishingTangentialElectricField"], Pancake3DSolveImposedField] = Field(
3186
+ default="vanishingTangentialElectricField",
3187
+ title="Boundary Conditions",
3188
+ description="Boundary conditions of the problem.",
3189
+ )
3190
+
3191
+ quantitiesToBeSaved: list[Pancake3DSolveSaveQuantity] = Field(
2896
3192
  default=None,
2897
3193
  title="Quantities to be Saved",
2898
3194
  description="List of quantities to be saved.",
@@ -2903,7 +3199,7 @@ class Pancake3DSolve(BaseModel):
2903
3199
  Field(
2904
3200
  title="Simulation Type",
2905
3201
  description=(
2906
- "FiQuS/Pancake3D can solve only electromagnetics and thermal or"
3202
+ "FiQuS/Pancake3D can solve only electromagnetic and thermal or"
2907
3203
  " electromagnetic and thermal coupled. In the weaklyCoupled setting,"
2908
3204
  " thermal and electromagnetics systems will be put into different"
2909
3205
  " matrices, whereas in the stronglyCoupled setting, they all will be"
@@ -2920,7 +3216,6 @@ class Pancake3DSolve(BaseModel):
2920
3216
 
2921
3217
  localDefects: Pancake3DSolveLocalDefects = Field(
2922
3218
  default=Pancake3DSolveLocalDefects(),
2923
- alias="localDefects",
2924
3219
  title="Local Defects",
2925
3220
  description=(
2926
3221
  "Local defects (like making a small part of the winding normal conductor at"
@@ -2934,7 +3229,9 @@ class Pancake3DSolve(BaseModel):
2934
3229
  description=(
2935
3230
  "The simulation is continued from an existing .res file. The .res file is"
2936
3231
  " from a previous computation on the same geometry and mesh. The .res file"
2937
- " is taken from the folder Solution_<<initFromPrevious>>"
3232
+ " is taken from the folder Solution_<<initFromPrevious>>."
3233
+ " IMPORTANT: When the option is used, the start time should be identical to the last "
3234
+ " time value for the <<initFromPrevious>> simulation."
2938
3235
  ),
2939
3236
  )
2940
3237
 
@@ -2948,15 +3245,101 @@ class Pancake3DSolve(BaseModel):
2948
3245
  ),
2949
3246
  )
2950
3247
 
3248
+ voltageTapPositions: Optional[list[Pancake3DPosition]] = Field(
3249
+ default=[],
3250
+ title="Voltage Tap Positions",
3251
+ description=(
3252
+ "List of voltage tap positions. The "
3253
+ "position can be given in the form of a list of [x, y, z] coordinates or "
3254
+ "as turnNumber and number of pancake coil."
3255
+ ),
3256
+ )
3257
+
3258
+ EECircuit: Optional[Pancake3DSolveEECircuit] = Field(
3259
+ default = Pancake3DSolveEECircuit(),
3260
+ title="Detection Circuit",
3261
+ description=(
3262
+ "This dictionary contains the detection circuit settings."
3263
+ ),
3264
+ )
3265
+
3266
+ noOfMPITasks: Optional[Union[bool, int]] = Field(
3267
+ default=False,
3268
+ title="No. of tasks for MPI parallel run of GetDP",
3269
+ description=(
3270
+ "If integer, GetDP will be run in parallel using MPI. This is only valid"
3271
+ " if MPI is installed on the system and an MPI-enabled GetDP is used."
3272
+ " If False, GetDP will be run in serial without invoking mpiexec."
3273
+ ),
3274
+ )
3275
+
3276
+ resistiveHeatingTerminals: bool = Field(
3277
+ default=True,
3278
+ title="Resistive Heating in Terminals",
3279
+ description=(
3280
+ "If True, terminals are subject to Joule heating. If False, terminal regions are"
3281
+ " not subject to Joule heating. In both cases, heat conduction through the terminal is "
3282
+ " considered."
3283
+ ),
3284
+ )
3285
+
3286
+ heatFlowBetweenTurns: bool = Field(
3287
+ default=True,
3288
+ title="Heat Equation Between Turns",
3289
+ description=(
3290
+ "If True, heat flow between turns is considered. If False, it is not considered. "
3291
+ "In the latter case, heat conduction is only considered to the middle of the winding in the thin shell approximation "
3292
+ "in order to keep the thermal mass of the insulation included. In the middle between the turns, an adiabatic condition is applied. "
3293
+ "Between the turns refers to the region between the winding turns, NOT to the region between terminals "
3294
+ "and the first and last turn. "
3295
+ "This feature is only implemented for the thin shell approximation."
3296
+ ),
3297
+ )
3298
+
3299
+ convectiveCooling: Optional[Pancake3DSolveConvectiveCooling] = Field(
3300
+ default=Pancake3DSolveConvectiveCooling(),
3301
+ title="Convective Cooling",
3302
+ description=(
3303
+ "This dictionary contains the convective cooling settings."
3304
+ ),
3305
+ )
3306
+
3307
+ imposedPowerDensity: Optional[Pancake3DSolvePowerDensity] = Field(
3308
+ default=None,
3309
+ title="Power Density",
3310
+ description=(
3311
+ "The power density for an imposed power density in the winding."
3312
+ ),
3313
+ )
3314
+
3315
+ materialParametersUseCoilField: bool = Field(
3316
+ default=True,
3317
+ title="Use Coil Field for Critical Current",
3318
+ description=(
3319
+ "If True, the total field (i.e., coil field plus potentially imposed field)"
3320
+ "will be used for the material (default)."
3321
+ "If False, only the imposed field (can be zero) will be used."
3322
+ ),
3323
+ )
3324
+
3325
+ stopWhenTemperatureReaches: Optional[float] = Field(
3326
+ default=0,
3327
+ title="Stop When Temperature Reaches",
3328
+ description=(
3329
+ "If the maximum temperature reaches this value, the simulation will"
3330
+ " be stopped."
3331
+ ),
3332
+ )
3333
+
2951
3334
  @computed_field
2952
3335
  @cached_property
2953
3336
  def systemsOfEquationsType(self) -> str:
2954
3337
 
2955
- windingMaterialIsGiven = self.wi.material is not None
2956
- contactLayerMaterialIsGiven = self.ii.material is not None
2957
- terminalMaterialIsGiven = self.ti.material is not None
2958
- notchMaterialIsGiven = self.ti.transitionNotch.material is not None
2959
- terminalContactLayerMaterialIsGiven = self.ti.terminalContactLayer.material is not None
3338
+ windingMaterialIsGiven = self.winding.material is not None
3339
+ contactLayerMaterialIsGiven = self.contactLayer.material is not None
3340
+ terminalMaterialIsGiven = self.terminals.material is not None
3341
+ notchMaterialIsGiven = self.terminals.transitionNotch.material is not None
3342
+ terminalContactLayerMaterialIsGiven = self.terminals.terminalContactLayer.material is not None
2960
3343
 
2961
3344
  if (
2962
3345
  windingMaterialIsGiven
@@ -2970,14 +3353,14 @@ class Pancake3DSolve(BaseModel):
2970
3353
  systemsOfEquationsType = "linear"
2971
3354
 
2972
3355
  return systemsOfEquationsType
2973
-
3356
+
2974
3357
  @model_validator(mode="before")
2975
3358
  @classmethod
2976
3359
  def check_nls_system_tolerances(cls, model: "Pancake3DSolve"):
2977
-
3360
+
2978
3361
  if not "nonlinearSolver" in model or not "tolerances" in model["nonlinearSolver"]:
2979
3362
  return model
2980
-
3363
+
2981
3364
  all_tolerances = model["nonlinearSolver"]["tolerances"]
2982
3365
 
2983
3366
  for tolerance in all_tolerances:
@@ -2988,7 +3371,7 @@ class Pancake3DSolve(BaseModel):
2988
3371
  "The 'electromagneticSolutionVector' tolerance can be used only"
2989
3372
  " in 'electromagnetic' or 'weaklyCoupled' simulations."
2990
3373
  )
2991
-
3374
+
2992
3375
  if tolerance["quantity"] == "thermalSolutionVector":
2993
3376
  if model["type"] == "electromagnetic" or model["type"] == "stronglyCoupled":
2994
3377
  raise ValueError(
@@ -2996,7 +3379,7 @@ class Pancake3DSolve(BaseModel):
2996
3379
  "The 'thermalSolutionVector' tolerance can be used only"
2997
3380
  " in 'thermal' or 'weaklyCoupled' simulations."
2998
3381
  )
2999
-
3382
+
3000
3383
  if tolerance["quantity"] == "coupledSolutionVector":
3001
3384
  if model["type"] == "electromagnetic" or model["type"] == "thermal" or model["type"] == "weaklyCoupled":
3002
3385
  raise ValueError(
@@ -3005,11 +3388,11 @@ class Pancake3DSolve(BaseModel):
3005
3388
  " in 'stronglyCoupled' simulations."
3006
3389
  )
3007
3390
  return model
3008
-
3391
+
3009
3392
  @model_validator(mode="before")
3010
3393
  @classmethod
3011
3394
  def check_adaptive_system_tolerances(cls, model: "Pancake3DSolve"):
3012
-
3395
+
3013
3396
  if model["time"]["timeSteppingType"] == "fixed":
3014
3397
  return model
3015
3398
 
@@ -3023,7 +3406,7 @@ class Pancake3DSolve(BaseModel):
3023
3406
  "The 'electromagneticSolutionVector' tolerance can be used only"
3024
3407
  " in 'electromagnetic' or 'weaklyCoupled' simulations."
3025
3408
  )
3026
-
3409
+
3027
3410
  if tolerance["quantity"] == "thermalSolutionVector":
3028
3411
  if model["type"] == "electromagnetic" or model["type"] == "stronglyCoupled":
3029
3412
  raise ValueError(
@@ -3031,7 +3414,7 @@ class Pancake3DSolve(BaseModel):
3031
3414
  "The 'thermalSolutionVector' tolerance can be used only"
3032
3415
  " in 'thermal' or 'weaklyCoupled' simulations."
3033
3416
  )
3034
-
3417
+
3035
3418
  if tolerance["quantity"] == "coupledSolutionVector":
3036
3419
  if model["type"] == "electromagnetic" or model["type"] == "thermal" or model["type"] == "weaklyCoupled":
3037
3420
  raise ValueError(
@@ -3040,7 +3423,83 @@ class Pancake3DSolve(BaseModel):
3040
3423
  " in 'stronglyCoupled' simulations."
3041
3424
  )
3042
3425
  return model
3426
+
3427
+ @model_validator(mode="before")
3428
+ @classmethod
3429
+ def check_convective_cooling(cls, model: "Pancake3DSolve"):
3430
+ if "convectiveCooling" in model:
3431
+
3432
+ convectiveCooling = model["convectiveCooling"]
3433
+
3434
+ if not "heatTransferCoefficient" in convectiveCooling or not "exteriorBathTemperature" in convectiveCooling:
3435
+ raise ValueError(
3436
+ "If convective cooling is turned on, the heatTransferCoefficient and exteriorBathTemperature must be provided."
3437
+ )
3043
3438
 
3439
+ return model
3440
+
3441
+ @model_validator(mode="before")
3442
+ @classmethod
3443
+ def check_power_density(cls, model: "Pancake3DSolve"):
3444
+ if "imposedPowerDensity" in model:
3445
+
3446
+ powerDensity = model["imposedPowerDensity"]
3447
+
3448
+ if powerDensity:
3449
+ if not "startTime" in powerDensity or not "endTime" in powerDensity or not "startArcLength" in powerDensity or not "endArcLength" in powerDensity or not "power" in powerDensity:
3450
+ raise ValueError(
3451
+ "If the power density provided, the power, startTime, endTime, startTurn, and endTurn must be provided."
3452
+ )
3453
+
3454
+ return model
3455
+
3456
+ @model_validator(mode="before")
3457
+ @classmethod
3458
+ def check_heat_equation_between_turns(cls, model: "Pancake3DSolve"):
3459
+ if "heatFlowBetweenTurns" in model and not model["heatFlowBetweenTurns"]:
3460
+
3461
+ geometry = geometry_input.get()
3462
+
3463
+ if not geometry["contactLayer"]["thinShellApproximation"]:
3464
+ raise ValueError(
3465
+ "The heat equation between turns can only be turned off in the thin shell approximation."
3466
+ )
3467
+
3468
+ if not model["contactLayer"]["resistivity"] == "perfectlyInsulating":
3469
+ raise ValueError(
3470
+ "The heat equation between turns can only be turned off for perfectly insulated coils. That is, coils with contactLayer resistivity set to 'perfectlyInsulating'."
3471
+ )
3472
+
3473
+ return model
3474
+
3475
+ @model_validator(mode="before")
3476
+ @classmethod
3477
+ def check_voltage_taps(cls, model: "Pancake3DSolve"):
3478
+
3479
+ if "voltageTapPositions" in model:
3480
+ if len(model['voltageTapPositions'])>0:
3481
+ if model["type"] == "thermal":
3482
+ raise ValueError(
3483
+ "Voltage taps can only be used in electromagnetic simulations."
3484
+ )
3485
+
3486
+ if not model["contactLayer"]["resistivity"] == "perfectlyInsulating":
3487
+ raise ValueError(
3488
+ "For now, voltage taps can only be used for perfectly insulated coils. That is, coils with contactLayer resistivity set to 'perfectlyInsulating'."
3489
+ )
3490
+
3491
+ geometry = geometry_input.get()
3492
+
3493
+ if not geometry["contactLayer"]["thinShellApproximation"]:
3494
+ raise ValueError(
3495
+ "Voltage taps are only available in the thin shell approximation for now."
3496
+ )
3497
+
3498
+
3499
+ return model
3500
+
3501
+ # TODO: add model_validator to check if MPI is installed on the system and an MPI-enabled GetDP is used if useMPI is True
3502
+
3044
3503
  # TODO: add model_validator to check postprocess quantities are available for this solve type (e.g. thermal quantities cannot be chosen for electromagnetic solve)
3045
3504
 
3046
3505
  # TODO: add model_validator to check convergence quantities are available for this solve type (e.g. thermal quantities cannot be chosen for electromagnetic solve)
@@ -3051,13 +3510,13 @@ class Pancake3DPostprocess(BaseModel):
3051
3510
  """
3052
3511
 
3053
3512
  # 1) User inputs:
3054
- timeSeriesPlots: list[Pancake3DPostprocessTimeSeriesPlot] = Field(
3513
+ timeSeriesPlots: Optional[list[Pancake3DPostprocessTimeSeriesPlot]] = Field(
3055
3514
  default=None,
3056
3515
  title="Time Series Plots",
3057
3516
  description="Values can be plotted with respect to time.",
3058
3517
  )
3059
3518
 
3060
- magneticFieldOnCutPlane: Pancake3DPostprocessMagneticFieldOnPlane = Field(
3519
+ magneticFieldOnCutPlane: Optional[Pancake3DPostprocessMagneticFieldOnPlane] = Field(
3061
3520
  default=None,
3062
3521
  title="Magnetic Field on a Cut Plane",
3063
3522
  description=(
@@ -3089,7 +3548,7 @@ class Pancake3D(BaseModel):
3089
3548
  description="This dictionary contains the solve information.",
3090
3549
  )
3091
3550
  postproc: Pancake3DPostprocess = Field(
3092
- default=None,
3551
+ default=Pancake3DPostprocess(),
3093
3552
  title="Postprocess",
3094
3553
  description="This dictionary contains the postprocess information.",
3095
3554
  )