@@ -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