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

Allow keeping y boundary cells at second divertor #40

Merged
merged 6 commits into from
Aug 7, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 2 additions & 0 deletions xbout/boutdataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ def open_boutdataset(datapath='./BOUT.dmp.*.nc',
If true, keep y-direction boundary cells (the cells past the physical edges of the
grid, where boundary conditions are set); increases the size of the y dimension in
the returned data-set. If false, trim these cells.
Note: in double-null topology, setting this argument to True loads the boundary
cells at the second divertor, so the length of the y-dimension would be ny+4*MYG.
run_name : str, optional
info : bool, optional

Expand Down
24 changes: 20 additions & 4 deletions xbout/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'wtime_comms', 'wtime_io', 'wtime_per_rhs', 'wtime_per_rhs_e',
'wtime_per_rhs_i']


def _auto_open_mfboutdataset(datapath, chunks={}, info=True,
keep_xboundaries=False, keep_yboundaries=False):
filepaths, filetype = _expand_filepaths(datapath)
Expand Down Expand Up @@ -177,7 +178,7 @@ def _trim(ds, *, guards, keep_boundaries, nxpe, nype):
# Relies on a change to xarray so datasets always have source encoding
# See xarray GH issue #2550
lower_boundaries, upper_boundaries = _infer_contains_boundaries(
ds.encoding['source'], nxpe, nype)
ds, nxpe, nype)
else:
lower_boundaries, upper_boundaries = {}, {}

Expand Down Expand Up @@ -211,16 +212,19 @@ def _trim(ds, *, guards, keep_boundaries, nxpe, nype):
return trimmed_ds


def _infer_contains_boundaries(filename, nxpe, nype):
def _infer_contains_boundaries(ds, nxpe, nype):
"""
Uses the name of the output file and the domain decomposition to work out
whether this dataset contains boundary cells, and on which side.
Uses the name of the output file, BOUT++'s topology indices, and the domain
decomposition to work out whether this dataset contains boundary cells, and on which
side.

Uses knowledge that BOUT names its output files as /folder/prefix.num.nc,
with a numbering scheme
num = nxpe*i + j, where i={0, ..., nype}, j={0, ..., nxpe}
"""

filename = ds.encoding['source']

*prefix, filenum, extension = Path(filename).suffixes
filenum = int(filenum.replace('.', ''))

Expand All @@ -232,6 +236,18 @@ def _infer_contains_boundaries(filename, nxpe, nype):
lower_boundaries['y'] = filenum < nxpe
upper_boundaries['y'] = filenum >= (nype-1)*nxpe

jyseps2_1 = int(ds['jyseps2_1'])
jyseps1_2 = int(ds['jyseps1_2'])
if jyseps1_2 > jyseps2_1:
# second divertor present
yproc = filenum // nxpe
ny_inner = int(ds['ny_inner'])
mysub = int(ds['MYSUB'])
if mysub*(yproc + 1) == ny_inner:
upper_boundaries['y'] = True
elif mysub*yproc == ny_inner:
lower_boundaries['y'] = True

return lower_boundaries, upper_boundaries


