"""
Classes and routines to represent coils and circuits
License
-------
Copyright 2016-2025 FreeGS contributors.
This file is part of FreeGS.
FreeGS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FreeGS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with FreeGS. If not, see <http://www.gnu.org/licenses/>.
"""
import numpy as np
from scipy.interpolate import interp1d
from shapely.geometry.polygon import Polygon
from .coil import AreaCurrentLimit, Coil
from .filament_coil import FilamentCoil
from .gradshafranov import Greens, GreensBr, GreensBz
from .shaped_coil import ShapedCoil
# We need this for the `label` part of the Circuit dtype for writing
# to HDF5 files. See the following for information:
# http://docs.h5py.org/en/latest/strings.html#how-to-store-text-strings
try:
import h5py
has_hdf5 = True
except ImportError:
has_hdf5 = False
[docs]
class Circuit:
"""
Represents a collection of coils connected to the same circuit
Public members
--------------
current Current in the circuit [Amps]
control Use feedback control? [bool]
"""
# We need a variable-length unicode string in the dtype. The numpy
# 'S' dtype is not suitable, so we only fallback to it if we don't
# have HDF5 (in which case, it doesn't matter, but we need
# something)
if has_hdf5:
# Python 2/3 compatibility
try:
string_dtype = h5py.special_dtype(vlen=unicode)
except NameError:
string_dtype = h5py.special_dtype(vlen=str)
else:
string_dtype = np.dtype("S")
# A dtype for converting to Numpy array and storing in HDF5 files
dtype = np.dtype(
[
("label", string_dtype),
("coil", Coil.dtype),
("multiplier", np.float64),
]
)
[docs]
def __init__(self, coils, current=0.0, control=True):
"""
coils - A list [ (label, Coil, multiplier) ]
"""
self.coils = coils
self.current = current
self.control = control
def __getitem__(self, name):
"""Returns a coil in the circuit using circuit[coil_name]"""
for label, coil, _ in self.coils:
if label == name:
return coil
raise KeyError(f"Circuit does not contain coil with label '{name}'")
@property
def current(self) -> float:
return self._current
@current.setter
def current(self, value: float):
"""
When updating the circuit current, also update the current in the coils in the circuit (with multipliers)
"""
self._current = value
for _, coil, multiplier in self.coils:
coil.current = multiplier * value
@property
def control(self) -> bool:
return self._control
@control.setter
def control(self, value: bool):
"""
When updating the circuit control switch, also update the control switch in the coils in the circuit
"""
self._control = value
for _, coil, _ in self.coils:
coil.control = value
[docs]
def psi(self, R, Z):
"""
Calculate poloidal flux at (R,Z)
"""
psival = 0.0
for _, coil, multiplier in self.coils:
coil.current = self.current * multiplier
psival += coil.psi(R, Z)
return psival
[docs]
def createPsiGreens(self, R, Z):
"""
Calculate Greens functions
"""
pgreen = {}
for label, coil, _ in self.coils:
pgreen[label] = coil.createPsiGreens(R, Z)
return pgreen
[docs]
def calcPsiFromGreens(self, pgreen):
"""
Calculate psi from Greens functions
"""
psival = 0.0
for label, coil, multiplier in self.coils:
coil.current = self.current * multiplier
psival += coil.calcPsiFromGreens(pgreen[label])
return psival
[docs]
def Br(self, R, Z):
"""
Calculate radial magnetic field Br at (R,Z)
"""
result = 0.0
for _, coil, multiplier in self.coils:
coil.current = self.current * multiplier
result += coil.Br(R, Z)
return result
[docs]
def Bz(self, R, Z):
"""
Calculate vertical magnetic field Bz at (R,Z)
"""
result = 0.0
for _, coil, multiplier in self.coils:
coil.current = self.current * multiplier
result += coil.Bz(R, Z)
return result
[docs]
def controlPsi(self, R, Z):
"""
Calculate poloidal flux at (R,Z) due to a unit current
"""
result = 0.0
for _, coil, multiplier in self.coils:
result += multiplier * coil.controlPsi(R, Z)
return result
[docs]
def controlBr(self, R, Z):
"""
Calculate radial magnetic field Br at (R,Z) due to a unit current
"""
result = 0.0
for _, coil, multiplier in self.coils:
result += multiplier * coil.controlBr(R, Z)
return result
[docs]
def controlBz(self, R, Z):
"""
Calculate vertical magnetic field Bz at (R,Z) due to a unit current
"""
result = 0.0
for _, coil, multiplier in self.coils:
result += multiplier * coil.controlBz(R, Z)
return result
[docs]
def getForces(self, equilibrium):
"""
Calculate forces on the coils
Returns a dictionary of coil label -> force
"""
forces = {}
for label, coil, _ in self.coils:
forces[label] = coil.getForces(equilibrium)
return forces
[docs]
def inShape(self, polygon):
return sum(
coil.inShape(polygon) * multiplier for label, coil, multiplier in self.coils
)
def __repr__(self):
coils = [
f'("{label}", {coil}, {multiplier})'
for label, coil, multiplier in self.coils
]
result = ", ".join(coils)
return f"Circuit([{result}], current={self.current}, control={self.control})"
def __eq__(self, other):
return (
self.coils == other.coils
and self.current == other.current
and self.control == other.control
)
def __ne__(self, other):
return not self == other
[docs]
def to_numpy_array(self):
"""
Helper method for writing output
"""
return np.array(
[
(label, coil.to_numpy_array(), multiplier)
for label, coil, multiplier in self.coils
],
dtype=self.dtype,
)
[docs]
@classmethod
def from_numpy_array(cls, value):
if value.dtype != cls.dtype:
raise ValueError(
f"Can't create {type(cls)} from dtype: {value.dtype} (expected: {cls.dtype})"
)
# HDF5 reads strings as bytes by default, so convert to string
def toString(s):
try:
return str(s, "utf-8") # Convert bytes to string, using encoding
except TypeError:
return s # Probably already a string
# Use the current/control values from the first coil in the circuit
# Should be consistent!
return Circuit(
[
(toString(label), Coil(*coil), multiplier)
for label, coil, multiplier in value
],
current=value[0][1]["current"] / value[0]["multiplier"],
control=value[0][1]["control"],
)
[docs]
def plot(self, axis=None, show=False):
"""
Plot the coils in the circuit
Returns the axis used
"""
for _, coil, _ in self.coils:
axis = coil.plot(axis=axis, show=False)
if show:
import matplotlib.pyplot as plt
plt.show()
return axis
[docs]
def MirroredCoil(
R, Z, current=0.0, turns=1, control=True, area=AreaCurrentLimit(), symmetric=True
):
"""
Create a pair of coils, at +/- Z
If symmetric = True then current is in the same direction (in series);
if symmetric = False then current is in the opposite direction
"""
return Circuit(
[
(
"U",
Coil(R, Z, current=current, turns=turns, control=control, area=area),
1.0,
),
(
"L",
Coil(R, Z, current=current, turns=turns, control=control, area=area),
1.0 if symmetric else -1.0,
),
]
)
[docs]
class Solenoid:
"""
Represents a central solenoid
Public members
--------------
current - current in each turn
control - enable or disable control system
"""
# A dtype for converting to Numpy array and storing in HDF5 files
dtype = np.dtype(
[
("Rs", np.float64),
("Zsmin", np.float64),
("Zsmax", np.float64),
("Ns", np.float64),
("current", np.float64),
("control", bool),
]
)
[docs]
def __init__(self, Rs, Zsmin, Zsmax, Ns, current=0.0, control=True):
"""
Rs - Radius of the solenoid
Zsmin, Zsmax - Minimum and maximum Z
Ns - Number of turns
current - current in each turn
control - enable or disable control system
"""
self.Rs = Rs
self.Zsmin = Zsmin
self.Zsmax = Zsmax
self.Ns = int(Ns)
self.current = current
self.control = control
[docs]
def psi(self, R, Z):
"""
Calculate poloidal flux at (R,Z)
"""
return self.controlPsi(R, Z) * self.current
[docs]
def createPsiGreens(self, R, Z):
"""
Calculate Greens functions
"""
return self.controlPsi(R, Z)
[docs]
def calcPsiFromGreens(self, pgreen):
"""
Calculate psi from Greens functions
"""
return self.current * pgreen
[docs]
def Br(self, R, Z):
"""
Calculate radial magnetic field Br at (R,Z)
"""
return self.controlBr(R, Z) * self.current
[docs]
def Bz(self, R, Z):
"""
Calculate vertical magnetic field Bz at (R,Z)
"""
return self.controlBz(R, Z) * self.current
[docs]
def controlPsi(self, R, Z):
"""
Calculate poloidal flux at (R,Z) due to a unit current
R and Z should have the same dimensions, but can be multi-dimensional
Return should have the same shape
"""
result = 0.0
for Zs in np.linspace(self.Zsmin, self.Zsmax, self.Ns):
result += Greens(self.Rs, Zs, R, Z)
return result
[docs]
def controlBr(self, R, Z):
"""
Calculate radial magnetic field Br at (R,Z) due to a unit current
"""
result = 0.0
for Zs in np.linspace(self.Zsmin, self.Zsmax, self.Ns):
result += GreensBr(self.Rs, Zs, R, Z)
return result
[docs]
def controlBz(self, R, Z):
"""
Calculate vertical magnetic field Bz at (R,Z) due to a unit current
"""
result = 0.0
for Zs in np.linspace(self.Zsmin, self.Zsmax, self.Ns):
result += GreensBz(self.Rs, Zs, R, Z)
return result
[docs]
def getForces(self, equilibrium):
"""
Calculate forces on the solenoid.
Not currently implemented
"""
return {}
def __repr__(self):
return (
f"Solenoid(Rs={self.Rs}, Zsmin={self.Zsmin}, Zsmax={self.Zsmax}, "
f"current={self.current}, Ns={self.Ns}, control={self.control})"
)
def __eq__(self, other):
return (
self.Rs == other.Rs
and self.Zsmin == other.Zsmin
and self.Zsmax == other.Zsmax
and self.Ns == other.Ns
and self.current == other.current
and self.control == other.control
)
def __ne__(self, other):
return not self == other
[docs]
def to_numpy_array(self):
"""
Helper method for writing output
"""
return np.array(
(self.Rs, self.Zsmin, self.Zsmax, self.Ns, self.current, self.control),
dtype=self.dtype,
)
[docs]
@classmethod
def from_numpy_array(cls, value):
if value.dtype != cls.dtype:
raise ValueError(
f"Can't create {type(cls)} from dtype: {value.dtype} (expected: {cls.dtype})"
)
return Solenoid(*value[()])
[docs]
def plot(self, axis=None, show=False):
return axis
[docs]
class Wall:
"""
Represents the wall of the device.
Consists of an ordered list of (R,Z) points
"""
[docs]
def __init__(self, R, Z):
assert len(R) == len(Z)
self.R = R
self.Z = Z
def __repr__(self):
return f"Wall(R={self.R}, Z={self.Z})"
def __eq__(self, other):
return np.allclose(self.R, other.R) and np.allclose(self.Z, other.Z)
def __ne__(self, other):
return not self == other
[docs]
class Sensor:
"""
Parent class for the sensors
Contains general attributes that apply to all sensors
"""
[docs]
def __init__(self, R, Z, name=None, weight=1, status=True, measurement=None):
self.R = R
self.Z = Z
self.status = status
self.name = name
self.weight = weight
if self.status:
self.measurement = measurement
def __repr__(self):
return f"R={self.R}, Z={self.Z}"
def __eq__(self, other):
return np.allclose(self.R, other.R) and np.allclose(self.Z, other.Z)
def __ne__(self, other):
return not self == other
[docs]
class RogowskiSensor(Sensor):
"""
Represents a Rogowski sensor.
Consists of an ordered list of (R,Z) points (defines polygon on which sensor lies)
Returns current inside the loop
"""
[docs]
def __init__(self, R, Z, name=None, weight=1, status=True, measurement=None):
Sensor.__init__(
self, R, Z, name=name, weight=weight, status=status, measurement=measurement
)
polygonlist = [(r, z) for r, z in zip(self.R, self.Z)]
self.polygon = Polygon(polygonlist)
[docs]
def get_measure(self, tokamak, eq):
"""
Method to update the current attribute of the sensor
with whatever current is contained within the sensor
if sensor is on (status == True)
"""
if self.status:
# coil current
coil_current = 0
for _, coil in tokamak.coils:
coil_current += coil.inShape(self.polygon) * coil.current
# plasma current
plasma_current = 0
if eq is not None:
for index, (R, Z) in enumerate(
zip(eq.R.flatten(order="F"), eq.Z.flatten(order="F"))
):
gridpoint = Polygon(
[
(R - eq.dR / 2, Z + eq.dZ / 2),
(R + eq.dR / 2, Z + eq.dZ / 2),
(R + eq.dR / 2, Z - eq.dZ / 2),
(R - eq.dR / 2, Z - eq.dZ / 2),
]
)
plasma_current += (
eq.Jtor.flatten(order="F")[index]
* self.polygon.intersection(gridpoint).area
)
self.measurement = plasma_current + coil_current
[docs]
def plot(self, axis):
axis.plot([*list(self.R), self.R[0]], [*list(self.Z), self.Z[0]], "b")
[docs]
class PoloidalFieldSensor(Sensor):
"""
Represents position of a Poloidal B field sensor.
Consists a single (R,Z) point, and an angle Theta
At which the field is measured at
"""
[docs]
def __init__(self, R, Z, theta, name=None, weight=1, status=True, measurement=None):
Sensor.__init__(
self, R, Z, name=name, weight=weight, status=status, measurement=measurement
)
self.theta = theta
def __repr__(self):
return f"R={self.R}, Z={self.Z}, Theta={self.theta}"
[docs]
def get_measure(self, tokamak, eq):
"""
Updates field attribute of sensor with measured field at that
point/direction if sensor is on
"""
if self.status:
field = (tokamak.Br(self.R, self.Z)) * np.cos(self.theta) + (
tokamak.Bz(self.R, self.Z)
) * np.sin(self.theta)
if eq is not None:
field += (eq.plasmaBr(self.R, self.Z)) * np.cos(self.theta) + (
eq.plasmaBz(self.R, self.Z)
) * np.sin(self.theta)
self.measurement = field
[docs]
def plot(self, axis):
axis.plot(self.R, self.Z, "mo")
axis.arrow(self.R, self.Z, 0.1 * np.cos(self.theta), 0.1 * np.sin(self.theta))
[docs]
class FluxLoopSensor(Sensor):
"""
Represents position of a Flux Loop Sensor.
Consists a single (R,Z) point, describing the position of the probe
Contains a method to find the value of flux (psi) at the position
"""
[docs]
def __init__(self, R, Z, name=None, weight=1, status=True, measurement=None):
Sensor.__init__(
self, R, Z, name=name, weight=weight, status=status, measurement=measurement
)
[docs]
def get_measure(self, tokamak, eq):
"""
Updates flux attribute with
poloidal flux at the point of measurement
if sensor is on
"""
if self.status:
if eq is not None:
psi = eq.psiRZ(self.R, self.Z)
else:
psi = tokamak.psi(self.R, self.Z)
self.measurement = psi
[docs]
def plot(self, axis):
axis.plot(self.R, self.Z, "ro")
[docs]
class Machine:
"""
Represents the machine (Tokamak), including
coils and power supply circuits
coils[(label, Coil|Circuit|Solenoid] - List of coils
Note: a list is used rather than a dict, so that the coils
remain ordered, and so can be updated easily by the control system.
Instead __getitem__ is implemented to allow access to coils
"""
[docs]
def __init__(self, coils, wall=None, sensors=None, nlimit=500, R0=1.0):
"""
coils - A list of coils [(label, Coil|Circuit|Solenoid)]
sensors - A list of sensors
R0 - The major radius to be written to GEQDSK [meters].
By convention EFIT has a value of R0 for each tokamak.
"""
self.coils = coils
self.wall = wall
self.sensors = sensors
self.R0 = R0
self.limit_points_R = None
self.limit_points_Z = None
if self.wall is not None:
self.limit_points_R, self.limit_points_Z = self.generate_limit_points(
nlimit
)
def __repr__(self):
return f"Machine(coils={self.coils}, wall={self.wall})"
def __eq__(self, other):
# Other Machine might be equivalent except for order of
# coils. Assume this doesn't actually matter
return sorted(self.coils) == sorted(other.coils) and self.wall == other.wall
def __ne__(self, other):
return not self == other
def __getitem__(self, name):
for label, coil in self.coils:
if label == name:
return coil
raise KeyError(f"Machine does not contain coil with label '{name}'")
[docs]
def generate_limit_points(self, nlimit):
"""
Generate points along the machine wall that may be used to check
if the plasma is limited or not.
"""
# Interpolate wall limit points.
# Make an interpolator for point location as function of normalised distance
# along the wall
points = np.array([self.wall.R, self.wall.Z]).T
distance = np.cumsum(np.sqrt(np.sum(np.diff(points, axis=0) ** 2, axis=1)))
distance = np.insert(distance, 0, 0) / distance[-1]
interpolator = interp1d(distance, points, kind="linear", axis=0)
new_distances = np.linspace(0, 1, nlimit, endpoint=True)
interpolated_points = interpolator(new_distances)
R = np.asarray(interpolated_points[:, 0])
Z = np.asarray(interpolated_points[:, 1])
return R, Z
[docs]
def psi(self, R, Z):
"""
Poloidal flux due to coils
"""
psi_coils = 0.0
for _, coil in self.coils:
psi_coils += coil.psi(R, Z)
return psi_coils
[docs]
def createPsiGreens(self, R, Z):
"""
An optimisation, which pre-computes the Greens functions
and puts into arrays for each coil. This map can then be
called at a later time, and quickly return the field
"""
pgreen = {}
for label, coil in self.coils:
pgreen[label] = coil.createPsiGreens(R, Z)
return pgreen
[docs]
def calcPsiFromGreens(self, pgreen):
"""
Uses the object returned by createPsiGreens to quickly
compute the plasma psi
"""
psi_coils = 0.0
for label, coil in self.coils:
psi_coils += coil.calcPsiFromGreens(pgreen[label])
return psi_coils
[docs]
def Br(self, R, Z):
"""
Radial magnetic field at given points
"""
Br = 0.0
for _, coil in self.coils:
Br += coil.Br(R, Z)
return Br
[docs]
def Bz(self, R, Z):
"""
Vertical magnetic field
"""
Bz = 0.0
for _, coil in self.coils:
Bz += coil.Bz(R, Z)
return Bz
[docs]
def controlBr(self, R, Z):
"""
Returns a list of control responses for Br
at the given (R,Z) location(s).
"""
return [coil.controlBr(R, Z) for label, coil in self.coils if coil.control]
[docs]
def controlBz(self, R, Z):
"""
Returns a list of control responses for Bz
at the given (R,Z) location(s)
"""
return [coil.controlBz(R, Z) for label, coil in self.coils if coil.control]
[docs]
def controlPsi(self, R, Z):
"""
Returns a list of control responses for psi
at the given (R,Z) location(s)
"""
return [coil.controlPsi(R, Z) for label, coil in self.coils if coil.control]
[docs]
def controlAdjust(self, current_change):
"""
Add given currents to the controls.
Given iterable must be the same length
as the list returned by controlBr, controlBz
"""
# Get list of coils being controlled
controlcoils = [coil for label, coil in self.coils if coil.control]
for coil, dI in zip(controlcoils, current_change):
# Ensure that dI is a scalar
coil.current += dI.item()
[docs]
def controlCurrents(self):
"""
Return a list of coil currents for the coils being controlled
"""
return [coil.current for label, coil in self.coils if coil.control]
[docs]
def setControlCurrents(self, currents):
"""
Sets the currents in the coils being controlled.
Input list must be of the same length as the list
returned by controlCurrents
"""
controlcoils = [coil for label, coil in self.coils if coil.control]
for coil, current in zip(controlcoils, currents):
coil.current = current
[docs]
def printCurrents(self):
print("==========================")
for label, coil in self.coils:
print(label + " : " + str(coil))
print("==========================")
[docs]
def takeMeasurements(self, eq=None):
"""
Method calling the measure method of each sensor on the machine
"""
for sensor in self.sensors:
sensor.get_measure(self, eq)
[docs]
def printMeasurements(self, eq=None):
"""
Method for calling the takeMeasurements method, then printing the results
"""
print("==========================")
self.takeMeasurements(eq=eq)
for sensor in self.sensors:
if sensor.name is not None:
print(
sensor.name
+ " "
+ str(sensor)
+ ", Measurement="
+ str(sensor.measurement)
)
else:
print(
str(type(sensor))
+ str(sensor)
+ " Measurement="
+ str(sensor.measurement)
)
print("==========================")
[docs]
def getForces(self, equilibrium=None):
"""
Calculate forces on the coils, given the plasma equilibrium.
If no plasma equilibrium given then the forces due to
the coils alone will be calculated.
Returns a dictionary of coil label -> force
"""
if equilibrium is None:
equilibrium = self
forces = {}
for label, coil in self.coils:
forces[label] = coil.getForces(equilibrium)
return forces
[docs]
def getCurrents(self):
"""
Returns a dictionary of coil label -> current in Amps
"""
currents = {}
for label, coil in self.coils:
currents[label] = coil.current
return currents
[docs]
def plot(self, axis=None, show=True):
"""
Plot the machine coils
"""
for _, coil in self.coils:
axis = coil.plot(axis=axis, show=False)
if show:
import matplotlib.pyplot as plt
plt.show()
return axis
[docs]
def EmptyTokamak():
"""
Creates a tokamak with no coils
"""
return Machine([])
[docs]
def TestTokamak():
"""
Create a simple tokamak
"""
coils = [
(
"P1L",
ShapedCoil([(0.95, -1.15), (0.95, -1.05), (1.05, -1.05), (1.05, -1.15)]),
),
("P1U", ShapedCoil([(0.95, 1.15), (0.95, 1.05), (1.05, 1.05), (1.05, 1.15)])),
("P2L", Coil(1.75, -0.6)),
("P2U", Coil(1.75, 0.6)),
]
wall = Wall(
[0.75, 0.75, 1.5, 1.8, 1.8, 1.5],
[-0.85, 0.85, 0.85, 0.25, -0.25, -0.85], # R
) # Z
return Machine(coils, wall)
[docs]
def TestTokamakLimited():
"""
Create a simple tokamak
"""
coils = [
(
"P1L",
ShapedCoil([(0.95, -1.15), (0.95, -1.05), (1.05, -1.05), (1.05, -1.15)]),
),
("P1U", ShapedCoil([(0.95, 1.15), (0.95, 1.05), (1.05, 1.05), (1.05, 1.15)])),
("P2L", Coil(1.75, -0.6)),
("P2U", Coil(1.75, 0.6)),
]
wall = Wall(
[0.93, 0.93, 1.5, 1.8, 1.8, 1.5],
[-0.85, 0.85, 0.85, 0.25, -0.25, -0.85], # R
) # Z
return Machine(coils, wall)
[docs]
def TestTokamakSensor():
"""
Creating a simple tokamak with sensors along the boundary
"""
coils = [
(
"P1L",
ShapedCoil([(0.95, -1.15), (0.95, -1.05), (1.05, -1.05), (1.05, -1.15)]),
),
("P1U", ShapedCoil([(0.95, 1.15), (0.95, 1.05), (1.05, 1.05), (1.05, 1.15)])),
("P2L", Coil(1.75, -0.6)),
("P2U", Coil(1.75, 0.6)),
]
wall = Wall(
[0.75, 0.75, 1.5, 1.8, 1.8, 1.5], [-0.85, 0.85, 0.85, 0.25, -0.25, -0.85]
)
sensors = [
RogowskiSensor(
[0.77, 0.77, 1.48, 1.78, 1.78, 1.48],
[-0.83, 0.83, 0.83, 0.23, -0.23, -0.83],
name="Rog1",
),
PoloidalFieldSensor(1.8, 0.14, 2.2, name="BP1"),
PoloidalFieldSensor(1.8, -0.14, -2.2, name="BP2"),
PoloidalFieldSensor(1.7, 0.475, 2.2, name="BP3"),
PoloidalFieldSensor(1.7, -0.475, -2.2, name="BP4"),
PoloidalFieldSensor(1.5, 0.85, 2.2, name="BP5"),
PoloidalFieldSensor(1.5, -0.85, -2.2, name="BP6"),
FluxLoopSensor(1.8, 0.2, name="FL1"),
FluxLoopSensor(1.8, -0.2, name="FL2"),
FluxLoopSensor(1.65, 0.52, name="FL3"),
FluxLoopSensor(1.65, -0.52, name="FL4"),
FluxLoopSensor(1.1, 0.85, name="FL5"),
FluxLoopSensor(1.1, -0.85, name="FL6"),
]
return Machine(coils, wall, sensors)
[docs]
def DIIID():
"""
PF coil set and boundary from g file and GA webpage
"""
coils = [
(
"FC1",
ShapedCoil(
[
[1.6042, -1.64455],
[1.7736, -1.64455],
[1.7736, -1.51145],
[1.6042, -1.51145],
]
),
),
(
"FC2",
ShapedCoil(
[
[0.8354, 0.00777],
[0.8862, 0.00777],
[0.8862, 0.32883],
[0.8354, 0.32883],
]
),
),
(
"FC3",
ShapedCoil(
[
[0.836, 0.34757],
[0.8868, 0.34757],
[0.8868, 0.66863],
[0.836, 0.66863],
]
),
),
(
"FC4",
ShapedCoil(
[
[0.8374, 0.68857],
[0.8882, 0.68857],
[0.8882, 1.00963],
[0.8374, 1.00963],
]
),
),
(
"FC5",
ShapedCoil(
[
[0.8357, 1.02937],
[0.8865, 1.02937],
[0.8865, 1.35043],
[0.8357, 1.35043],
]
),
),
(
"FC6",
ShapedCoil(
[[0.9345, 1.3876], [1.0737, 1.5268], [1.0737, 1.6462], [0.9345, 1.507]]
),
),
(
"FC7",
ShapedCoil(
[[2.5298, 0.3403], [2.703, 0.3403], [2.6949, 0.5349], [2.5217, 0.5349]]
),
),
(
"FC8",
ShapedCoil(
[[2.5387, 1.0325], [2.7267, 1.0325], [2.2078, 1.2017], [2.0198, 1.2017]]
),
),
(
"FC9",
ShapedCoil(
[
[1.13435, 1.55935],
[1.36925, 1.55935],
[1.36925, 1.64445],
[1.13435, 1.64445],
]
),
),
(
"FC10",
ShapedCoil(
[
[1.6043, 1.52085],
[1.7737, 1.52085],
[1.7737, 1.65395],
[1.6043, 1.65395],
]
),
),
(
"FC11",
ShapedCoil(
[
[0.8354, -0.33423],
[0.8862, -0.33423],
[0.8862, -0.01317],
[0.8354, -0.01317],
]
),
),
(
"FC12",
ShapedCoil(
[
[0.8353, -0.67403],
[0.8861, -0.67403],
[0.8861, -0.35297],
[0.8353, -0.35297],
]
),
),
(
"FC13",
ShapedCoil(
[
[0.8357, -1.01483],
[0.8865, -1.01483],
[0.8865, -0.69377],
[0.8357, -0.69377],
]
),
),
(
"FC14",
ShapedCoil(
[
[0.8376, -1.35623],
[0.8884, -1.35623],
[0.8884, -1.03517],
[0.8376, -1.03517],
]
),
),
(
"FC15",
ShapedCoil(
[
[0.9329, -1.507],
[1.0721, -1.6462],
[1.0721, -1.5268],
[0.9329, -1.3876],
]
),
),
(
"FC16",
ShapedCoil(
[
[2.5217, -0.5349],
[2.6949, -0.5349],
[2.703, -0.3403],
[2.5298, -0.3403],
]
),
),
(
"FC17",
ShapedCoil(
[
[2.0299, -1.2017],
[2.2179, -1.2017],
[2.7368, -1.0325],
[2.5488, -1.0325],
]
),
),
(
"FC18",
ShapedCoil(
[
[1.13495, -1.64525],
[1.36985, -1.64525],
[1.36985, -1.56015],
[1.13495, -1.56015],
]
),
),
]
R = np.array(
[
1.016,
1.016,
1.016,
1.016,
1.016,
1.016,
1.016,
1.016,
1.016,
1.016,
1.016,
1.012,
1.001,
1.029,
1.042,
1.046,
1.056,
1.097,
1.108,
1.116,
1.134,
1.148,
1.162,
1.181,
1.182,
1.185,
1.19,
1.195,
1.201,
1.209,
1.215,
1.222,
1.228,
1.234,
1.239,
1.242,
1.248,
1.258,
1.263,
1.28,
1.28,
1.28,
1.31,
1.328,
1.361,
1.38,
1.419,
1.419,
1.372,
1.37167,
1.37003,
1.36688,
1.36719,
1.37178,
1.37224,
1.38662,
1.38708,
1.40382,
1.41127,
1.41857,
1.421,
1.48663,
1.4973,
1.49762,
1.49745,
1.49275,
1.4926,
1.49261,
1.49279,
1.4934,
1.4947,
1.49622,
1.47981,
1.48082,
1.48149,
1.48646,
1.49095,
1.50305,
1.59697,
1.6255,
1.63752,
1.647,
1.785,
2.07,
2.128,
2.245,
2.33956,
2.34708,
2.34913,
2.35103,
2.35158,
2.35125,
2.35051,
2.34965,
2.3487,
2.3476,
2.3402,
2.32942,
2.134,
1.786,
1.768,
1.768,
1.682,
1.372,
1.372,
1.42,
1.42,
1.273,
1.153,
1.016,
1.016,
1.016,
1.016,
1.016,
1.016,
1.016,
1.016,
]
)
Z = np.array(
[
0.00000e00,
9.64000e-01,
9.68000e-01,
1.00100e00,
1.01900e00,
1.07700e00,
1.07000e00,
1.09600e00,
1.11300e00,
1.13800e00,
1.14700e00,
1.16500e00,
1.21700e00,
1.21700e00,
1.16240e00,
1.16238e00,
1.16260e00,
1.16450e00,
1.16594e00,
1.16591e00,
1.16896e00,
1.17175e00,
1.17556e00,
1.18300e00,
1.18350e00,
1.18500e00,
1.18800e00,
1.19100e00,
1.19600e00,
1.20200e00,
1.20800e00,
1.21400e00,
1.22100e00,
1.23100e00,
1.23800e00,
1.24400e00,
1.25400e00,
1.27800e00,
1.29000e00,
1.33100e00,
1.34700e00,
1.34800e00,
1.34800e00,
1.34800e00,
1.34800e00,
1.34800e00,
1.34800e00,
1.31000e00,
1.31000e00,
1.29238e00,
1.28268e00,
1.25644e00,
1.22955e00,
1.19576e00,
1.19402e00,
1.16487e00,
1.16421e00,
1.15696e00,
1.15730e00,
1.16132e00,
1.16400e00,
1.24050e00,
1.23458e00,
1.23428e00,
1.23174e00,
1.21330e00,
1.21061e00,
1.20486e00,
1.20214e00,
1.19642e00,
1.18511e00,
1.16070e00,
1.12426e00,
1.12256e00,
1.12138e00,
1.11692e00,
1.11439e00,
1.11244e00,
1.09489e00,
1.08530e00,
1.07988e00,
1.07700e00,
1.07700e00,
1.04000e00,
9.93000e-01,
7.09000e-01,
4.61430e-01,
4.15830e-01,
2.72180e-01,
1.70180e-01,
7.01200e-02,
-3.17900e-02,
-1.44350e-01,
-2.14830e-01,
-3.26690e-01,
-3.86770e-01,
-4.53040e-01,
-4.77570e-01,
-9.73000e-01,
-1.17400e00,
-1.21100e00,
-1.25000e00,
-1.25000e00,
-1.25000e00,
-1.32900e00,
-1.32900e00,
-1.36300e00,
-1.36300e00,
-1.36300e00,
-1.22300e00,
-1.22300e00,
-8.30000e-01,
-8.00000e-01,
-4.15000e-01,
-4.00000e-01,
-1.00000e-03,
0.00000e00,
]
)
wall = Wall(R, Z)
# Note: Using same R0 convention as EFIT on DIII-D.
return Machine(coils, wall=wall, R0=1.6995)
[docs]
def MAST():
"""
Mega-Amp Spherical Tokamak. This version has all independent coils
so that each is powered by a separate coil
"""
coils = [
("P2U", Coil(0.49, 1.76)),
("P2L", Coil(0.49, -1.76)),
("P3U", Coil(1.1, 1.1)),
("P3L", Coil(1.1, -1.1)),
("P4U", Coil(1.51, 1.095)),
("P4L", Coil(1.51, -1.095)),
("P5U", Coil(1.66, 0.52)),
("P5L", Coil(1.66, -0.52)),
("P6U", Coil(1.5, 0.9)),
("P6L", Coil(1.5, -0.9)),
("P1", Solenoid(0.15, -1.4, 1.4, 100)),
]
return Machine(coils)
[docs]
def MAST_sym():
"""
Mega-Amp Spherical Tokamak. This version the upper and lower coils
are connected to the same circuits P2 - P6
"""
coils = [
(
"P2",
Circuit([("P2U", Coil(0.49, 1.76), 1.0), ("P2L", Coil(0.49, -1.76), 1.0)]),
),
("P3", Circuit([("P3U", Coil(1.1, 1.1), 1.0), ("P3L", Coil(1.1, -1.1), 1.0)])),
(
"P4",
Circuit(
[("P4U", Coil(1.51, 1.095), 1.0), ("P4L", Coil(1.51, -1.095), 1.0)]
),
),
(
"P5",
Circuit([("P5U", Coil(1.66, 0.52), 1.0), ("P5L", Coil(1.66, -0.52), 1.0)]),
),
("P6", Circuit([("P6U", Coil(1.5, 0.9), 1.0), ("P6L", Coil(1.5, -0.9), -1.0)])),
("P1", Solenoid(0.15, -1.45, 1.45, 100)),
]
return Machine(coils)
[docs]
def TCV():
coils = [
# ("A1", Coil(0.422500,0.000000)),
# ("B1", Coil(0.445700,-0.936000)),
# ("B2", Coil(0.445700,0.936000)),
# ("C1", Coil(0.621500,-1.110000)),
# ("C2", Coil(0.621500,1.110000)),
# ("D1", Coil(1.176500,-1.170000)),
# ("D2", Coil(1.176500,1.170000)),
("E1", Coil(0.505000, -0.700000)),
("E2", Coil(0.505000, -0.500000)),
("E3", Coil(0.505000, -0.300000)),
("E4", Coil(0.505000, -0.100000)),
("E5", Coil(0.505000, 0.100000)),
("E6", Coil(0.505000, 0.300000)),
("E7", Coil(0.505000, 0.500000)),
("E8", Coil(0.505000, 0.700000)),
("F1", Coil(1.309500, -0.770000)),
("F2", Coil(1.309500, -0.610000)),
("F3", Coil(1.309500, -0.310000)),
("F4", Coil(1.309500, -0.150000)),
("F5", Coil(1.309500, 0.150000)),
("F6", Coil(1.309500, 0.310000)),
("F7", Coil(1.309500, 0.610000)),
("F8", Coil(1.309500, 0.770000)),
# ("G1", Coil(1.098573,-0.651385)),
# ("G2", Coil(1.114000,-0.633000)),
# ("G3", Coil(1.129427,-0.614615)),
# ("G4", Coil(1.129427,0.614615)),
# ("G5", Coil(1.114000,0.633000)),
# ("G6", Coil(1.098573,0.651385)),
("T1", Coil(1.554000, -0.780000)),
("T2", Coil(1.717000, -0.780000)),
("T3", Coil(1.754000, -0.780000)),
(
"OH",
Circuit(
[
("OH1", Solenoid(0.43, -0.93, 0.93, 100), 0.01),
("C1", Coil(0.621500, -1.110000), 1.0),
("C2", Coil(0.621500, 1.110000), 1.0),
("D1", Coil(1.176500, -1.170000), 1.0),
("D2", Coil(1.176500, 1.170000), 1.0),
]
),
),
]
return Machine(coils)
[docs]
def MASTU_simple():
"""This is an older version of the MAST-U coilset.
A simplified set of coils, with one strand per coil.
This may be easier to use for initial development of scenarios,
but less detailed than the FilamentCoil description (MASTU).
"""
coils = [
("Solenoid", Solenoid(0.19475, -1.581, 1.581, 324)),
# ("Pc", Coil(0.067, 0.0, turns=142)),
("Pc", Solenoid(0.067, -0.6, 0.6, 142)),
(
"Px",
Circuit(
[
("PxU", Coil(0.2405, 1.2285, turns=44), 1.0),
("PxL", Coil(0.2405, -1.2285, turns=44), 1.0),
]
),
),
(
"D1",
Circuit(
[
("D1U", Coil(0.381, 1.555, turns=35), 1.0),
("D1L", Coil(0.381, -1.555, turns=35), 1.0),
]
),
),
(
"D2",
Circuit(
[
("D2U", Coil(0.574, 1.734, turns=23), 1.0),
("D2L", Coil(0.574, -1.734, turns=23), 1.0),
]
),
),
(
"D3",
Circuit(
[
("D3U", Coil(0.815, 1.980, turns=23), 1.0),
("D3L", Coil(0.815, -1.980, turns=23), 1.0),
]
),
),
(
"Dp",
Circuit(
[
("DpU", Coil(0.918, 1.501, turns=23), 1.0),
("DpL", Coil(0.918, -1.501, turns=23), 1.0),
]
),
),
(
"D5",
Circuit(
[
("D5U", Coil(1.900, 1.950, turns=27), 1.0),
("D5L", Coil(1.900, -1.950, turns=27), 1.0),
]
),
),
(
"D6",
Circuit(
[
("D6U", Coil(1.285, 1.470, turns=23), 1.0),
("D6L", Coil(1.285, -1.470, turns=23), 1.0),
]
),
),
(
"D7",
Circuit(
[
("D7U", Coil(1.520, 1.470, turns=23), 1.0),
("D7L", Coil(1.520, -1.470, turns=23), 1.0),
]
),
),
(
"P4",
Circuit(
[
("P4U", Coil(1.500, 1.100, turns=23), 1.0),
("P4L", Coil(1.500, -1.100, turns=23), 1.0),
]
),
),
(
"P5",
Circuit(
[
("P5U", Coil(1.650, 0.357, turns=23), 1.0),
("P5L", Coil(1.650, -0.357, turns=23), 1.0),
]
),
),
# Vertical control coils wired in opposite directions
# Two pairs of coils, P6 pair 1 and P6 pair 2
(
"P61",
Circuit(
[
("P61U", Coil(1.1975, 1.11175, turns=2), 1.0),
("P61L", Coil(1.1975, -1.11175, turns=2), -1.0),
]
),
),
(
"P62",
Circuit(
[
("P62U", Coil(1.2575, 1.0575, turns=2), 1.0),
("P62L", Coil(1.2575, -1.0575, turns=2), -1.0),
]
),
),
]
rwall = [
1.6,
1.2503,
1.3483,
1.47,
1.47,
1.45,
1.45,
1.3214,
1.1904,
0.89296,
0.86938,
0.83981,
0.82229,
0.81974,
0.81974,
0.82734,
0.8548,
0.89017,
0.91974,
0.94066,
1.555,
1.85,
2,
2,
2,
2,
1.3188,
1.7689,
1.7301,
1.35,
1.09,
1.09,
0.90576,
0.87889,
0.87889,
0.90717,
0.53948,
0.5112,
0.50553,
0.53594,
0.5074,
0.4974,
0.5074,
0.4788,
0.4688,
0.4788,
0.333,
0.333,
0.275,
0.334,
0.261,
0.261,
0.244,
0.261,
0.261,
0.244,
0.261,
0.261,
]
zwall = [
1,
1,
0.86,
0.86,
0.81,
0.81,
0.82,
0.82,
1.007,
1.304,
1.3312,
1.3826,
1.4451,
1.4812,
1.4936,
1.5318,
1.5696,
1.5891,
1.5936,
1.5936,
1.567,
1.08,
1.08,
1.7,
2.035,
2.169,
2.169,
1.7189,
1.68,
2.06,
2.06,
2.06,
1.8786,
1.9055,
1.9055,
1.8772,
1.5095,
1.5378,
1.5321,
1.5017,
1.4738,
1.4838,
1.4738,
1.4458,
1.4558,
1.4458,
1.303,
1.1,
1.1,
1.1,
0.502,
0.348,
0.348,
0.348,
0.146,
0.146,
0.146,
0,
]
# Concatenate with mirror image in Z,
# with points in reverse order
rwall = rwall + rwall[::-1]
zwall = zwall + [-z for z in zwall[::-1]]
return Machine(coils, Wall(rwall, zwall))
#########################################
# MAST-U, using FilamentCoil to represent multiple strands
[docs]
def MASTU():
"""MAST-Upgrade, using FilamentCoil to represent coils with different locations
for each strand.
"""
d1_upper_r = [
0.35275,
0.36745015,
0.38215014,
0.39685005,
0.35275,
0.35275,
0.35275,
0.35275,
0.35275039,
0.36745039,
0.36745039,
0.36745039,
0.36745039,
0.36745,
0.38215002,
0.38215002,
0.38215002,
0.38215002,
0.39685014,
0.39685014,
0.39685014,
0.39685014,
0.39685008,
0.41155013,
0.41155013,
0.41155013,
0.41155013,
0.4115501,
0.42625037,
0.42625007,
0.42625007,
0.42625007,
0.42625007,
0.41155002,
0.4262501,
]
d1_upper_z = [
1.60924995,
1.60924995,
1.60924995,
1.60924995,
1.59455001,
1.57984996,
1.5651499,
1.55044997,
1.53574991,
1.53574991,
1.55044997,
1.5651499,
1.57984996,
1.59455001,
1.57984996,
1.5651499,
1.55044997,
1.53574991,
1.53574991,
1.55044997,
1.5651499,
1.57984996,
1.59455001,
1.59455001,
1.57984996,
1.5651499,
1.55044997,
1.53574991,
1.53574991,
1.55044997,
1.5651499,
1.57984996,
1.59455001,
1.60924995,
1.60924995,
]
d1_lower_z = [x * -1 for x in d1_upper_z]
d2_upper_r = [
0.60125011,
0.58655024,
0.60125017,
0.60125017,
0.60125023,
0.58655,
0.58655,
0.57185012,
0.57185036,
0.57185042,
0.55715007,
0.55715007,
0.55715001,
0.54245019,
0.54245019,
0.54245001,
0.52775019,
0.52775025,
0.52775025,
0.57185012,
0.55715013,
0.54245007,
0.52774996,
]
d2_upper_z = [
1.75705004,
1.75705004,
1.74234998,
1.72765005,
1.71294999,
1.71294999,
1.72765005,
1.74234998,
1.72765005,
1.71294999,
1.71294999,
1.72765005,
1.74234998,
1.74234998,
1.72765005,
1.71294999,
1.71294999,
1.72765005,
1.74234998,
1.75705004,
1.75705004,
1.75705004,
1.75705004,
]
d2_lower_z = [x * -1 for x in d2_upper_z]
d3_upper_r = [
0.82854998,
0.8432501,
0.84325004,
0.84325004,
0.82855022,
0.82855004,
0.8285504,
0.81384999,
0.81385022,
0.81385005,
0.79915011,
0.79915005,
0.79915005,
0.78445005,
0.78444934,
0.78445005,
0.76975012,
0.76975018,
0.76975018,
0.76975006,
0.78445035,
0.79915041,
0.81384987,
]
d3_upper_z = [
2.00405002,
2.00405002,
1.98935008,
1.97465003,
1.95995009,
1.97465003,
1.98935008,
1.98935008,
1.97465003,
1.95995009,
1.95995009,
1.97465003,
1.98935008,
1.98935008,
1.97465003,
1.95995009,
1.95995009,
1.97465003,
1.98935008,
2.00405002,
2.00405002,
2.00405002,
2.00405002,
]
d3_lower_z = [x * -1 for x in d3_upper_z]
d5_upper_r = [
1.90735006,
1.92205048,
1.92205,
1.92205,
1.92205,
1.92205,
1.92205,
1.90735018,
1.9073503,
1.9073503,
1.9073503,
1.9073503,
1.90735006,
1.89265001,
1.89265013,
1.89265013,
1.89265013,
1.89265013,
1.89265001,
1.87794995,
1.87795019,
1.87795019,
1.87795019,
1.87795019,
1.87795019,
1.87795019,
1.89265037,
]
d5_upper_z = [
1.99409997,
1.99409997,
1.97940004,
1.96469998,
1.95000005,
1.93529999,
1.92060006,
1.9059,
1.92060006,
1.93529999,
1.95000005,
1.96469998,
1.97940004,
1.97940004,
1.96469998,
1.95000005,
1.93529999,
1.92060006,
1.9059,
1.9059,
1.92060006,
1.93529999,
1.95000005,
1.96469998,
1.97940004,
1.99409997,
1.99409997,
]
d5_lower_z = [x * -1 for x in d5_upper_z]
d6_upper_r = [
1.30704987,
1.32175004,
1.32175004,
1.32174993,
1.30705011,
1.32174993,
1.30704999,
1.29235005,
1.30704999,
1.29235005,
1.27765,
1.27765,
1.26295006,
1.27765,
1.26294994,
1.24825013,
1.26294994,
1.24825001,
1.24825001,
1.24825013,
1.26294994,
1.27765,
1.29234993,
]
d6_upper_z = [
1.44564998,
1.44564998,
1.46034992,
1.47504997,
1.48974991,
1.48975003,
1.47504997,
1.46034992,
1.46035004,
1.47504997,
1.48975003,
1.47504997,
1.46034992,
1.46034992,
1.47504997,
1.48974991,
1.48975003,
1.47504997,
1.46034992,
1.44564998,
1.44564998,
1.44564998,
1.44564998,
]
d6_lower_z = [x * -1 for x in d6_upper_z]
d7_upper_r = [
1.54205,
1.55675006,
1.55675006,
1.55675006,
1.54205012,
1.55674994,
1.54205,
1.52735007,
1.54204988,
1.52735007,
1.51265013,
1.52734995,
1.51265001,
1.49794996,
1.51265001,
1.49794996,
1.48325002,
1.48325002,
1.48325002,
1.48325002,
1.49795008,
1.51265001,
1.52734995,
]
d7_upper_z = [
1.44564998,
1.44564998,
1.46034992,
1.47504997,
1.48974991,
1.48974991,
1.47504997,
1.46034992,
1.46034992,
1.47504997,
1.48974991,
1.48974991,
1.47504997,
1.46035004,
1.46034992,
1.47504997,
1.48974991,
1.47504997,
1.46035004,
1.44564998,
1.44564998,
1.44564998,
1.44564998,
]
d7_lower_z = [x * -1 for x in d7_upper_z]
dp_upper_r = [
0.93285,
0.94755,
0.93285,
0.94755,
0.96224999,
0.96224999,
0.88875002,
0.90345001,
0.91815001,
0.91815001,
0.90345001,
0.88875002,
0.96224999,
0.94755,
0.93285,
0.96224999,
0.94755,
0.93285,
0.91815001,
0.90345001,
0.88875002,
0.88874996,
0.90345001,
0.91815001,
]
dp_upper_z = [
1.48634994,
1.48634994,
1.47165,
1.47165,
1.48634994,
1.47165,
1.47165,
1.47165,
1.47165,
1.48634994,
1.48634994,
1.48634994,
1.51574993,
1.51574993,
1.51574993,
1.50105,
1.50105,
1.50105,
1.51574993,
1.51574993,
1.51574993,
1.50105,
1.50105,
1.50105,
]
dp_lower_z = [x * -1 for x in dp_upper_z]
p4_upper_r = [
1.43500018,
1.53500021,
1.51000023,
1.48500025,
1.46000016,
1.43500006,
1.43500006,
1.46100008,
1.43500018,
1.46100008,
1.48700011,
1.4610002,
1.48700011,
1.51300013,
1.48700011,
1.51300013,
1.53900015,
1.51300013,
1.53900003,
1.56500018,
1.53900015,
1.56500006,
1.56500006,
]
p4_upper_z = [
1.04014993,
1.03714991,
1.03714991,
1.03714991,
1.03714991,
1.07814991,
1.1161499,
1.15414989,
1.15414989,
1.1161499,
1.07814991,
1.07814991,
1.1161499,
1.15414989,
1.15414989,
1.1161499,
1.07814991,
1.07814991,
1.1161499,
1.15414989,
1.15414989,
1.1161499,
1.07814991,
]
p4_lower_z = [x * -1 for x in p4_upper_z]
p5_upper_r = [
1.58500004,
1.61000001,
1.63499999,
1.65999997,
1.68499994,
1.58500004,
1.58500004,
1.58500004,
1.63499999,
1.63499999,
1.63499999,
1.65999997,
1.65999997,
1.65999997,
1.68499994,
1.68500006,
1.68500006,
1.71500003,
1.71500003,
1.71500003,
1.71500003,
1.6099776,
1.60997999,
]
p5_upper_z = [
0.41065004,
0.41065004,
0.41065004,
0.41065004,
0.41065004,
0.37165004,
0.33265004,
0.29365003,
0.37165004,
0.33265004,
0.29365003,
0.29365003,
0.33262005,
0.37165004,
0.37165004,
0.33265004,
0.29365003,
0.29365006,
0.33265004,
0.37165004,
0.41065004,
0.31147972,
0.35528255,
]
p5_lower_z = [x * -1 for x in p5_upper_z]
p6_upper_r = [
1.2887001,
1.2887001,
1.30900013,
1.2887001,
1.30900013,
1.33414996,
1.33414996,
1.35444999,
1.33414996,
1.35444999,
]
p6_upper_z = [
0.99616498,
0.97586501,
0.95556498,
0.95556498,
0.97586501,
0.931265,
0.91096503,
0.89066499,
0.89066499,
0.91096503,
]
p6_lower_z = [x * -1 for x in p6_upper_z]
px_upper_r = [
0.24849965,
0.24849975,
0.24849974,
0.2344998,
0.24849974,
0.24849974,
0.24849972,
0.24849972,
0.24849972,
0.24849971,
0.24849971,
0.24849971,
0.24849969,
0.24849969,
0.24849969,
0.24849968,
0.24849968,
0.24849968,
0.24849966,
0.24849966,
0.24849966,
0.24849965,
0.23449969,
0.23449969,
0.23449971,
0.23449971,
0.23449971,
0.23449971,
0.23449972,
0.23449972,
0.23449974,
0.23449974,
0.23449974,
0.23449975,
0.23449975,
0.23449977,
0.23449977,
0.23449977,
0.23449978,
0.23449978,
0.2344998,
0.2344998,
]
px_upper_z = [
1.41640627,
1.03640544,
1.0554055,
1.03164983,
1.07440555,
1.0934056,
1.11240554,
1.13140559,
1.15040565,
1.1694057,
1.18840575,
1.20740581,
1.22640586,
1.24540591,
1.26440585,
1.2834059,
1.30240595,
1.32140601,
1.34040606,
1.35940611,
1.37840617,
1.39740622,
1.41164911,
1.39264905,
1.37364912,
1.35464919,
1.33564925,
1.31664932,
1.29764926,
1.27864933,
1.2596494,
1.24064946,
1.22164953,
1.20264947,
1.18364954,
1.16464961,
1.14564967,
1.12664974,
1.10764968,
1.08864975,
1.06964982,
1.05064988,
]
px_lower_z = [x * -1 for x in px_upper_z]
pc_r = [
0.05950115,
0.05950115,
0.05950116,
0.05950116,
0.05950116,
0.05950121,
0.05950117,
0.05950117,
0.05950118,
0.05950122,
0.05950119,
0.05950119,
0.05950119,
0.05950119,
0.05950123,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.0595012,
0.05950124,
0.05950119,
0.05950119,
0.05950119,
0.05950119,
0.05950119,
0.05950122,
0.05950122,
0.05950118,
0.05950117,
0.05950117,
0.05950117,
0.05950116,
0.05950116,
0.05950115,
0.05950115,
0.05950114,
0.05950114,
0.05950113,
0.05950113,
0.05950112,
0.05950112,
0.05950111,
0.0595011,
0.05950113,
0.05950109,
0.05950108,
0.05950111,
0.0595011,
0.05950109,
0.05950109,
0.05950107,
0.0595009,
0.05950106,
0.05950104,
0.05950104,
0.05950103,
0.05950105,
0.05950146,
0.05950177,
0.05950198,
0.05950211,
0.05950096,
0.07150049,
0.0715005,
0.07150051,
0.07150052,
0.07150053,
0.07150054,
0.07150055,
0.07150055,
0.07150057,
0.07150058,
0.07150058,
0.07150059,
0.0715006,
0.07150061,
0.07150061,
0.07150062,
0.07150063,
0.07150064,
0.07150064,
0.07150065,
0.07150066,
0.07150067,
0.07150067,
0.07150067,
0.07150068,
0.07150069,
0.0715007,
0.0715007,
0.0715007,
0.07150071,
0.07150071,
0.07150072,
0.07150066,
0.07150073,
0.07150073,
0.07150073,
0.07150073,
0.07150074,
0.07150074,
0.07150085,
0.07150132,
0.07150154,
0.07150155,
0.07150079,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150073,
0.07150077,
0.07150077,
0.07150077,
0.07150077,
0.07150077,
0.07150077,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150075,
0.07150075,
0.07150074,
0.0715007,
0.07150044,
0.07150045,
0.07150047,
0.07150047,
0.07150049,
0.07150128,
0.07150129,
0.07150111,
0.07150067,
0.07150006,
0.07150054,
0.07150055,
0.07150055,
0.07150056,
0.07150057,
0.07150058,
0.07150058,
0.0715006,
0.07150061,
0.07150061,
0.07150062,
0.07150062,
0.07150063,
0.07150064,
0.07150064,
0.07150065,
0.07150065,
0.07150066,
0.07150067,
0.07150067,
0.07150067,
0.07150068,
0.07150068,
0.07150069,
0.07150069,
0.0715007,
0.0715007,
0.0715007,
0.0715007,
0.0715007,
0.07150071,
0.07150071,
0.07150071,
0.07150072,
0.07150072,
0.07150072,
0.07150072,
0.07150072,
0.07150072,
0.07150073,
0.07150073,
0.07150073,
0.07150073,
0.07150073,
0.07150073,
0.07150073,
0.07150073,
0.07150073,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150076,
0.07150075,
0.07150075,
0.07150075,
0.07150074,
0.059501,
0.05950099,
0.059501,
0.05950118,
0.05950101,
0.05950101,
0.05950101,
0.05950102,
0.05950102,
0.05950102,
0.05950103,
0.05950122,
0.05950122,
0.05950122,
0.05950104,
0.05950104,
0.05950104,
0.05950104,
0.05950104,
0.05950104,
0.05950104,
0.05950104,
0.05950104,
0.0595013,
0.05950121,
0.05950123,
0.05950116,
0.05950124,
0.05950121,
0.05950119,
0.05950121,
0.05950122,
0.05950122,
0.05950121,
0.0595012,
0.05950121,
0.05950121,
0.0595012,
0.05950101,
0.05950119,
0.05950119,
0.05950118,
0.05950118,
0.05950117,
0.05950117,
0.05950116,
0.05950116,
0.05950119,
0.05950115,
0.05950114,
0.05950114,
0.05950113,
0.05950112,
0.05950112,
0.05950111,
0.0595011,
0.05950109,
0.05950108,
0.05950107,
0.05950107,
0.05950106,
0.05950105,
0.05950104,
0.05950103,
0.05950102,
0.05950101,
0.059501,
0.05950099,
0.05950078,
0.05950077,
0.05950096,
]
pc_z = [
-7.81521082e-01,
-7.59520829e-01,
-7.37520635e-01,
-7.15520382e-01,
-6.93520188e-01,
-6.71519935e-01,
-6.49519742e-01,
-6.27519488e-01,
-6.05519295e-01,
-5.83519042e-01,
-5.61518848e-01,
-5.39518654e-01,
-5.17518401e-01,
-4.95518208e-01,
-4.73517984e-01,
-4.51517761e-01,
-4.29517567e-01,
-4.07517344e-01,
-3.85517120e-01,
-3.63516897e-01,
-3.41516674e-01,
-3.19516450e-01,
-2.97516227e-01,
-2.75516003e-01,
-2.53515780e-01,
-2.31515557e-01,
-2.09515333e-01,
-1.87515110e-01,
-1.65514901e-01,
-1.43514678e-01,
-1.21514454e-01,
-9.95142311e-02,
-7.75140151e-02,
-5.55137955e-02,
-3.35135758e-02,
-1.15133552e-02,
1.04868645e-02,
3.24870832e-02,
5.44873066e-02,
7.64875263e-02,
9.84877497e-02,
1.20487973e-01,
1.42488196e-01,
1.64488405e-01,
1.86488628e-01,
2.08488852e-01,
2.30489075e-01,
2.52489269e-01,
2.74489492e-01,
2.96489716e-01,
3.18489939e-01,
3.40490162e-01,
3.62490386e-01,
3.84490609e-01,
4.06490862e-01,
4.28490698e-01,
4.50491309e-01,
4.72491503e-01,
4.94491905e-01,
5.16492069e-01,
5.38492322e-01,
5.60492516e-01,
5.82492828e-01,
6.04492962e-01,
6.26493216e-01,
6.48493409e-01,
6.70493662e-01,
6.92494035e-01,
7.14494169e-01,
7.36494422e-01,
7.58494616e-01,
7.58488476e-01,
7.36488640e-01,
7.14488804e-01,
6.92489028e-01,
6.70489192e-01,
6.48489356e-01,
6.26489520e-01,
6.04489744e-01,
5.82489908e-01,
5.60490072e-01,
5.38490295e-01,
5.16490459e-01,
4.94490594e-01,
4.72490788e-01,
4.50490981e-01,
4.28491175e-01,
4.06491339e-01,
3.84491533e-01,
3.62491727e-01,
3.40491891e-01,
3.18492085e-01,
2.96492279e-01,
2.74492443e-01,
2.52492636e-01,
2.30492830e-01,
2.08493009e-01,
1.86493203e-01,
1.64493382e-01,
1.42493561e-01,
1.20493740e-01,
9.84939337e-02,
7.64944404e-02,
5.44943213e-02,
3.24945636e-02,
1.04946950e-02,
-1.15051102e-02,
-3.35049182e-02,
-5.55047244e-02,
-7.75045305e-02,
-9.95043367e-02,
-1.21504150e-01,
-1.43503949e-01,
-1.65503755e-01,
-1.87503561e-01,
-2.09503368e-01,
-2.31503174e-01,
-2.53502995e-01,
-2.75502801e-01,
-2.97502607e-01,
-3.19502413e-01,
-3.41502219e-01,
-3.63502026e-01,
-3.85501832e-01,
-4.07501638e-01,
3.95501614e-01,
-4.29501444e-01,
-4.51501250e-01,
-4.73501056e-01,
-4.95500863e-01,
-5.17500639e-01,
-5.39500475e-01,
-5.61500251e-01,
-5.83500087e-01,
-6.05499864e-01,
-6.27499700e-01,
-6.49499476e-01,
-6.71499312e-01,
-6.93499088e-01,
-7.15498924e-01,
-7.37498701e-01,
-7.59498537e-01,
-7.81498313e-01,
7.69502759e-01,
7.47502983e-01,
7.25503147e-01,
7.03503370e-01,
6.81503534e-01,
6.59503758e-01,
6.37503922e-01,
6.15504146e-01,
5.93504310e-01,
5.71504533e-01,
5.49504697e-01,
5.27504921e-01,
5.05505085e-01,
4.83505279e-01,
4.61505353e-01,
4.39505666e-01,
4.17505413e-01,
3.73506218e-01,
3.51506412e-01,
3.29506606e-01,
3.07506770e-01,
2.85506964e-01,
2.63507158e-01,
2.41507336e-01,
2.19507530e-01,
1.97507709e-01,
1.75507888e-01,
1.53508082e-01,
1.31508261e-01,
1.09508440e-01,
8.75086263e-02,
6.55088127e-02,
4.35089879e-02,
2.15091743e-02,
-4.90644481e-04,
-2.24904604e-02,
-4.44902778e-02,
-6.64900914e-02,
-8.84899125e-02,
-1.10489726e-01,
-1.32489547e-01,
-1.54489353e-01,
-1.76489174e-01,
-1.98488995e-01,
-2.20488816e-01,
-2.42488623e-01,
-2.64488459e-01,
-2.86488265e-01,
-3.08488101e-01,
-3.30487907e-01,
-3.52487713e-01,
-3.74487549e-01,
-3.96487355e-01,
-4.18487161e-01,
-4.40486997e-01,
-4.62486804e-01,
-4.84486639e-01,
-5.06486416e-01,
-5.28486252e-01,
-5.50486028e-01,
-5.72485864e-01,
-5.94485700e-01,
-6.16485476e-01,
-6.38485312e-01,
-6.60485148e-01,
-6.82484925e-01,
-7.04484761e-01,
-7.26484597e-01,
-7.48484373e-01,
-7.70484209e-01,
-7.26499677e-01,
-7.48499930e-01,
-7.04499483e-01,
-7.70499945e-01,
-6.82499230e-01,
-6.60498977e-01,
-6.38498724e-01,
-6.16498530e-01,
-5.94498277e-01,
-5.72498024e-01,
-5.50497830e-01,
-5.28497398e-01,
-5.06497145e-01,
-4.84496951e-01,
-4.62496907e-01,
-4.40496653e-01,
-4.18496430e-01,
-3.96496207e-01,
-3.74495953e-01,
-3.52495730e-01,
-3.30495477e-01,
-3.08495253e-01,
-2.86495030e-01,
-2.63993531e-01,
-2.42494494e-01,
-2.20494315e-01,
-1.98494136e-01,
-1.76493689e-01,
-1.54493496e-01,
-1.32493302e-01,
-1.10493094e-01,
-8.84926915e-02,
-6.64924607e-02,
-4.44922261e-02,
-2.24919878e-02,
-4.91753221e-04,
2.15084739e-02,
4.35086973e-02,
6.55087605e-02,
1.09509401e-01,
1.31509617e-01,
1.53509840e-01,
1.75510064e-01,
1.97510287e-01,
2.19510511e-01,
2.41510719e-01,
2.63510942e-01,
8.75091851e-02,
2.85511166e-01,
3.07511151e-01,
3.29511374e-01,
3.51511598e-01,
3.73512030e-01,
3.95512253e-01,
4.17512476e-01,
4.39512491e-01,
4.61512923e-01,
4.83513147e-01,
5.05513191e-01,
5.27513623e-01,
5.49513638e-01,
5.71513832e-01,
5.93514264e-01,
6.15514517e-01,
6.37514710e-01,
6.59514725e-01,
6.81514919e-01,
7.03515172e-01,
7.25515604e-01,
7.47515619e-01,
7.69516051e-01,
]
coils = [
("Solenoid", Solenoid(0.19475, -1.581, 1.581, 324, control=False)),
("Pc", FilamentCoil(pc_r, pc_z)),
(
"Px",
Circuit(
[
("PxU", FilamentCoil(px_upper_r, px_upper_z), 1.0),
("PxL", FilamentCoil(px_upper_r, px_lower_z), 1.0),
]
),
),
(
"D1",
Circuit(
[
("D1U", FilamentCoil(d1_upper_r, d1_upper_z), 1.0),
("D1L", FilamentCoil(d1_upper_r, d1_lower_z), 1.0),
]
),
),
(
"D2",
Circuit(
[
("D2U", FilamentCoil(d2_upper_r, d2_upper_z), 1.0),
("D2L", FilamentCoil(d2_upper_r, d2_lower_z), 1.0),
]
),
),
(
"D3",
Circuit(
[
("D3U", FilamentCoil(d3_upper_r, d3_upper_z), 1.0),
("D3L", FilamentCoil(d3_upper_r, d3_lower_z), 1.0),
]
),
),
(
"Dp",
Circuit(
[
("DPU", FilamentCoil(dp_upper_r, dp_upper_z), 1.0),
("DPL", FilamentCoil(dp_upper_r, dp_lower_z), 1.0),
]
),
),
(
"D5",
Circuit(
[
("D5U", FilamentCoil(d5_upper_r, d5_upper_z), 1.0),
("D5L", FilamentCoil(d5_upper_r, d5_lower_z), 1.0),
]
),
),
(
"D6",
Circuit(
[
("D6U", FilamentCoil(d6_upper_r, d6_upper_z), 1.0),
("D6L", FilamentCoil(d6_upper_r, d6_lower_z), 1.0),
]
),
),
(
"D7",
Circuit(
[
("D7U", FilamentCoil(d7_upper_r, d7_upper_z), 1.0),
("D7L", FilamentCoil(d7_upper_r, d7_lower_z), 1.0),
]
),
),
(
"P4",
Circuit(
[
("P4U", FilamentCoil(p4_upper_r, p4_upper_z), 1.0),
("P4L", FilamentCoil(p4_upper_r, p4_lower_z), 1.0),
]
),
),
(
"P5",
Circuit(
[
("P5U", FilamentCoil(p5_upper_r, p5_upper_z), 1.0),
("P5L", FilamentCoil(p5_upper_r, p5_lower_z), 1.0),
]
),
),
(
"P6",
Circuit(
[
("P6U", FilamentCoil(p6_upper_r, p6_upper_z), 1.0),
("P6L", FilamentCoil(p6_upper_r, p6_lower_z), -1.0),
]
),
),
]
rwall = [
1.6,
1.2503,
1.3483,
1.47,
1.47,
1.45,
1.45,
1.3214,
1.1904,
0.89296,
0.86938,
0.83981,
0.82229,
0.81974,
0.81974,
0.82734,
0.8548,
0.89017,
0.91974,
0.94066,
1.555,
1.85,
2,
2,
2,
2,
1.3188,
1.7689,
1.7301,
1.35,
1.09,
1.09,
0.90576,
0.87889,
0.87889,
0.90717,
0.53948,
0.5112,
0.50553,
0.53594,
0.5074,
0.4974,
0.5074,
0.4788,
0.4688,
0.4788,
0.333,
0.333,
0.275,
0.334,
0.261,
0.261,
0.244,
0.261,
0.261,
0.244,
0.261,
0.261,
]
zwall = [
1,
1,
0.86,
0.86,
0.81,
0.81,
0.82,
0.82,
1.007,
1.304,
1.3312,
1.3826,
1.4451,
1.4812,
1.4936,
1.5318,
1.5696,
1.5891,
1.5936,
1.5936,
1.567,
1.08,
1.08,
1.7,
2.035,
2.169,
2.169,
1.7189,
1.68,
2.06,
2.06,
2.06,
1.8786,
1.9055,
1.9055,
1.8772,
1.5095,
1.5378,
1.5321,
1.5017,
1.4738,
1.4838,
1.4738,
1.4458,
1.4558,
1.4458,
1.303,
1.1,
1.1,
1.1,
0.502,
0.348,
0.348,
0.348,
0.146,
0.146,
0.146,
0,
]
# Concatenate with mirror image in Z,
# with points in reverse order
rwall = rwall + rwall[::-1]
zwall = zwall + [-z for z in zwall[::-1]]
return Machine(coils, Wall(rwall, zwall))
if __name__ == "__main__":
# Run test case
# Define a machine with a single coil
coils = [{"R": 2.0, "Z": "-1.5", "label": "P1", "current": 3.0}]
tokamak = Machine(coils)