Skip to content

Commit 79f680d

Browse files
committed
Add normalised psi constraint
1 parent 6720a55 commit 79f680d

2 files changed

Lines changed: 31 additions & 0 deletions

File tree

freegsnke/GSstaticsolver.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,8 @@ def inverse_solve(
997997
else:
998998
this_l2_reg = 1e-4 * np.array(l2_reg)
999999

1000+
constrain.eq = eq
1001+
constrain.profiles = profiles
10001002
if (
10011003
use_full_Jacobian
10021004
* (rel_change_full < full_jacobian_handover[0])

freegsnke/inverse.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(
3636
isoflux_set=None,
3737
null_points=None,
3838
psi_vals=None,
39+
psin_vals=None,
3940
curr_vals=None,
4041
):
4142
"""Instantiates the object and sets all magnetic constraints to be used.
@@ -53,6 +54,8 @@ def __init__(
5354
structure [Rcoords, Zcoords, psi_values]
5455
with Rcoords, Zcoords and psi_values having the same shape
5556
Sets the desired values of psi for a set of coordinates, possibly an entire map
57+
psin_vals : list or np.array, optional
58+
structure [Rcoord, Zcoord, normalised_psi_values]
5659
curr_vals : list, optional
5760
structure [[coil indexes in the array of coils available for control], [coil current values]]
5861
"""
@@ -82,6 +85,8 @@ def __init__(
8285
self.psi_vals[2] -= np.mean(self.psi_vals[2])
8386
self.norm_psi_vals = np.linalg.norm(self.psi_vals[2])
8487

88+
self.psin_vals = None if psin_vals is None else np.array(psin_vals)
89+
8590
self.curr_vals = curr_vals
8691
self.curr_loss = 0
8792
if self.curr_vals is not None:
@@ -213,6 +218,11 @@ def build_greens(self, eq):
213218
R=self.psi_vals[0], Z=self.psi_vals[1]
214219
)
215220

221+
if self.psin_vals is not None:
222+
self.G_for_psin = eq.tokamak.createPsiGreensVec(
223+
R=self.psin_vals[:, 0], Z=self.psin_vals[:, 1]
224+
)
225+
216226
def build_plasma_vals(self, trial_plasma_psi):
217227
"""Builds and stores all the values relative to the plasma,
218228
based on the provided plasma_psi
@@ -321,6 +331,20 @@ def build_psi_vals_lsq(self, full_currents_vec):
321331

322332
return A, b, [normalised_loss]
323333

334+
def build_psin_vals_lsq(self, full_currents_vec):
335+
# G is the gradient of psi at control points wrt the coil current
336+
A = self.G_for_psin[self.control_mask].T
337+
# apply the chain rule so that the gradnient is normalised psi wrt coil currents
338+
self.eq._updatePlasmaPsi(self.eq.plasma_psi)
339+
self.eq.psi_bndry = self.profiles.psi_bndry
340+
A *= 1.0 / (self.profiles.psi_bndry - self.eq.psi_axis)
341+
342+
b = self.psin_vals[:, 2] - self.eq.psiNRZ(
343+
self.psin_vals[:, 0], self.psin_vals[:, 1]
344+
)
345+
346+
return A, b, np.linalg.norm(b) / np.linalg.norm(self.psin_vals[:, 2])
347+
324348
def build_curr_vals_lsq(self, full_currents_vec):
325349
"""Builds for the ordinary least sq problem associated to the psi values
326350
@@ -368,6 +392,11 @@ def build_lsq(self, full_currents_vec):
368392
b = np.concatenate((b, b_pv), axis=0)
369393
self.psiv_dim = len(b)
370394
loss = loss + l
395+
if self.psin_vals is not None:
396+
A_pnv, b_pnv, l = self.build_psin_vals_lsq(full_currents_vec)
397+
A = np.concatenate((A, A_pnv), axis=0)
398+
b = np.concatenate((b, b_pnv), axis=0)
399+
loss = loss + l
371400
if self.curr_vals is not None:
372401
A_cv, b_cv, l = self.build_curr_vals_lsq(full_currents_vec)
373402
A = np.concatenate((A, A_cv), axis=0)

0 commit comments

Comments
 (0)