Skip to content

Commit d7cf8ae

Browse files
authored
Merge pull request #300 from xylar/add-global-combine-topo-step
Add a step for combining global and Antarctic topography
2 parents 7f4c6f5 + 39bed53 commit d7cf8ae

File tree

27 files changed

+1437
-15
lines changed

27 files changed

+1437
-15
lines changed

.github/workflows/build_workflow.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ jobs:
106106
--verbose \
107107
--python=${{ matrix.python-version }}
108108
source load_polaris_test_mpich.sh
109+
python -c "import polaris; import polaris.version; print(polaris.version.__version__)"
109110
110111
- if: ${{ steps.skip_check.outputs.should_skip != 'true' }}
111112
name: Build Sphinx Docs

docs/Makefile

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ help:
3030

3131

3232
clean:
33-
rm -rf generated
33+
rm -rf $(BUILDDIR) developers_guide/generated/ developers_guide/*/generated/ developers_guide/*/*/generated/
3434
@$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
3535

3636
clean-versioned-html:
@@ -47,6 +47,3 @@ clean-versioned-html:
4747
# raise warnings to errors
4848
html-strict:
4949
@$(SPHINXBUILD) -b html -nW --keep-going "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS) $(O)
50-
51-
clean:
52-
rm -rf $(BUILDDIR) developers_guide/generated/ developers_guide/*/generated/

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
]
4848

4949
autosummary_generate = ['developers_guide/api.md',
50+
'developers_guide/e3sm/init/api.md',
5051
'developers_guide/mesh/api.md',
5152
'developers_guide/ocean/api.md',
5253
'developers_guide/seaice/api.md']
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# e3sm/init
2+
3+
```{eval-rst}
4+
.. currentmodule:: polaris.tasks.e3sm.init
5+
6+
.. autosummary::
7+
:toctree: generated/
8+
9+
add_tasks.add_e3sm_init_tasks
10+
```
11+
12+
## Tasks
13+
14+
### topo
15+
16+
```{eval-rst}
17+
.. currentmodule:: polaris.tasks.e3sm.init.topo.combine
18+
19+
.. autosummary::
20+
:toctree: generated/
21+
22+
CombineStep
23+
CombineStep.get_subdir
24+
CombineStep.setup
25+
CombineStep.constrain_resources
26+
CombineStep.run
27+
CombineTask
28+
VizCombinedStep
29+
VizCombinedStep.setup
30+
VizCombinedStep.run
31+
```
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
(dev-e3sm-init)=
2+
3+
# E3SM initial condition (e3sm/init) component
4+
5+
The `e3sm/init` component defines tasks and steps related to generating ocean
6+
and sea-ice initial conditions for E3SM. There are currently no
7+
component-level config options or shared framework.
8+
9+
```{toctree}
10+
:titlesonly: true
11+
12+
tasks/index
13+
```
132 KB
Loading
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(dev-e3sm-init-tasks)=
2+
3+
# Tasks
4+
5+
```{toctree}
6+
:titlesonly: true
7+
8+
topo
9+
```
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# Topography Tasks
2+
3+
The `polaris.tasks.e3sm.init.topo` module provides tools for working with
4+
topography data in Polaris. This includes steps for processing, modifying, and
5+
combining topography datasets to create inputs for E3SM components such as
6+
MPAS-Ocean. The framework is designed to handle both global and regional
7+
datasets, supporting various grid types like lat-lon and cubed-sphere grids.
8+
9+
## Combine Steps and Task
10+
11+
```{image} images/bathymetry_500.png
12+
:align: center
13+
:width: 500 px
14+
```
15+
16+
Global bathymetry datasets do not typically include the latest datasets around
17+
Antarctica needed for ice-sheet and ice-shelf modeling. For this reason, we
18+
typically combine a global topography dataset north of the Southern Ocean with
19+
one for Antarctica.
20+
21+
```{note}
22+
At the moment, the step for combining these datasets provides fields that are
23+
masked to locations where the bed topography (bathymetry) is below sea level.
24+
This avoids the risk of interpolated topography resulting in a bed that is
25+
above sea level in ocean regions when we perform further interpolation of
26+
the data to an MPAS mesh. However, a more general topogrpahy data set will
27+
likely be needed in the future that accommodates both the ocean and land/river
28+
components.
29+
```
30+
31+
The {py:class}`polaris.tasks.e3sm.init.topo.combine.CombineStep` step is a key
32+
component of the topography framework. It is responsible for combining global
33+
and Antarctic topography datasets into a single dataset suitable for use in
34+
E3SM simulations. The step supports blending datasets across specified latitude
35+
ranges and remapping them to a target grid.
36+
37+
The {py:class}`polaris.tasks.e3sm.init.topo.combine.CombineTask` wraps the
38+
`CombineStep` into a task that can be used to generate and cache combined
39+
topography datasets for reuse in other contexts.
40+
41+
The {py:class}`polaris.tasks.e3sm.init.topo.combine.VizCombinedStep` step is
42+
an optional visualization step that can be added to the workflow to create
43+
plots of the combined topography dataset. This step is particularly useful for
44+
debugging or analyzing the combined dataset.
45+
46+
### Key Features
47+
48+
- **Dataset Support**: Supports multiple datasets, including `bedmap3`,
49+
`bedmachinev3`, and `gebco2023`.
50+
- **Grid Types**: Handles both lat-lon and cubed-sphere target grids.
51+
- **Blending**: Blends global and Antarctic datasets across a configurable
52+
latitude range.
53+
- **Remapping**: Uses tools like `mbtempest`, `ESMF_RegridWeightGen` and
54+
`ncremap` for remapping datasets to the target grid.
55+
- **Output**: Produces combined topography datasets with consistent variables
56+
and attributes.
57+
- **Visualization**: Generates rasterized images of various fields (e.g.,
58+
bathymetry, ice draft) using the `datashader` library.
59+
60+
### Configuration Options
61+
62+
The `CombineStep` step is configured through the `[combine_topo]` section in
63+
the configuration file. Key options include:
64+
65+
- `resolution_latlon`: Target resolution for lat-lon grids (in degrees).
66+
- `resolution_cubedsphere`: Target resolution for cubed-sphere grids (e.g.,
67+
`3000` for NExxx grids).
68+
- `latmin` and `latmax`: Latitude range for blending datasets.
69+
- `ntasks` and `min_tasks`: Number of MPI tasks for remapping.
70+
- `method`: Remapping method (e.g., `bilinear`).
71+
72+
### Workflow
73+
74+
1. **Setup**: The step downloads required datasets and sets up input/output
75+
files.
76+
2. **Modification**: Antarctic and global datasets are modified to include
77+
necessary variables and attributes.
78+
3. **Remapping**: Datasets are remapped to the target grid using SCRIP files
79+
and weight generation.
80+
4. **Blending**: The datasets are blended across the specified latitude range.
81+
5. **Output**: The combined dataset is saved in NetCDF format.
82+
8. **Optional Field Plotting**: Each field in the dataset is rasterized and saved as an image with a colorbar.
83+
84+
### Example Usage
85+
86+
Below is an example of how the `CombineStep` can be added to a Polaris
87+
task:
88+
89+
```python
90+
from polaris.tasks.e3sm.init.topo.combine import CombineStep
91+
92+
93+
component = task.component
94+
subdir = CombineStep.get_subdir()
95+
if subdir in component.steps:
96+
step = component.steps[subdir]
97+
else:
98+
step = CombineStep(component=component)
99+
component.add_step(step)
100+
task.add_step(step)
101+
```
102+
103+
To create a `CombineTask` for caching combined datasets:
104+
105+
```python
106+
from polaris.tasks.e3sm.init.topo.combine import CombineTask
107+
108+
combine_task = CombineTask(component=my_component)
109+
my_component.add_task(combine_task)
110+
```
111+
112+
Below is an example of how the `VizCombinedStep` can be added to a Polaris task:
113+
114+
```python
115+
from polaris.tasks.e3sm.init.topo.combine import VizCombinedStep
116+
117+
viz_step = VizCombinedStep(component=my_component, combine_step=combine_step)
118+
my_component.add_step(viz_step)
119+
```
120+
121+
Since there is a single shared step for each pair of Antarctic and global
122+
datasets, the step should be added only once to the component and the existing
123+
step (identifiable via its `subdir`) should be used subsequently.
124+
125+
The `VizCombinedStep` is typically added only when visualization is explicitly required, as it is not part of the default workflow.
126+
127+
For more details, refer to the source code of the
128+
{py:class}`polaris.tasks.e3sm.init.topo.combine.CombineStep` and
129+
{py:class}`polaris.tasks.e3sm.init.topo.combine.CombineTask` classes.
130+
131+
```{note}
132+
Since this step is expensive and time-consuming to run, most tasks will
133+
want to use cached outputs from this step rather than running it in full.
134+
```

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ developers_guide/quick_start
7070
developers_guide/overview
7171
developers_guide/command_line
7272
developers_guide/organization/index
73+
developers_guide/e3sm/init/index
7374
developers_guide/mesh/index
7475
developers_guide/ocean/index
7576
developers_guide/seaice/index

polaris/cache.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,16 @@ def update_cache(step_paths, date_string=None, dry_run=False):
5959
else:
6060
steps[component] = [step]
6161

62+
out_filename = f'{component.replace("/", "_")}_cached_files.json'
63+
6264
# now, iterate over cores and steps
6365
for component in steps:
6466
database_root = config.get('paths', 'database_root')
6567
cache_root = f'{database_root}/{component}/polaris_cache'
6668

67-
package = f'polaris.{component}'
69+
package = f'polaris.{component.replace("/", ".")}'
6870
try:
69-
with open(f'{component}_cached_files.json') as data_file:
71+
with open(out_filename) as data_file:
7072
cached_files = json.load(data_file)
7173
except FileNotFoundError:
7274
# we don't have a local version of the file yet, let's see if
@@ -86,14 +88,14 @@ def update_cache(step_paths, date_string=None, dry_run=False):
8688

8789
for output in step.outputs:
8890
output = os.path.basename(output)
89-
out_filename = os.path.join(step_path, output)
91+
dest_filename = os.path.join(step_path, output)
9092
# remove the component from the file path
91-
target = out_filename[len(component) + 1 :]
93+
target = dest_filename[len(component) + 1 :]
9294
path, ext = os.path.splitext(target)
9395
target = f'{path}.{date_string}{ext}'
94-
cached_files[out_filename] = target
96+
cached_files[dest_filename] = target
9597

96-
print(out_filename)
98+
print(dest_filename)
9799
print(f' ==> {target}')
98100
output_path = f'{cache_root}/{target}'
99101
print(f' copy to: {output_path}')
@@ -104,9 +106,8 @@ def update_cache(step_paths, date_string=None, dry_run=False):
104106
os.makedirs(directory)
105107
except FileExistsError:
106108
pass
107-
shutil.copyfile(out_filename, output_path)
109+
shutil.copyfile(dest_filename, output_path)
108110

109-
out_filename = f'{component}_cached_files.json'
110111
with open(out_filename, 'w') as data_file:
111112
json.dump(cached_files, data_file, indent=4)
112113

0 commit comments

Comments
 (0)