Skip to content

Commit

Permalink
Merge pull request #251 from xylar/cosine-bell-restart
Browse files Browse the repository at this point in the history
Add a restart test to cosine bell
  • Loading branch information
xylar authored Dec 23, 2024
2 parents d6bc3a1 + 2b6571d commit 018ae1a
Show file tree
Hide file tree
Showing 18 changed files with 463 additions and 87 deletions.
30 changes: 20 additions & 10 deletions docs/developers_guide/framework/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ be compared against a baseline, if one is provided, after the step as run.

In addition to baseline validation, it is often useful to compare files between
steps of a run. This is done by adding later step to perform the validation.
This validation step will use the function
{py:func}`polaris.validate.compare_variables()` to compare variables in a file
with a given relative path (`filename1`) with the same variables in another
This validation step will use the function
{py:func}`polaris.validate.compare_variables()` to compare variables in a file
with a given relative path (`filename1`) with the same variables in another
file (`filename2`).

As a compact example of creating a validate step for a restart run:
Expand All @@ -58,7 +58,7 @@ class Validate(Step):
super().run()
variables = ['temperature', 'salinity', 'layerThickness',
'normalVelocity']
all_pass = compare_variables(variables,
all_pass = compare_variables(variables,
filename1='output_full_run.nc',
filename2='output_restart_run.nc',
logger=self.logger)
Expand All @@ -68,12 +68,12 @@ class Validate(Step):

```

The 2 files `../full_run/output.nc` and `../restart_run/output.nc` are
symlinked locally and compared to make sure the variables `temperature`,
The 2 files `../full_run/output.nc` and `../restart_run/output.nc` are
symlinked locally and compared to make sure the variables `temperature`,
`salinity`, `layerThickness`, and `normalVelocity` are identical between the
two.

By default, the output is "quiet". If you set `quiet=False`, typical output
By default, the output is "quiet". If you set `quiet=False`, typical output
will look like this:

```none
Expand Down Expand Up @@ -186,7 +186,7 @@ normalVelocity Time index: 0, 1, 2

## Norms

In circumstance where you would like to allow comparison to pass with non-zero
In circumstance where you would like to allow comparison to pass with non-zero
differences between variables, you can supply keyword arguments
`l1_norm`, `l2_norm` and/or `linf_norm` to give the desired maximum
values for these norms, above which the comparison will fail, raising a
Expand All @@ -196,8 +196,18 @@ values for these norms, above which the comparison will fail, raising a
If you want different nonzero norm values for different variables,
the easiest solution is to call {py:func}`polaris.validate.compare_variables()`
separately for each variable and with different norm values specified.
You will need to "and" together the results from calling
{py:func}`polaris.validate.compare_variables()`. When you specify a nonzero
You will need to "and" together the results from calling
{py:func}`polaris.validate.compare_variables()`. When you specify a nonzero
norm, you may want polaris to print the norm values it is using for comparison
when the results are printed. To do so, use the optional `quiet=False`
argument.

## Datasets

In some cases, a comparison cannot be made directly between the datasets loaded
from the two files to be compared. Instead, the datasets require manipulation
for some reason. Currently, this is the case for datasets from the Omega model,
which need to have their variables renamed to the MPAS-Ocean names for use in
Polaris. The `ds1` and `ds2` keyword arguments are used to supply datasets
corresponding to `filename1` and `filename2`, respectively, in such
circumstances.
11 changes: 9 additions & 2 deletions docs/developers_guide/ocean/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,21 @@
init.cosine_bell
forward.Forward
forward.Forward.compute_cell_count
forward.Forward.dynamic_model_config
analysis.Analysis
analysis.Analysis.exact_solution
validate.Validate
validate.Validate.run
viz.Viz
viz.Viz.run
restart.Restart
restart.RestartStep
restart.RestartStep.dynamic_model_config
```

### geostrophic
Expand Down
30 changes: 26 additions & 4 deletions docs/developers_guide/ocean/tasks/cosine_bell.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

# cosine_bell

The {py:class}`polaris.ocean.tasks.cosine_bell.CosineBell`
In most cases, the {py:class}`polaris.ocean.tasks.cosine_bell.CosineBell`
test performs a series of 24-day runs that advect a bell-shaped tracer blob
around the sphere. The resolution of the sphere varies (by default, between
60 and 240 km). Advected results are compared with a known exact solution to
determine the rate of convergence.

There is also a restart test, described below, that performs only two time
steps of the test to verify the exact restart capability.

## framework

The config options for the `cosine_bell` tests are described in
The config options for the `cosine_bell` tests are described in
{ref}`ocean-cosine-bell` in the User's Guide.

Additionally, the test uses a `forward.yaml` file with a few common
Expand Down Expand Up @@ -38,18 +41,24 @@ descends from {py:class}`polaris.ocean.convergence.spherical.SphericalConvergenc
and defines a step for running MPAS-Ocean from an initial condition produced in
an `init` step. See {ref}`dev-ocean-convergence` for some relevant
discussion of the parent class. The time step is determined from the resolution
based on the `dt_per_km` config option in the `[convergence_forward]`
based on the `dt_per_km` config option in the `[convergence_forward]`
section. Other model config options are taken from `forward.yaml`.

### analysis

The class {py:class}`polaris.ocean.tasks.cosine_bell.analysis.Analysis`
descends from
{py:class}`polaris.ocean.convergence.ConvergenceAnalysis`,
{py:class}`polaris.ocean.convergence.analysis.ConvergenceAnalysis`,
and defines a step for computing the error norm (L2) for the results
at each resolution, saving them in `convergence_tracer1.csv` and plotting them
in `convergence_tracer1.png`.

### validate

The class {py:class}`polaris.ocean.tasks.cosine_bell.validate.Validate` is a
step for validating the results between two cosine-bell runs. It is currently
used to verify bit-for-bit restart in the `restart` test described below.

### viz

The visualization step is available only in the `cosine_bell/with_viz`
Expand All @@ -76,3 +85,16 @@ colorbar_limits = 0.0, 1.0
```

See {ref}`dev-visualization-global` for more details.

## restart

The {py:class}`polaris.ocean.tasks.cosine_bell.restart.Restart` class defines
a restart check that performs two time steps of the Cosine Bell test at coarse
resolution, then performs reruns the second time step, as a restart run to
verify the bit-for-bit restart capability for tracer advection.

### restart_step

The {py:class}`polaris.ocean.tasks.cosine_bell.restart.RestartStep` class
defines both steps of the restart run, the "full" run (2 time steps) and the
"restart" run (repeating the last time step after a restart).
57 changes: 33 additions & 24 deletions docs/users_guide/ocean/tasks/cosine_bell.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,61 @@

## description

The `cosine_bell` and `cosine_bell/with_viz` tasks implement the Cosine
Bell test case as first described in
The `cosine_bell/convergence_*` and `cosine_bell/convergence_*/with_viz` tasks
implement the Cosine Bell test case as first described in
[Williamson et al. 1992](<https://doi.org/10.1016/S0021-9991(05)80016-6>)
but using the variant from Sec. 3a of
[Skamarock and Gassmann](https://doi.org/10.1175/MWR-D-10-05056.1). A flow
field representing solid-body rotation transports a bell-shaped perturbation
in a tracer $\psi$ once around the sphere, returning to its initial location.

The task is a convergence test with time step varying proportionately to grid
size. The result of the `analysis` step of the task is a plot like the
following showing convergence as a function of the number of cells:
The `convergence_both` task is a convergence test with time step varying
proportionately to cell size, while `convergence_time` and `convergence_space`
vary only the time step and the cell size, respectively. The result of the
`analysis` step of each task is a plot like the following showing convergence
as a function of the cell size and/or the time step:

```{image} images/cosine_bell_convergence.png
:align: center
:width: 500 px
```

The `cosine_bell/with_viz` variant also includes visualization of the initial
The `with_viz` variant also includes visualization of the initial
and final state on a lat-lon grid for each resolution. The visualization is
not included in the `cosine_bell` version of the task in order to not slow down
not included in the other versions of the task in order to not slow down
regression testing.

Another task, `cosine_bell/restart`, performs two time steps of the Cosine Bell
test at coarse resolution, then performs reruns the second time step,
as a restart run to verify the bit-for-bit restart capability for tracer
advection.

## suppported models

These tasks support only MPAS-Ocean.
These tasks support both MPAS-Ocean and Omega.

(ocean-cosine-bell-mesh)=
## mesh

Two global mesh variants are tested, quasi-uniform (QU) and icosohydral. There
are also variants to test convergence in space, time, or both space and time.
In addition, the tests can be set up with or without the viz step. Thus, there
are 12 variants of the task:
are also variants to test convergence in space, time, or both space and time
as well as the restart test. In addition, the tests can be set up with or
without the viz step. Thus, there are 14 variants of the task:
```
ocean/spherical/icos/cosine_bell/convergence_both/
ocean/spherical/icos/cosine_bell/convergence_both/with_viz
ocean/spherical/qu/cosine_bell/convergence_both/
ocean/spherical/qu/cosine_bell/convergence_both/with_viz
ocean/spherical/icos/cosine_bell/convergence_space/
ocean/spherical/icos/cosine_bell/convergence_space
ocean/spherical/icos/cosine_bell/convergence_space/with_viz
ocean/spherical/qu/cosine_bell/convergence_space/
ocean/spherical/qu/cosine_bell/convergence_space/with_viz
ocean/spherical/icos/cosine_bell/convergence_time/
ocean/spherical/icos/cosine_bell/convergence_time
ocean/spherical/icos/cosine_bell/convergence_time/with_viz
ocean/spherical/qu/cosine_bell/convergence_time/
ocean/spherical/icos/cosine_bell/convergence_both
ocean/spherical/icos/cosine_bell/convergence_both/with_viz
ocean/spherical/icos/cosine_bell/restart
ocean/spherical/qu/cosine_bell/convergence_space
ocean/spherical/qu/cosine_bell/convergence_space/with_viz
ocean/spherical/qu/cosine_bell/convergence_time
ocean/spherical/qu/cosine_bell/convergence_time/with_viz
ocean/spherical/qu/cosine_bell/convergence_both
ocean/spherical/qu/cosine_bell/convergence_both/with_viz
ocean/spherical/qu/cosine_bell/restart
```
The default resolutions used in the task depends on the mesh type.

Expand Down Expand Up @@ -85,10 +94,10 @@ qu_base_resolution = 120.
qu_refinement_factors = 0.5, 0.75, 1., 1.25, 1.5, 1.75, 2.
```

To alter the resolutions used in this task, you will need to create your own
config file (or add a `spherical_convergence` section to a config file if
you're already using one). The resolutions are a comma-separated list of the
resolution of the mesh in km. If you specify a different list
To alter the resolutions used in the convergence tasks, you will need to create
your own config file (or add a `spherical_convergence` section to a config file
if you're already using one). The resolutions are a comma-separated list of
the resolution of the mesh in km. If you specify a different list
before setting up `cosine_bell`, steps will be generated with the requested
resolutions. (If you alter `icos_resolutions` or `qu_resolutions`) in the
task's config file in the work directory, nothing will happen.) For `icos`
Expand Down
9 changes: 4 additions & 5 deletions polaris/ocean/convergence/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import numpy as np


def get_resolution_for_task(config, refinement_factor,
refinement='both'):
"""
Expand Down Expand Up @@ -32,7 +29,8 @@ def get_resolution_for_task(config, refinement_factor,

if refinement_factor not in refinement_factors:
raise ValueError(
'refinement_factor not found in config option refinement_factors')
f'refinement_factor {refinement_factor} not found in config '
f'option {option}:\n {refinement_factors}')

if refinement == 'time':
resolution = base_resolution
Expand Down Expand Up @@ -74,7 +72,8 @@ def get_timestep_for_task(config, refinement_factor,

if refinement_factor not in refinement_factors:
raise ValueError(
'refinement_factor not found in config option refinement_factors')
f'refinement_factor {refinement_factor} not found in config '
f'option {option}:\n {refinement_factors}')

resolution = get_resolution_for_task(
config, refinement_factor, refinement=refinement)
Expand Down
16 changes: 14 additions & 2 deletions polaris/ocean/suites/cosine_bell.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
ocean/spherical/icos/cosine_bell
ocean/spherical/qu/cosine_bell
ocean/spherical/icos/cosine_bell/convergence_space
ocean/spherical/icos/cosine_bell/convergence_space/with_viz
ocean/spherical/icos/cosine_bell/convergence_time
ocean/spherical/icos/cosine_bell/convergence_time/with_viz
ocean/spherical/icos/cosine_bell/convergence_both
ocean/spherical/icos/cosine_bell/convergence_both/with_viz
ocean/spherical/icos/cosine_bell/restart
ocean/spherical/qu/cosine_bell/convergence_space
ocean/spherical/qu/cosine_bell/convergence_space/with_viz
ocean/spherical/qu/cosine_bell/convergence_time
ocean/spherical/qu/cosine_bell/convergence_time/with_viz
ocean/spherical/qu/cosine_bell/convergence_both
ocean/spherical/qu/cosine_bell/convergence_both/with_viz
ocean/spherical/qu/cosine_bell/restart
8 changes: 0 additions & 8 deletions polaris/ocean/suites/cosine_bell_cached_init.txt

This file was deleted.

1 change: 1 addition & 0 deletions polaris/ocean/suites/nightly.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ ocean/planar/ice_shelf_2d/5km/z-star/default/with_restart
ocean/planar/ice_shelf_2d/5km/z-level/default/with_restart
ocean/planar/inertial_gravity_wave/convergence_both
# ocean/planar/manufactured_solution
ocean/spherical/icos/cosine_bell/restart
1 change: 1 addition & 0 deletions polaris/ocean/suites/pr.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ ocean/planar/internal_wave/vlr/default
# ocean/planar/manufactured_solution
ocean/single_column/cvmix
ocean/single_column/ideal_age
ocean/spherical/icos/cosine_bell/restart
38 changes: 27 additions & 11 deletions polaris/ocean/tasks/cosine_bell/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from polaris.ocean.tasks.cosine_bell.analysis import Analysis
from polaris.ocean.tasks.cosine_bell.forward import Forward
from polaris.ocean.tasks.cosine_bell.init import Init
from polaris.ocean.tasks.cosine_bell.restart import Restart
from polaris.ocean.tasks.cosine_bell.viz import Viz


Expand All @@ -22,7 +23,8 @@ def add_cosine_bell_tasks(component):
the ocean component that the tasks will be added to
"""

for icosahedral, prefix in [(True, 'icos'), (False, 'qu')]:
for icosahedral, prefix, restart_refinement in [(True, 'icos', 8.0),
(False, 'qu', 2.0)]:

filepath = f'spherical/{prefix}/cosine_bell/cosine_bell.cfg'
config = PolarisConfigParser(filepath=filepath)
Expand All @@ -32,6 +34,7 @@ def add_cosine_bell_tasks(component):
'spherical.cfg')
config.add_from_package('polaris.ocean.tasks.cosine_bell',
'cosine_bell.cfg')
_set_convergence_configs(config, prefix)

for refinement in ['space', 'time', 'both']:
for include_viz in [False, True]:
Expand All @@ -41,6 +44,12 @@ def add_cosine_bell_tasks(component):
include_viz=include_viz,
refinement=refinement))

