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 1 commit
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
49 changes: 22 additions & 27 deletions hypnotoad/cases/tokamak.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,33 +533,6 @@ def makeRegions(self):
"""
assert self.psi_axis is not None

self.updateOptions()

# Check that there are only one or two left
assert 0 < len(self.x_points) <= 2

if len(self.x_points) == 1:
# Generate the specifications for a lower or upper single null
leg_regions, core_regions, segments, connections = self.describeSingleNull()
else:
# Specifications for a double null (connected or disconnected)
leg_regions, core_regions, segments, connections = self.describeDoubleNull()

# Create a new dictionary, which will contain all regions
# including core and legs
all_regions = leg_regions.copy()
all_regions.update(self.coreRegionToRegion(core_regions))

# Create the regions in an OrderedDict, assign to self.regions
self.regions = self.createRegionObjects(all_regions, segments)

# Make the connections between regions
for connection in connections:
self.makeConnection(*connection)

def updateOptions(self):
super().updateOptions()

# psi values
def psinorm_to_psi(psinorm):
if psinorm is None:
Expand Down Expand Up @@ -611,6 +584,28 @@ def psi_to_psinorm(psi):
)
)

# Check that there are only one or two left
assert 0 < len(self.x_points) <= 2

if len(self.x_points) == 1:
# Generate the specifications for a lower or upper single null
leg_regions, core_regions, segments, connections = self.describeSingleNull()
else:
# Specifications for a double null (connected or disconnected)
leg_regions, core_regions, segments, connections = self.describeDoubleNull()

# Create a new dictionary, which will contain all regions
# including core and legs
all_regions = leg_regions.copy()
all_regions.update(self.coreRegionToRegion(core_regions))

# Create the regions in an OrderedDict, assign to self.regions
self.regions = self.createRegionObjects(all_regions, segments)

# Make the connections between regions
for connection in connections:
self.makeConnection(*connection)

def describeSingleNull(self):
"""
Create the specifications for a single null configuration
Expand Down
18 changes: 11 additions & 7 deletions hypnotoad/core/equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,11 @@ def __init__(
self.xPointsAtStart.append(None)
self.xPointsAtEnd.append(None)

def resetNonorthogonalOptions(self, nonorthogonal_settings):
self.nonorthogonal_options = self.nonorthogonal_options_factory.create(
nonorthogonal_settings
)

def getSpacings(self):
# Set spacings depending on options.kind
if self.kind.split(".")[0] == "wall":
Expand Down Expand Up @@ -2907,13 +2912,12 @@ def __init__(self, nonorthogonal_settings):
nonorthogonal_settings
)

def updateOptions(self):
"""
Set default values from user_options
"""
if hasattr(self, "regions"):
for region in self.regions.values():
region.setupSpacing()
def resetNonorthogonalOptions(self, nonorthogonal_settings):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to create a new object if possible, rather than reset the options, in case this object is shared elsewhere. Is modifying options definitely needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is necessary - once I update #26 you'll be able to try this out, but when creating nonorthogonal grids, the initial construction (creating FineContours and finding wall intersections) takes a long time. Then often the grids look weird for one reason or another, and the way to fix that is to adjust the poloidal spacing of the grid points, which can be done much more quickly than building the Mesh object in the first place, because the FineContours don't need to change, so all the work done in constructing them can just be reused. It does make the workflow a lot better, so I think it's worth the compromise of allowing a small number of options to be changed. I've tried to make sure that there is no persistent state in the Mesh/Equilibrium/MeshRegion/EquilibriumRegion that depends on nonorthogonal_options except for the actual position of the grid points to minimise the chances of some odd history-dependant thing happening. Would still recommend building the grid once from the ground up with the final settings once they're decided on to be safe (and maybe we should enforce that in the GUI at some point).

self.nonorthogonal_options = self.nonorthogonal_options_factory.create(
nonorthogonal_settings
)
for region in self.regions.values():
region.resetNonorthogonalOptions(dict(self.nonorthogonal_options))