Expand Down
118 changes: 116 additions & 2 deletions xbout/tests/test_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,81 @@ def test_infer_boundaries_2d_parallelization(self, filenum, nxpe, nype,
-----> x
"""

filename = "folder0/BOUT.dmp." + str(filenum) + ".nc"
ds = create_test_data(0)
ds['jyseps2_1'] = 0
ds['jyseps1_2'] = 0
ds.encoding['source'] = "folder0/BOUT.dmp." + str(filenum) + ".nc"
actual_lower_boundaries, actual_upper_boundaries = _infer_contains_boundaries(
filename, nxpe, nype)
ds, nxpe, nype)

assert actual_lower_boundaries == lower_boundaries
assert actual_upper_boundaries == upper_boundaries

@pytest.mark.parametrize("filenum, nxpe, nype, lower_boundaries, upper_boundaries",
# 1d parallelization along y:
# Bottom
[(0, 1, 4, {'x': True, 'y': True},
{'x': True, 'y': False}),
# Lower Middle
(1, 1, 4, {'x': True, 'y': False},
{'x': True, 'y': True}),
# Upper Middle
(2, 1, 4, {'x': True, 'y': True},
{'x': True, 'y': False}),
# Top
(3, 1, 4, {'x': True, 'y': False},
{'x': True, 'y': True}),

# 2d parallelization:
# Bottom left corner
(0, 3, 4, {'x': True, 'y': True},
{'x': False, 'y': False}),
(1, 3, 4, {'x': False, 'y': True},
{'x': False, 'y': False}),
# Bottom right corner
(2, 3, 4, {'x': False, 'y': True},
{'x': True, 'y': False}),
(3, 3, 4, {'x': True, 'y': False},
{'x': False, 'y': True}),
(4, 3, 4, {'x': False, 'y': False},
{'x': False, 'y': True}),
(5, 3, 4, {'x': False, 'y': False},
{'x': True, 'y': True}),
(6, 3, 4, {'x': True, 'y': True},
{'x': False, 'y': False}),
(7, 3, 4, {'x': False, 'y': True},
{'x': False, 'y': False}),
(8, 3, 4, {'x': False, 'y': True},
{'x': True, 'y': False}),
# Top left corner
(9, 3, 4, {'x': True, 'y': False},
{'x': False, 'y': True}),
(10, 3, 4, {'x': False, 'y': False},
{'x': False, 'y': True}),
# Top right corner
(11, 3, 4, {'x': False, 'y': False},
{'x': True, 'y': True}),
])
def test_infer_boundaries_2d_parallelization_doublenull(
self, filenum, nxpe, nype, lower_boundaries, upper_boundaries):
"""
Numbering scheme for nxpe=3, nype=4

y 9 10 11
^ 6 7 8
| 3 4 5
| 0 1 2
-----> x
"""

ds = create_test_data(0)
ds['jyseps2_1'] = 3
ds['jyseps1_2'] = 11
ds['ny_inner'] = 8
ds['MYSUB'] = 4
ds.encoding['source'] = "folder0/BOUT.dmp." + str(filenum) + ".nc"
actual_lower_boundaries, actual_upper_boundaries = _infer_contains_boundaries(
ds, nxpe, nype)

assert actual_lower_boundaries == lower_boundaries
assert actual_upper_boundaries == upper_boundaries
Expand All @@ -443,10 +515,52 @@ def test_keep_xboundaries(self):
# Manually add filename - encoding normally added by xr.open_dataset
ds.encoding['source'] = 'folder0/BOUT.dmp.0.nc'

ds['jyseps2_1'] = 8
ds['jyseps1_2'] = 8

actual = _trim(ds, guards={'x': 2}, keep_boundaries={'x': True}, nxpe=1, nype=1)
expected = ds # Should be unchanged
xrt.assert_equal(expected, actual)

def test_keep_yboundaries(self):
ds = create_test_data(0)
ds = ds.rename({'dim2': 'y'})

# Manually add filename - encoding normally added by xr.open_dataset
ds.encoding['source'] = 'folder0/BOUT.dmp.0.nc'

ds['jyseps2_1'] = 8
ds['jyseps1_2'] = 8

actual = _trim(ds, guards={'y': 2}, keep_boundaries={'y': True}, nxpe=1, nype=1)
expected = ds # Should be unchanged
xrt.assert_equal(expected, actual)

@pytest.mark.parametrize("filenum, lower, upper",
[(0, True, False),
(1, False, True),
(2, True, False),
(3, False, True)])
def test_keep_yboundaries_doublenull(self, filenum, lower, upper):
ds = create_test_data(0)
ds = ds.rename({'dim2': 'y'})

# Manually add filename - encoding normally added by xr.open_dataset
ds.encoding['source'] = 'folder0/BOUT.dmp.'+str(filenum)+'.nc'

ds['jyseps2_1'] = 3
ds['jyseps1_2'] = 11
ds['ny_inner'] = 8
ds['MYSUB'] = 4

actual = _trim(ds, guards={'y': 2}, keep_boundaries={'y': True}, nxpe=1, nype=4)
expected = ds # Should be unchanged
if not lower:
expected = expected.isel(y=slice(2, None, None))
if not upper:
expected = expected.isel(y=slice(None, -2, None))
xrt.assert_equal(expected, actual)

def test_trim_timing_info(self):
ds = create_test_data(0)
from xbout.load import _BOUT_TIMING_VARIABLES
Expand Down