Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Options class #33

Merged
merged 24 commits into from
May 5, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fa57c2b
Define Options and OptionsFactory classes
johnomotani Apr 28, 2020
ef0bcdf
Use new Options/OptionsFactory in hypnotoad
johnomotani Apr 29, 2020
5c6a68e
Ensure no persistent state is set from nonorthogonal_options
johnomotani Apr 30, 2020
0ca3b13
Add methods to reset nonorthogonal options
johnomotani Apr 30, 2020
d3642e8
Implement new options in GUI and executable scripts
johnomotani Apr 30, 2020
ca70c28
Use strings instead of expressions for defaults where possible
johnomotani May 1, 2020
a8b4d69
Remove hypnotoad_options.py
johnomotani May 1, 2020
03142d7
Remove options package from requirements
johnomotani May 1, 2020
2809a34
Update whats-new.md
johnomotani May 1, 2020
defdd59
Tidy up function that returns default if value is None
johnomotani May 1, 2020
953b19b
Remove empty dict {} default arguments
johnomotani May 1, 2020
c7f05c5
Use a list for refine_methods rather than comma-separated string
johnomotani May 1, 2020
5d29b4a
Use a hasattr() check instead of try...except
johnomotani May 1, 2020
ac32660
Better formatting for options doc attributes
johnomotani May 1, 2020
36cf3c7
Move shiftedmetric to MeshRegion options
johnomotani May 1, 2020
968efd7
Wrap text in tooltips for options widget table
johnomotani May 1, 2020
33ba0bb
Remove line breaks from refine_methods doc
johnomotani May 1, 2020
cce1c15
Remove unused 'options_factory' objects
johnomotani May 4, 2020
052f51d
Move with_default() to utils/utils.py, add tests
johnomotani May 4, 2020
ea78bb8
Replace OptionsFactory with external optionsfactory package
johnomotani May 4, 2020
2369638
Update minor changes to OptionsFactory API
johnomotani May 4, 2020
75abc97
Fix use of sin_angle_at_start and sin_angle_at_end
johnomotani May 4, 2020
a3782a6
Allow ints to be passed for floats
johnomotani May 4, 2020
ca005f1
Fix SolutionError message in refinePoint()
johnomotani May 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
509 changes: 285 additions & 224 deletions hypnotoad/cases/tokamak.py

Large diffs are not rendered by default.

170 changes: 102 additions & 68 deletions hypnotoad/cases/torpex.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,27 @@

from ..core.mesh import BoutMesh
from ..core.equilibrium import (
setDefault,
Equilibrium,
PsiContour,
Point2D,
EquilibriumRegion,
SolutionError,
)
from ..geqdsk._geqdsk import read as geq_read
from ..utils.hypnotoad_options import (
HypnotoadOptions,
optionsTableString,
)
from ..utils.options import WithMeta, is_positive, optionsTableString

# type for manipulating information about magnetic field coils
from collections import namedtuple

Coil = namedtuple("Coil", "R, Z, I")


def withDefault(option, default):
if option is not None:
return option
else:
return default
johnomotani marked this conversation as resolved.
Show resolved Hide resolved