def makeConnection(self, lowerRegion, lowerSegment, upperRegion, upperSegment):
"""
Expand Down
7 changes: 5 additions & 2 deletions hypnotoad/core/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -2005,14 +2005,17 @@ def makeRegions(self):
break
self.y_groups.append(group)

def redistributePoints(self, **kwargs):
def redistributePoints(self, nonorthogonal_settings):
warnings.warn(
"It is not recommended to use Mesh.redistributePoints() for 'production' "
"output. Suggest saving the final settings to a .yaml file and creating the "
"'production' grid non-interactively to ensure reproducibility."
)

self.user_options.set(**kwargs)
self.nonorthogonal_options = self.nonorthogonal_options_factory.create(
nonorthogonal_settings
)
self.equilibrium.resetNonorthogonalOptions(dict(self.nonorthogonal_options))

assert (
not self.user_options.orthogonal
Expand Down
59 changes: 25 additions & 34 deletions hypnotoad/test_suite/test_equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,11 +900,11 @@ def test_combineSfuncsPoloidalSpacingRangeLower(self, eqReg):
n = len(eqReg)
L = eqReg.totalDistance()

eqReg.monotonic_d_lower = L * 40.0 / (n - 1)
eqReg.monotonic_d_upper = L * 40.0 / (n - 1)
eqReg.nonorthogonal_range_lower = 0.1
eqReg.nonorthogonal_range_lower_inner = 0.1
eqReg.nonorthogonal_range_lower_outer = 0.1
new_settings = {
"nonorthogonal_target_poloidal_spacing_length": L * 40.0 / (n - 1),
"nonorthogonal_target_poloidal_spacing_range": 0.1,
}
eqReg.resetNonorthogonalOptions(new_settings)
eqReg.ny_total = 40

def sfunc_orthogonal(i):
Expand All @@ -926,11 +926,11 @@ def test_combineSfuncsPoloidalSpacingRangeUpper(self, eqReg):
n = len(eqReg)
L = eqReg.totalDistance()

eqReg.monotonic_d_lower = L * 40.0 / (n - 1)
eqReg.monotonic_d_upper = L * 40.0 / (n - 1)
eqReg.nonorthogonal_range_upper = 0.1
eqReg.nonorthogonal_range_upper_inner = 0.1
eqReg.nonorthogonal_range_upper_outer = 0.1
new_settings = {
"nonorthogonal_target_poloidal_spacing_length": L * 40.0 / (n - 1),
"nonorthogonal_target_poloidal_spacing_range": 0.1,
}
eqReg.resetNonorthogonalOptions(new_settings)
eqReg.ny_total = 40

def sfunc_orthogonal(i):
Expand All @@ -952,14 +952,11 @@ def test_combineSfuncsPoloidalSpacingRangeBoth(self, eqReg):
n = len(eqReg)
L = eqReg.totalDistance()

eqReg.monotonic_d_lower = L * 40.0 / (n - 1)
eqReg.monotonic_d_upper = L * 40.0 / (n - 1)
eqReg.nonorthogonal_range_lower = 0.1
eqReg.nonorthogonal_range_lower_inner = 0.1
eqReg.nonorthogonal_range_lower_outer = 0.1
eqReg.nonorthogonal_range_upper = 0.1
eqReg.nonorthogonal_range_upper_inner = 0.1
eqReg.nonorthogonal_range_upper_outer = 0.1
new_settings = {
"nonorthogonal_target_poloidal_spacing_length": L * 40.0 / (n - 1),
"nonorthogonal_target_poloidal_spacing_range": 0.1,
}
eqReg.resetNonorthogonalOptions(new_settings)
eqReg.ny_total = 40

def sfunc_orthogonal(i):
Expand All @@ -985,14 +982,11 @@ def test_combineSfuncsPerpSpacingIntegrated(self, eqReg):
# MeshRegion.distributePointsNonorthogonal for the default 'combined' option.
n = len(eqReg)

eqReg.monotonic_d_lower = 0.1
eqReg.monotonic_d_upper = 0.1
eqReg.nonorthogonal_range_lower = 0.3
eqReg.nonorthogonal_range_lower_inner = 0.3
eqReg.nonorthogonal_range_lower_outer = 0.3
eqReg.nonorthogonal_range_upper = 0.3
eqReg.nonorthogonal_range_upper_inner = 0.3
eqReg.nonorthogonal_range_upper_outer = 0.3
new_settings = {
"nonorthogonal_target_poloidal_spacing_length": 0.1,
"nonorthogonal_target_poloidal_spacing_range": 0.3,
}
eqReg.resetNonorthogonalOptions(new_settings)
eqReg.ny_total = 40

sfunc_orthogonal_original = eqReg.contourSfunc()
Expand Down Expand Up @@ -1031,14 +1025,11 @@ def test_combineSfuncsPoloidalSpacingIntegrated(self, eqReg):
# option
n = len(eqReg)

eqReg.monotonic_d_lower = 0.1
eqReg.monotonic_d_upper = 0.1
eqReg.nonorthogonal_range_lower = 0.2
eqReg.nonorthogonal_range_lower_inner = 0.2
eqReg.nonorthogonal_range_lower_outer = 0.2
eqReg.nonorthogonal_range_upper = 0.2
eqReg.nonorthogonal_range_upper_inner = 0.2
eqReg.nonorthogonal_range_upper_outer = 0.2
new_settings = {
"nonorthogonal_target_poloidal_spacing_length": 0.1,
"nonorthogonal_target_poloidal_spacing_range": 0.2,
}
eqReg.resetNonorthogonalOptions(new_settings)
eqReg.ny_total = 40

sfunc_orthogonal_original = eqReg.contourSfunc()
Expand Down