Skip to content
1 change: 1 addition & 0 deletions ci/environment-py3.12.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ dependencies:
- setuptools >=68
- wheel
- pip >=24
- libgfortran5
1 change: 1 addition & 0 deletions ci/environment-py3.13.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ dependencies:
- setuptools >=68
- wheel
- pip >=24
- libgfortran5
1 change: 1 addition & 0 deletions ci/environment-xarraymaster.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ dependencies:
- setuptools >=68
- wheel
- pip >=24
- libgfortran5
- pip:
- git+https://github.com/pydata/xarray.git
21 changes: 13 additions & 8 deletions doc/llcreader.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ be used right away. These are
- ``llcreader.ECCOPortalLLC4320Model``: LLC4320 accessed via ECCO data portal
- ``llcreader.PleiadesLLC2160Model``: LLC2160 accessed on Pleaides filesystem
- ``llcreader.PleiadesLLC4320Model``: LLC4320 accessed on Pleaides filesystem
- ``llcreader.CRIOSPortalASTE270Model``: ASTE Release 1 accessed via AWS
- ``llcreader.CRIOSAWSPortalASTE270Model``: ASTE Release 1 accessed via AWS
- ``llcreader.CRIOSTACCPortalASTE270Model``: ASTE Release 1 accessed via TACC Corral
- ``llcreader.SverdrupASTE270Model``: ASTE Release 1 accessed on Sverdrup filesystem at UT Austin

Below are a few examples of how to use these.
Expand Down Expand Up @@ -303,11 +304,11 @@ Because of the high-performance Lustre filesystem on Pleiades, data throughput
should be much higher than via the ECCO data portal.


ASTE Release 1 on AWS
~~~~~~~~~~~~~~~~~~~~~
ASTE Release 1 on TACC
~~~~~~~~~~~~~~~~~~~~~~

Monthly time mean output from the Arctic Subpolar gyre sTate Estimate (ASTE) Release 1
has been made available on AWS servers.
Monthly time mean and snapshot output from the Arctic Subpolar gyre sTate Estimate (ASTE) Release 1
have been made available on TACC's Corral.
ASTE is a medium-resolution data-constrained and dynamically consistent
ocean-sea ice synthesis, spanning 2002-2017.
Read more about this effort in [Nguyen et al, 2020].
Expand Down Expand Up @@ -335,7 +336,8 @@ and this changing coordinate has been taken into account during the time average

Example usage to get temperature and salinity:

>>> aste = llcreader.CRIOSPortalASTE270Model()

>>> aste = llcreader.CRIOSTACCPortalASTE270Model()
>>> ds = aste.get_dataset(varnames=['THETA','SALT'])
>>> ds
<xarray.Dataset>
Expand Down Expand Up @@ -405,7 +407,10 @@ All available diagnostics are shown here:
'SIsnPrcp', 'SItflux', 'SIuice', 'SIvice', 'SRELAX', 'TFLUX', 'THETA', 'TRELAX',
'UVELMASS', 'VVELMASS', 'WSLTMASS', 'WTHMASS', 'WVELMASS', 'oceFWflx',
'oceQnet', 'oceQsw', 'oceSPDep', 'oceSPflx', 'oceSPtnd', 'oceSflux', 'oceTAUX',
'oceTAUY', 'sIceLoad']
'oceTAUY', 'sIceLoad', 'THETADR_snap', 'SALTDR_snap', 'ETAN_snap',
'SIarea_snap', 'SIheff_snap', 'SIhsnow_snap', 'sIceLoad_snap', 'PHIBOT_snap']

The monthly time mean output is also accessible via AWS using ``CRIOSAWSPortalASTE270Model``.

Nguyen, A. T., H. Pillar, V. Ocana, A. Bigdeli, T. A. Smith, and P. Heimbach, 2020: The Arctic Subpolar gyre sTate Estimate (ASTE): Description and assessment of a data-constrained, dynamically consistent ocean-sea ice estimate for 2002-2017. J. Adv. Model. Earth Syst., submitted. https://doi.org/10.1002/essoar.10504669.3

Expand Down Expand Up @@ -534,4 +539,4 @@ be (``nx`` , ``nx``) = (270,270), but the data only cover
(270,180), shown in color.
Therefore, the data are padded with an array of size (270,90),
shown in white, and this is padded to the `i` dimension
since ``reshape`` = ``True`` for this facet.
since ``reshape`` = ``True`` for this facet.
41 changes: 35 additions & 6 deletions xmitgcm/llcreader/known_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,17 @@ def wrapper(*args,**kwargs):