class TORPEXMagneticField(Equilibrium):
"""
Magnetic configuration defined by coil positions and currents for the TORPEX device
Expand All @@ -60,70 +62,100 @@ class TORPEXMagneticField(Equilibrium):
Zmax = 0.2

# Add TORPEX-specific options and default values
user_options = HypnotoadOptions.add(
nx_core=None,
nx_sol=None,
ny_inner_lower_divertor=None,
ny_inner_upper_divertor=None,
ny_outer_upper_divertor=None,
ny_outer_lower_divertor=None,
psi_core=None,
psi_sol=None,
psi_sol_inner=None,
psi_pf=None,
psi_pf_lower=None,
psi_pf_upper=None,
user_options_factory = Equilibrium.user_options_factory.add(
refine_methods="line",
nx_core=WithMeta(
4,
doc="Number of radial points in the PFR regions",
value_type=int,
checks=is_positive,
),
nx_sol=WithMeta(
4,
doc="Number of radial points in the SOL regions",
value_type=int,
checks=is_positive,
),
ny_inner_lower_divertor=WithMeta(
4,
doc="Number of poloidal points in the inner, lower leg",
value_type=int,
checks=is_positive,
),
ny_inner_upper_divertor=WithMeta(
4,
doc="Number of poloidal points in the inner, upper leg",
value_type=int,
checks=is_positive,
),
ny_outer_upper_divertor=WithMeta(
4,
doc="Number of poloidal points in the outer, upper leg",
value_type=int,
checks=is_positive,
),
ny_outer_lower_divertor=WithMeta(
4,
doc="Number of poloidal points in the outer, lower leg",
value_type=int,
checks=is_positive,
),
psi_core=WithMeta(
None,
doc="psi value of the 'core' (used as default for PFR regions)",
value_type=float,
),
psi_sol=WithMeta(
None, doc="psi value of the SOL", value_type=float, checks=is_positive,
),
psi_sol_inner=WithMeta(
lambda options: options.psi_sol,
doc="psi value of the inner SOL",
value_type=float,
),
psi_pf=WithMeta(
lambda options: options.psi_core,
doc="psi value of the PFRs",
value_type=float,
),
psi_pf_lower=WithMeta(
lambda options: options.psi_pf,
doc="psi value of the lower PFR",
value_type=float,
),
psi_pf_upper=WithMeta(
lambda options: options.psi_pf,
doc="psi value of the upper PFR",
value_type=float,
),
saddle_point_p1=[0.85, -0.15],
saddle_point_p2=[0.85, 0.15],
)

def __init__(self, equilibOptions, meshOptions, **kwargs):

self.equilibOptions = equilibOptions
def __init__(self, equilibOptions, meshOptions):

# Set up options read from user input
self.user_options = TORPEXMagneticField.user_options

# Set sensible defaults for options
self.user_options.set(
xpoint_poloidal_spacing_length=5.0e-2,
nonorthogonal_xpoint_poloidal_spacing_length=5.0e-2,
follow_perpendicular_rtol=2.0e-8,
follow_perpendicular_atol=1.0e-8,
refine_width=1.0e-5,
refine_atol=2.0e-8,
)

default_options = self.user_options.copy()
self.user_options.set(**meshOptions)
self.user_options = self.user_options.push(kwargs)

setDefault(self.user_options, "psi_pf", self.user_options.psi_core)
setDefault(self.user_options, "psi_pf_lower", self.user_options.psi_pf)
setDefault(self.user_options, "psi_pf_upper", self.user_options.psi_pf)

setDefault(self.user_options, "psi_sol_inner", self.user_options.psi_sol)
self.user_options = self.user_options_factory.create(meshOptions)

setDefault(
self.user_options,
"poloidal_spacing_delta_psi",
self.poloidal_spacing_delta_psi = withDefault(
self.user_options.poloidal_spacing_delta_psi,
numpy.abs((self.user_options.psi_core - self.user_options.psi_sol) / 20.0),
)

print(optionsTableString(self.user_options, default_options))
print(optionsTableString(self.user_options))

# Call Equilibrium constructor after adding stuff to options
super().__init__(**kwargs)
super().__init__(meshOptions)

if "Coils" in self.equilibOptions:
self.coils = [Coil(**c) for c in self.equilibOptions["Coils"]]
if "Coils" in equilibOptions:
self.coils = [Coil(**c) for c in equilibOptions["Coils"]]

self.magneticFunctionsFromCoils()

self.Bt_axis = self.equilibOptions["Bt_axis"]
elif "gfile" in self.equilibOptions:
self.Bt_axis = equilibOptions["Bt_axis"]
elif "gfile" in equilibOptions:
# load a g-file
with open(self.equilibOptions["gfile"], "rt") as fh:
with open(equilibOptions["gfile"], "rt") as fh:
gfile = geq_read(fh)

R = numpy.linspace(
Expand Down Expand Up @@ -202,13 +234,13 @@ def __init__(self, equilibOptions, meshOptions, **kwargs):
self.magneticFunctionsFromGrid(R, Z, psirz)

self.Bt_axis = gfile["bcentr"]
elif "matfile" in self.equilibOptions:
elif "matfile" in equilibOptions:
# Loading directly from the TORPEX-provided matlab file should be slightly
# more accurate than going via a g-file because g-files don't save full
# double-precision
from scipy.io import loadmat

eqfile = loadmat(self.equilibOptions["matfile"])["eq"]
eqfile = loadmat(equilibOptions["matfile"])["eq"]

R = eqfile["R"][0, 0]
Z = eqfile["Z"][0, 0]
Expand Down Expand Up @@ -463,26 +495,28 @@ def makeRegions(self, npoints=100):
legoptions[name] = options

# set hard-wired poloidal grid spacing options
ny_total = sum(opt["ny"] for opt in legoptions.values())
self.ny_total = sum(opt["ny"] for opt in legoptions.values())

setDefault(self.options, "N_norm", ny_total)
self.regions = OrderedDict()
wall_vectors = OrderedDict()
s = numpy.linspace(10.0 * PsiContour.options.refine_atol, 1.0, npoints)
s = numpy.linspace(10.0 * self.user_options.refine_atol, 1.0, npoints)
for i, boundary_position in enumerate(boundary):
name = legnames[i]
boundaryPoint = self.wallPosition(boundary_position)
legR = xpoint.R + s * (boundaryPoint.R - xpoint.R)
legZ = xpoint.Z + s * (boundaryPoint.Z - xpoint.Z)
leg = EquilibriumRegion(
self,
legnames[i],
2,
self.user_options,
self.options.push(legoptions[name]),
[Point2D(R, Z) for R, Z in zip(legR, legZ)],
self.psi,
self.psi_sep[0],
equilibrium=self,
name=name,
nSegments=2,
settings=dict(self.user_options),
nonorthogonal_settings=dict(self.nonorthogonal_options),
nx=legoptions[name]["nx"],
ny=legoptions[name]["ny"],
kind=legoptions[name]["kind"],
ny_total=self.ny_total,
points=[Point2D(R, Z) for R, Z in zip(legR, legZ)],
psi_val=self.psi_sep[0],
)
self.regions[name] = leg.getRefined()
wall_vectors[name] = self.wallVector(boundary_position)
Expand Down Expand Up @@ -602,11 +636,11 @@ def parseInput(filename):
return equilib_inputs, mesh_inputs


def createMesh(filename, **kwargs):
def createMesh(filename):
# parse input file
equilibOptions, meshOptions = parseInput(filename)

equilibrium = TORPEXMagneticField(equilibOptions, meshOptions, **kwargs)
equilibrium = TORPEXMagneticField(equilibOptions, meshOptions)

print("X-point", equilibrium.x_points[0], "with psi=" + str(equilibrium.psi_sep[0]))

Expand Down
Loading