Skip to content

Commit c81335a

Browse files
authored
Merge branch 'main' into fix-issue-21
2 parents 7d45542 + 9a61373 commit c81335a

7 files changed

Lines changed: 986 additions & 548 deletions

File tree

examples/example05 - evolutive_forward_solve.ipynb

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,44 @@
244244
")"
245245
]
246246
},
247+
{
248+
"cell_type": "markdown",
249+
"metadata": {},
250+
"source": [
251+
"#### Set active coil voltages\n",
252+
"\n",
253+
"In this example, we will evolve a plasma in absence of any control policy or current drive.\n",
254+
"\n",
255+
"Just as an example, the following calculates active voltages to be applied to the poloidal field coils (and Solenoid) using $V = RI$, with current values as defined by the initial equilibrium (i.e. we have constant voltages).\n",
256+
"\n",
257+
"In most FreeGSNKE use cases, these active voltages will be determined by a control policy."
258+
]
259+
},
260+
{
261+
"cell_type": "code",
262+
"execution_count": null,
263+
"metadata": {},
264+
"outputs": [],
265+
"source": [
266+
"voltages = (stepping.vessel_currents_vec*stepping.evol_metal_curr.coil_resist)[:stepping.evol_metal_curr.n_active_coils] "
267+
]
268+
},
269+
{
270+
"cell_type": "markdown",
271+
"metadata": {},
272+
"source": [
273+
"To start, the solver is prepared by setting the initial conditions."
274+
]
275+
},
276+
{
277+
"cell_type": "code",
278+
"execution_count": null,
279+
"metadata": {},
280+
"outputs": [],
281+
"source": [
282+
"stepping.initialize_from_ICs(eq, profiles)"
283+
]
284+
},
247285
{
248286
"cell_type": "markdown",
249287
"metadata": {},
@@ -293,7 +331,7 @@
293331
"metadata": {},
294332
"outputs": [],
295333
"source": [
296-
"voltages = (stepping.vessel_currents_vec*stepping.evol_metal_curr.R)[:stepping.evol_metal_curr.n_active_coils] "
334+
"voltages = (stepping.vessel_currents_vec*stepping.evol_metal_curr.coil_resist)[:stepping.evol_metal_curr.n_active_coils] "
297335
]
298336
},
299337
{
@@ -443,21 +481,9 @@
443481
},
444482
{
445483
"cell_type": "code",
446-
"execution_count": 1,
447-
"metadata": {},
448-
"outputs": [
449-
{
450-
"ename": "NameError",
451-
"evalue": "name 'plt' is not defined",
452-
"output_type": "error",
453-
"traceback": [
454-
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
455-
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
456-
"Cell \u001b[0;32mIn[1], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Plot evolution of tracked values and compare between linear and non-linear evolution\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m fig, axs \u001b[38;5;241m=\u001b[39m \u001b[43mplt\u001b[49m\u001b[38;5;241m.\u001b[39msubplots(\u001b[38;5;241m3\u001b[39m, \u001b[38;5;241m3\u001b[39m, figsize\u001b[38;5;241m=\u001b[39m(\u001b[38;5;241m15\u001b[39m, \u001b[38;5;241m10\u001b[39m), dpi\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m80\u001b[39m, constrained_layout\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[1;32m 3\u001b[0m axs_flat \u001b[38;5;241m=\u001b[39m axs\u001b[38;5;241m.\u001b[39mflat\n\u001b[1;32m 5\u001b[0m axs_flat[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;241m.\u001b[39mplot(history_times, history_o_points[:, \u001b[38;5;241m0\u001b[39m],\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mk+\u001b[39m\u001b[38;5;124m'\u001b[39m, label\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlinear\u001b[39m\u001b[38;5;124m'\u001b[39m)\n",
457-
"\u001b[0;31mNameError\u001b[0m: name 'plt' is not defined"
458-
]
459-
}
460-
],
484+
"execution_count": null,
485+
"metadata": {},
486+
"outputs": [],
461487
"source": [
462488
"# Plot evolution of tracked values and compare between linear and non-linear evolution\n",
463489
"fig, axs = plt.subplots(3, 3, figsize=(15, 10), dpi=80, constrained_layout=True)\n",
@@ -553,7 +579,7 @@
553579
"source": [
554580
"## Limited plasma example\n",
555581
"\n",
556-
"In this example, we examine a limiter configuration plasma and show how to use time-varying plasma profile parameters and plasma resisitivity."
582+
"In this example, we examine a limiter configuration plasma and show how to use time-varying plasma profile parameters and plasma resisitivity. Note there is also the option to specify time-dependent active coil resistances. For example, if you want to switch a coil \"off\" during a simulation you can set its resistance to an arbitrarily high number. See the optional parameter `custom_active_coil_resistances`. "
557583
]
558584
},
559585
{
@@ -648,7 +674,7 @@
648674
"outputs": [],
649675
"source": [
650676
"# determine the (constant) active voltages to be applied at each time step\n",
651-
"voltages = (stepping.vessel_currents_vec*stepping.evol_metal_curr.R)[:stepping.evol_metal_curr.n_active_coils] "
677+
"voltages = (stepping.vessel_currents_vec*stepping.evol_metal_curr.coil_resist)[:stepping.evol_metal_curr.n_active_coils] "
652678
]
653679
},
654680
{
@@ -782,6 +808,15 @@
782808
"metadata": {},
783809
"outputs": [],
784810
"source": [
811+
"# recalculate the active voltages using the new currents\n",
812+
"U_active = (stepping.vessel_currents_vec*stepping.evol_metal_curr.coil_resist)[:stepping.evol_metal_curr.n_active_coils] \n",
813+
"\n",
814+
"# number of time steps to simulate\n",
815+
"max_count = 50\n",
816+
"\n",
817+
"# reset the solver object by resetting the initial conditions\n",
818+
"stepping.initialize_from_ICs(eq, profiles)\n",
819+
"\n",
785820
"# initialising some variables for iteration and logging\n",
786821
"counter = 0\n",
787822
"t = 0\n",

freegsnke/circuit_eq_metal.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ def __init__(
9898
self.coil_resist = eq.tokamak.coil_resist
9999

100100
self.Rm1 = 1.0 / self.coil_resist
101-
self.R = self.coil_resist
101+
# self.R = np.copy(self.coil_resist)
102+
self.active_coil_resistances = np.copy(self.coil_resist[: self.n_active_coils])
102103

103104
# prepare inductance data
104105
if coil_self_ind is not None:
@@ -110,6 +111,8 @@ def __init__(
110111
else:
111112
self.coil_self_ind = eq.tokamak.coil_self_ind
112113

114+
self.build_rm1l()
115+
113116
self.flag_vessel_eig = flag_vessel_eig
114117
self.flag_plasma = flag_plasma
115118

@@ -138,6 +141,12 @@ def __init__(
138141
# Dummy voltage vector
139142
self.empty_U = np.zeros(self.n_coils)
140143

144+
def build_rm1l(
145+
self,
146+
):
147+
# active + passive
148+
self.rm1l_non_symm = np.diag(self.coil_resist**-1.0) @ self.coil_self_ind
149+
141150
def make_selected_mode_mask(self, mode_coupling_masks, verbose):
142151
"""Creates a mask for the vessel normal modes to include in the circuit
143152
equations, based on the maximum frequency of the selected modes.
@@ -243,7 +252,7 @@ def initialize_for_eig(
243252
# diagonalised separately from the active coils. The modes of used for the passive structures
244253
# diagonalise the isolated dynamics of the walls.
245254
# Equation is Lambda**(-1)Iddot + I = F
246-
self.Lambdam1 = self.Pm1 @ (self.normal_modes.rm1l_non_symm @ self.P)
255+
self.Lambdam1 = self.Pm1 @ (self.rm1l_non_symm @ self.P)
247256
# self.RP = np.diag(self.coil_resist) @ self.P
248257
# self.RP_inv = np.linalg.solve(self.RP.T @ self.RP, self.RP.T)
249258
# self.Lambdam1 = (self.RP_inv @ self.coil_self_ind) @ self.P
@@ -260,6 +269,15 @@ def initialize_for_eig(
260269
else:
261270
self.forcing_term = self.forcing_term_eig_no_plasma
262271

272+
def reset_active_coil_resistances(self, active_coil_resistances):
273+
self.coil_resist = np.concatenate(
274+
(active_coil_resistances, self.coil_resist[self.n_active_coils :])
275+
)
276+
self.active_coil_resistances = np.copy(self.coil_resist[: self.n_active_coils])
277+
self.Rm1 = 1 / self.coil_resist
278+
self.build_rm1l()
279+
self.Lambdam1 = self.Pm1 @ (self.rm1l_non_symm @ self.P)
280+
263281
def initialize_for_no_eig(self):
264282
"""Initializes the metal currents object for the case where vessel
265283
eigenmodes are not used."""

freegsnke/linear_solve.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class linear_solver:
3434

3535
def __init__(
3636
self,
37-
eq,
37+
coil_numbers,
3838
Lambdam1,
3939
P,
4040
Pm1,
@@ -93,10 +93,10 @@ def __init__(
9393
self.Pm1Rm1Mey = np.matmul(self.Pm1Rm1, Mey)
9494
self.MyeP = np.matmul(Mey.T, P).T
9595

96-
if Lambdam1 is None:
97-
self.Lambdam1 = Pm1 @ (Rm1 @ (eq.tokamak.coil_self_ind @ P))
98-
else:
99-
self.Lambdam1 = Lambdam1
96+
# if Lambdam1 is None:
97+
# self.Lambdam1 = Pm1 @ (Rm1 @ (eq.tokamak.coil_self_ind @ P))
98+
# else:
99+
self.Lambdam1 = Lambdam1
100100
self.n_independent_vars = np.shape(self.Lambdam1)[0]
101101

102102
self.Mmatrix = np.zeros(
@@ -109,7 +109,7 @@ def __init__(
109109
(self.n_independent_vars + 1, self.n_independent_vars + 1)
110110
)
111111

112-
self.n_active_coils = eq.tokamak.n_active_coils
112+
self.n_active_coils, self.n_coils = coil_numbers
113113

114114
self.solver = implicit_euler_solver(
115115
Mmatrix=np.eye(self.n_independent_vars + 1),

0 commit comments

Comments
 (0)