component.add_task(Restart(component=component,
config=config,
icosahedral=icosahedral,
refinement_factor=restart_refinement,
refinement='both'))


class CosineBell(Task):
"""
Expand Down Expand Up @@ -133,17 +142,8 @@ def _setup_steps(self, refinement):
option = 'refinement_factors_time'
else:
option = 'refinement_factors_space'
refinement_factors = config.getlist('spherical_convergence',
f'{prefix}_{option}', dtype=str)
refinement_factors = ', '.join(refinement_factors)
config.set('convergence', option, value=refinement_factors)
refinement_factors = config.getlist('convergence',
option, dtype=float)

base_resolution = config.getfloat('spherical_convergence',
f'{prefix}_base_resolution')
config.set('convergence', 'base_resolution',
value=f'{base_resolution:03g}')
refinement_factors = config.getlist('convergence', option, dtype=float)

# start fresh with no steps
for step in list(self.steps.values()):
Expand Down Expand Up @@ -233,3 +233,19 @@ def _setup_steps(self, refinement):
self.add_step(step, symlink=symlink)
else:
self.add_step(step)


def _set_convergence_configs(config, prefix):
for refinement in ['space', 'time']:
option = f'refinement_factors_{refinement}'
refinement_factors = config.getlist('spherical_convergence',
f'{prefix}_{option}', dtype=str)
refinement_factors = ', '.join(refinement_factors)
config.set('convergence', option, value=refinement_factors)
refinement_factors = config.getlist('convergence',
option, dtype=float)

base_resolution = config.getfloat('spherical_convergence',
f'{prefix}_base_resolution')
config.set('convergence', 'base_resolution',
value=f'{base_resolution:03g}')
Loading

0 comments on commit 018ae1a

Please sign in to comment.