def _make_http_filesystem():
import os
import fsspec
from fsspec.implementations.http import HTTPFileSystem
return HTTPFileSystem()

if os.getenv("CI", "false") == "true":
return fsspec.filesystem(
"filecache",
target_protocol="https",
cache_storage="/tmp/fsspec_cache",
)

return fsspec.filesystem("http")

class LLC90Model(BaseLLCModel):
nx = 90
Expand Down Expand Up @@ -59,6 +67,7 @@ class LLC2160Model(BaseLLCModel):
mask_override = {'oceTAUX': 'c', 'oceTAUY': 'c',
'SIuice': 'c', 'SIvice': 'c'}


class LLC4320Model(BaseLLCModel):
nx = 4320
nz = 90
Expand Down Expand Up @@ -145,7 +154,10 @@ class ASTE270Model(BaseLLCModel):
'TFLUX', 'THETA', 'TRELAX', 'UVELMASS', 'VVELMASS',
'WSLTMASS', 'WTHMASS', 'WVELMASS', 'oceFWflx', 'oceQnet',
'oceQsw', 'oceSPDep', 'oceSPflx', 'oceSPtnd', 'oceSflux',
'oceTAUX', 'oceTAUY', 'sIceLoad']
'oceTAUX', 'oceTAUY', 'sIceLoad',
'THETADR_snap', 'SALTDR_snap', 'ETAN_snap', 'SIarea_snap',
'SIheff_snap', 'SIhsnow_snap', 'sIceLoad_snap', 'PHIBOT_snap'
]

grid_varnames = ['AngleCS', 'AngleSN', 'DRC', 'DRF', 'DXC',
'DXG', 'DYC', 'DYG', 'Depth', 'PHrefC',
Expand Down Expand Up @@ -187,7 +199,10 @@ class ASTE270Model(BaseLLCModel):
"oceFWflx":">f8", "oceQnet":">f8", "oceQsw":">f8",
"oceSPDep":">f4", "oceSPflx":">f8", "oceSPtnd":">f8",
"oceSflux":">f8", "oceTAUX":">f4", "oceTAUY":">f4",
"sIceLoad":">f4"}
"sIceLoad":">f4", "THETADR_snap":">f8", "SALTDR_snap":">f8",
"ETAN_snap":">f8", "SIarea_snap":">f8", "SIheff_snap":">f8",
"SIhsnow_snap":">f8", "sIceLoad_snap":">f8", "PHIBOT_snap":">f8"
}

class ECCOPortalLLC2160Model(LLC2160Model):

Expand Down Expand Up @@ -244,7 +259,7 @@ def __init__(self):
shrunk=True,grid_path=grid_path)
super(PleiadesLLC4320Model, self).__init__(store)

class CRIOSPortalASTE270Model(ASTE270Model):
class CRIOSAWSPortalASTE270Model(ASTE270Model):

def __init__(self):
fs = _make_http_filesystem()
Expand All @@ -255,7 +270,21 @@ def __init__(self):
mask_path=mask_path,
shrunk=True, join_char='/')

super(CRIOSPortalASTE270Model, self).__init__(store)
super(CRIOSAWSPortalASTE270Model, self).__init__(store)

class CRIOSTACCPortalASTE270Model(ASTE270Model):

def __init__(self):
fs = _make_http_filesystem()
base_path = 'https://web.corral.tacc.utexas.edu/OceanProjects/ASTE/Release1/diags_binary_wet/'
grid_path = 'https://web.corral.tacc.utexas.edu/OceanProjects/ASTE/Release1/diags_binary_wet/grid/'
mask_path = 'https://web.corral.tacc.utexas.edu/OceanProjects/ASTE/Release1/diags_binary_wet/masks.zarr'

store = stores.NestedStore(fs, base_path=base_path, grid_path=grid_path,
mask_path=mask_path,
shrunk=True, join_char='/')

super(CRIOSTACCPortalASTE270Model, self).__init__(store)

class SverdrupASTE270Model(ASTE270Model):

