freegs-plasma / freegs

Free boundary Grad-Shafranov solver

Home Page:http://freegs.readthedocs.io/en/latest/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Reading/writing separatrix in geqdsk files

ZedThree opened this issue · comments

Currently, the geqdsk utility files call the separatrix points rbndry, zbndry -- should these be changed to rbbbs, zbbbs? It doesn't make too much difference, as these are internal names only, but it might be good to consistent with the conventional naming scheme, however bad it might be...

Also, when reading these values in, should they be used to define an isoflux constraint?

Yes, probably best to be consistent. In hindsight I'm not sure rbndry is really much better than rbbbs, since it's not clear which bndry it refers to (plasma, wall?).

When reading in there are several constraints we can use, or a weighting between them. At the moment it's hard-wired to fit the entire 2D psi, this is probably a reasonable default, but not really optimal in general. Some other possible constraints:

  • X-, O-point locations
  • Plasma boundary shape
  • Strike point locations

Would a dictionary of possible constraints be useful? In the call to geqdsk.read there could be a keyword "constrain" (or similar) so that constrain="critical" would constrain X,O-points only, constrain="boundary" the plasma boundary only. It would be nice to support weighting of constraints e.g. constrain=[("boundary", 0.1), ("critical", 0.5)] which would use the weightings to combine different constraints.

We can feed in known coil currents by setting them in the Machine object passed in, and turning off feedback control for those coils. Some coil current feedback is however needed for stability.

I've been trying to read / write geqdsk files and have encountered a problem. It may be related to this thread:

The file:

import freegs
from freegs import geqdsk
from numpy import allclose


def readwrite():
    """Test reading/writing to a file round-trip
    """
    tokamak = freegs.machine.MAST_sym()
    eq = freegs.Equilibrium(
        tokamak=tokamak,
        Rmin=0.1,
        Rmax=2.0,
        Zmin=-1.0,
        Zmax=1.0,
        nx=17,
        ny=17,
        boundary=freegs.boundary.freeBoundaryHagenow,
    )
    profiles = freegs.jtor.ConstrainPaxisIp(1e4, 1e6, 2.0)
    xpoints = [(1.1, -0.6), (1.1, 0.8)]
    isoflux = [(1.1, -0.6, 1.1, 0.6)]
    constrain = freegs.control.constrain(xpoints=xpoints, isoflux=isoflux)
    freegs.solve(eq, profiles, constrain, maxits=25, atol=1e-3, rtol=1e-1)

    file_name = "./example.geqdsk"

    with open(file_name, "w") as f:
        geqdsk.write(eq, f)

    with open(file_name, "r") as f:
        read_eq = geqdsk.read(f, tokamak)

    assert tokamak == read_eq.tokamak
    assert allclose(eq.psi(), read_eq.psi())

if __name__ == "__main__":
    readwrite()

The output:

  nx = 17, ny = 17
102
CURRENT:  10692601.169881243
Plasma current: 10692601.169881243 Amps, input: 1000000.0 Amps
==========================
P2 : Circuit([("P2U", Coil(R=0.49, Z=1.76, current=-9940237.3, turns=1, control=True), 1.0), ("P2L", Coil(R=0.49, Z=-1.76, current=-9940237.3, turns=1, control=True), 1.0)], current=-9940237.261495335, control=True)
P3 : Circuit([("P3U", Coil(R=1.1, Z=1.1, current=-534928.7, turns=1, control=True), 1.0), ("P3L", Coil(R=1.1, Z=-1.1, current=-534928.7, turns=1, control=True), 1.0)], current=-534928.6729608789, control=True)
P4 : Circuit([("P4U", Coil(R=1.51, Z=1.095, current=-250090.2, turns=1, control=True), 1.0), ("P4L", Coil(R=1.51, Z=-1.095, current=-250090.2, turns=1, control=True), 1.0)], current=-250090.22098848983, control=True)
P5 : Circuit([("P5U", Coil(R=1.66, Z=0.52, current=-1286230.6, turns=1, control=True), 1.0), ("P5L", Coil(R=1.66, Z=-0.52, current=-1286230.6, turns=1, control=True), 1.0)], current=-1286230.6032388543, control=True)
P6 : Circuit([("P6U", Coil(R=1.5, Z=0.9, current=-0.6, turns=1, control=True), 1.0), ("P6L", Coil(R=1.5, Z=-0.9, current=0.6, turns=1, control=True), -1.0)], current=-0.5055800302579883, control=True)
P1 : Solenoid(Rs=0.15, Zsmin=-1.45, Zsmax=1.45, current=-5507149.315804305, Ns=100, control=True)
==========================
Traceback (most recent call last):
  File "example_geqdsk.py", line 45, in <module>
    readwrite()
  File "example_geqdsk.py", line 39, in readwrite
    read_eq = geqdsk.read(f, tokamak)
  File "/home/cookj/Documents/code/python/freegs/freegs/geqdsk.py", line 411, in read
    rtol=rtol, blend=0.5)
  File "/home/cookj/Documents/code/python/freegs/freegs/picard.py", line 104, in solve
    constrain(eq)
  File "/home/cookj/Documents/code/python/freegs/freegs/control.py", line 253, in __call__
    end_currents, _ = optimize.leastsq(self.psinorm_difference, start_currents, args=(eq,))
  File "/home/cookj/Documents/code/python/freegs/venv/lib/python3.6/site-packages/scipy/optimize/minpack.py", line 401, in leastsq
    gtol, maxfev, epsfcn, factor, diag)
  File "/home/cookj/Documents/code/python/freegs/freegs/control.py", line 274, in psinorm_difference
    raise ValueError("No X-points found")
ValueError: No X-points found

Any ideas?

Sounds like it's not restoring the constraint properly. I might have done something dumb like not actually write the xpoints and isoflux to file. Will investigate now. Thanks for the MVCE!

Two things I've found out:

https://github.com/bendudson/freegs/blob/master/freegs/geqdsk.py#L100

   data["rmagx"], data["zmagx"], data["simagx"] = opoint[0] # magnetic axis
    
    #Remove Psi magi axis to set it to zero at mag x
    data["sibdry"] = xpoint[0][2]  - data["simagx"] # Psi at boundary

    ...

    data["psi"] = psi - data["simagx"]
    data["simagx"] = 0.0

I'm not sure why this is necessary -- surely the point of sibdry and simagx is to store the values of psi at the boundary/axis respectively?

Secondly, eq.Jtor is nothing like the Jtor calculated here: https://github.com/bendudson/freegs/blob/master/freegs/geqdsk.py#L302

I'm afraid I've quickly reached the limits of my ignorance here. Maybe @bendudson can weigh in? I think it's going wrong in calculating the currents, as it already has the correct psi (within the precision of roundtrip floats via ASCII, and minus a shift in the absolute value due to fixing the value on the magnetic axis to be 0.0).