Adding 3D perturbations to a 2D equilibrium (J-TEXT)

import fusionsc as fsc
from fusionsc.devices import jtext
import numpy as np

import matplotlib.pyplot as plt

We use J-TEXT as an example for a 3D perturbation applied to a 2D equilibrium. We use an example equilibrium for the J-TEXT Tokamak (which is just the contents of an EFit geqdsk file). On top of that equilibrium, we apply a perturbation field generated by the island coils. The field will then be calculated as field + coilCurrent * perturbation.

efitExample = jtext.exampleGeqdsk()
lines = efitExample.split('\n')

for i in range(20):
    print(lines[i])
print()
print('... {} lines follow ...'.format(len(lines) - 20))
  EFID    16/03/13     #     75  400ms             0  65  65
 7.000000000e-01 7.000000000e-01 1.050000000e+00 7.000000000e-01 0.000000000e+00
 1.063671875e+00 0.000000000e+00 3.229880485e-02 1.512832695e-02 1.400000000e+00
 7.500000000e+04 3.229880485e-02 0.000000000e+00 1.063671875e+00 0.000000000e+00
 0.000000000e+00 0.000000000e+00 1.512832695e-02 0.000000000e+00 0.000000000e+00
 1.480607441e+00 1.480103394e+00 1.479615448e+00 1.479143353e+00 1.478686857e+00
 1.478245708e+00 1.477819652e+00 1.477408437e+00 1.477011806e+00 1.476629505e+00
 1.476261278e+00 1.475906867e+00 1.475566015e+00 1.475238465e+00 1.474923958e+00
 1.474622235e+00 1.474333035e+00 1.474056099e+00 1.473791166e+00 1.473537974e+00
 1.473296263e+00 1.473065769e+00 1.472846230e+00 1.472637384e+00 1.472438966e+00
 1.472250714e+00 1.472072363e+00 1.471903648e+00 1.471744305e+00 1.471594069e+00
 1.471452675e+00 1.471319857e+00 1.471195349e+00 1.471078885e+00 1.470970199e+00
 1.470869025e+00 1.470775095e+00 1.470688143e+00 1.470607902e+00 1.470534105e+00
 1.470466484e+00 1.470404772e+00 1.470348702e+00 1.470298005e+00 1.470252415e+00
 1.470211663e+00 1.470175480e+00 1.470143601e+00 1.470115755e+00 1.470091676e+00
 1.470071094e+00 1.470053742e+00 1.470039352e+00 1.470027655e+00 1.470018383e+00
 1.470011267e+00 1.470006040e+00 1.470002433e+00 1.470000177e+00 1.469999005e+00
 1.469998648e+00 1.469998837e+00 1.469999304e+00 1.469999781e+00 1.470000000e+00
 7.804532977e+02 7.596308094e+02 7.389767452e+02 7.184962088e+02 6.981943040e+02
 6.780761346e+02 6.581468042e+02 6.384114168e+02 6.188750759e+02 5.995428854e+02

... 967 lines follow ...
field = fsc.magnetics.MagneticConfig.fromEFit(efitExample)
geometry = jtext.hfsLimiter() + jtext.target()

perturbation = jtext.islandCoils([1] * 6)

We pre-calculate the perturbation and the background field separately, so that field + current * perturbation can be efficiently computed.

grid = jtext.defaultGrid()
geoGrid = jtext.defaultGeometryGrid()

field = field.compute(grid)
perturbation = perturbation.compute(grid)

geometry = geometry.index(geoGrid)

Now we run Poincaré plots for a few different cases

startPoints = np.linspace([0.8, 0, 0], [1.2, 0, 0], 20, axis = 1)

for current in [0, 2000, 4000]:
    perturbedField = field + current * perturbation
    print("Perturbed field:\n", perturbedField.toYaml())
    
    x, y, z, lFwd, lBwd = fsc.flt.poincareInPhiPlanes(
        startPoints,
        perturbedField,
        [0],
        500,
        distanceLimit = 1e5,
        grid = grid # A "+" expression is not a computed field, so we need to specify a grid to evaluate on
    )
    
    plt.figure()
    plt.title("I = {}A".format(current))
    plt.scatter(x, z, marker = '.', s = 1)
    plt.show()
Perturbed field:
 sum:
  - computedField:
      grid:
        rMin: 0.69999999999999996
        rMax: 1.3999999999999999
        zMin: -0.34999999999999998
        zMax: 0.34999999999999998
        nR: 70
        nZ: 70
        nPhi: 128
      data: <capability>
  - scaleBy:
      field:
        computedField:
          grid:
            rMin: 0.69999999999999996
            rMax: 1.3999999999999999
            zMin: -0.34999999999999998
            zMax: 0.34999999999999998
            nR: 70
            nZ: 70
            nPhi: 128
          data: <capability>
../_images/fa4900735b663dc37e097109815b3a77ad2521d69e552c107ecf4c3df549a9e3.png
Perturbed field:
 sum:
  - computedField:
      grid:
        rMin: 0.69999999999999996
        rMax: 1.3999999999999999
        zMin: -0.34999999999999998
        zMax: 0.34999999999999998
        nR: 70
        nZ: 70
        nPhi: 128
      data: <capability>
  - scaleBy:
      field:
        computedField:
          grid:
            rMin: 0.69999999999999996
            rMax: 1.3999999999999999
            zMin: -0.34999999999999998
            zMax: 0.34999999999999998
            nR: 70
            nZ: 70
            nPhi: 128
          data: <capability>
      factor: 2000
../_images/f5e706ef9c8e3697db8cc061ea655af09506cee017659833f1899f55b1bcd476.png
Perturbed field:
 sum:
  - computedField:
      grid:
        rMin: 0.69999999999999996
        rMax: 1.3999999999999999
        zMin: -0.34999999999999998
        zMax: 0.34999999999999998
        nR: 70
        nZ: 70
        nPhi: 128
      data: <capability>
  - scaleBy:
      field:
        computedField:
          grid:
            rMin: 0.69999999999999996
            rMax: 1.3999999999999999
            zMin: -0.34999999999999998
            zMax: 0.34999999999999998
            nR: 70
            nZ: 70
            nPhi: 128
          data: <capability>
      factor: 4000
../_images/516d80f42e6f5607688a8cbc7eaafeb6f47e2d397ca4bde1ded0cbec0258ec07.png