Expand Down
4 changes: 2 additions & 2 deletions xmitgcm/llcreader/llcmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ def _get_facet_chunk(store, varname, iternum, nfacet, klevels, nx, nz, nfaces,
mykey = nx if domain == 'global' else f'{domain}_{nx}'
index = all_index_data[mykey][point]
zgroup = store.open_mask_group()
mask = zgroup['mask_' + point].astype('bool')
mask = zgroup['mask_' + point][:].astype('bool')
else:
index = None
mask = None
Expand Down Expand Up @@ -509,7 +509,7 @@ def _get_facet_chunk(store, varname, iternum, nfacet, klevels, nx, nz, nfaces,

assert len(data) == (end - start)

if mask:
if mask is not None:
mask_level = mask[k]
mask_facets = _faces_to_facets(mask_level,nfaces)
this_mask = mask_facets[nfacet]
Expand Down
30 changes: 25 additions & 5 deletions xmitgcm/test/test_llcreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,12 @@ def test_ecco_portal_latlon(llc_global_model):


########### ASTE Portal Tests ##################################################
#@pytest.fixture(scope='module', params=['portal','sverdrup'])
@pytest.fixture(scope='module', params=['sverdrup'])
@pytest.fixture(scope='module', params=['aws', 'tacc'])
def aste_model(request):
if request.param == 'portal':
return llcreader.CRIOSPortalASTE270Model()
if request.param == 'aws':
return llcreader.CRIOSAWSPortalASTE270Model()
elif request.param == 'tacc':
return llcreader.CRIOSTACCPortalASTE270Model()
else:
if not os.path.exists('/scratch2/heimbach'):
pytest.skip("Not on Sverdrup")
Expand All @@ -235,7 +236,6 @@ def test_aste_portal_faces(aste_model):
assert len(ds_faces[fld].data.chunks)==1
assert (len(ds_faces[fld]),)==ds_faces[fld].data.chunks[0]


def test_aste_portal_iterations(aste_model):
with pytest.warns(RuntimeWarning, match=r"Some requested iterations may not exist, you may need to change 'iters'"):
iters = aste_model.iters[:2]
Expand Down Expand Up @@ -268,4 +268,24 @@ def test_aste_portal_latlon(aste_model):
with pytest.raises(TypeError):
ds_ll = aste_model.get_dataset(iters=iters,type='latlon')

def test_aste_tacc_snapshots(aste_model):
if not isinstance(aste_model, llcreader.CRIOSTACCPortalASTE270Model):
pytest.skip("Only testing TACC Portal snapshot variables here")

iters = aste_model.iters[0:2]
varnames = ['THETADR_snap', 'ETAN_snap']
ds = aste_model.get_dataset(varnames=varnames, iters=iters)

for vn in varnames:
assert vn in ds.data_vars, f"{vn} not in dataset"

nx = aste_model.nx
assert ds.THETADR_snap.sizes == {'time': 2, 'k': 50, 'face': 6, 'j': 270, 'i': 270}
assert ds.ETAN_snap.sizes == {'time': 2, 'face': 6, 'j': 270, 'i': 270}

for vn in varnames:
val = ds[vn].isel(time=0).isel(i=0,j=0)
if 'k' in val.coords:
val = val.isel(k=0)
val = val.data.compute() if isinstance(val.data, dsa) else val.data
assert val is not None
35 changes: 34 additions & 1 deletion xmitgcm/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,40 @@
standard_name="ADJvice",
long_name='dJ/dvice: Sensitivity to meridional ice drift',
mate='ADJuice',
units='dJ/(m s-1)'))
units='dJ/(m s-1)')),
# snapshots
'THETADR_snap': dict(dims=['k', 'j', 'i'], attrs=dict(
standard_name="THETADR_snap",
long_name='snapshot depth-weighted potential temperature',
units='degC m')),
'SALTDR_snap': dict(dims=['k', 'j', 'i'], attrs=dict(
standard_name="SALTDR_snap",
long_name='snapshot depth-weighted salinity',
units='g kg-1 m')),
'ETAN_snap': dict(dims=['j', 'i'], attrs=dict(
standard_name="ETAN_snap",
long_name='snapshot sea surface height anomaly',
units='m')),
'SIarea_snap': dict(dims=['j', 'i'], attrs=dict(
standard_name="SIarea_snap",
long_name='snapshot sea ice fractional area',
units=' ')),
'SIheff_snap': dict(dims=['j', 'i'], attrs=dict(
standard_name="SIheff_snap",
long_name='snapshot effective sea ice thickness',
units='m')),
'SIhsnow_snap': dict(dims=['j', 'i'], attrs=dict(
standard_name="SIhsnow_snap",
long_name='snapshot snow thickness on sea ice',
units='m')),
'sIceLoad_snap': dict(dims=['j', 'i'], attrs=dict(
standard_name="SIceLoad_snap",
long_name='snapshot sea ice load (ice + snow)',
units='kg m-2')),
'PHIBOT_snap': dict(dims=['j', 'i'], attrs=dict(
standard_name="PHIBOT_snap",
long_name='snapshot bottom pressue',
units='m2 s-2')),
}

extra_grid_variables = OrderedDict(
Expand Down