Skip to content

Commit

Permalink
Merge pull request #131 from cbegeman/ocn-enhance-convergence-analysis
Browse files Browse the repository at this point in the history
Enhance shared convergence steps:

This PR makes it possible for all convergence tests to leverage shared convergence steps and implements those shared convergence steps in all convergence tests.
  • Loading branch information
cbegeman authored Oct 10, 2023
2 parents 2ddd30f + 7664324 commit df86670
Show file tree
Hide file tree
Showing 37 changed files with 601 additions and 602 deletions.
35 changes: 24 additions & 11 deletions docs/developers_guide/ocean/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,14 @@
InertialGravityWave
analysis.Analysis
analysis.Analysis.run
analysis.Analysis.exact_solution
exact_solution.ExactSolution
exact_solution.ExactSolution.ssh
exact_solution.ExactSolution.normal_velocity
forward.Forward
forward.Forward.compute_cell_count
forward.Forward.dynamic_model_config
init.Init
init.Init.run
Expand Down Expand Up @@ -127,15 +126,14 @@
ManufacturedSolution
analysis.Analysis
analysis.Analysis.run
analysis.Analysis.exact_solution
exact_solution.ExactSolution
exact_solution.ExactSolution.ssh
exact_solution.ExactSolution.normal_velocity
forward.Forward
forward.Forward.compute_cell_count
forward.Forward.dynamic_model_config
init.Init
init.Init.run
Expand Down Expand Up @@ -169,6 +167,27 @@

## Ocean Framework

### Convergence Tests

```{eval-rst}
.. currentmodule:: polaris.ocean.convergence
.. autosummary::
:toctree: generated/
ConvergenceForward
ConvergenceForward.compute_cell_count
ConvergenceForward.dynamic_model_config
ConvergenceAnalysis
ConvergenceAnalysis.compute_error
ConvergenceAnalysis.convergence_parameters
ConvergenceAnalysis.exact_solution
ConvergenceAnalysis.get_output_field
ConvergenceAnalysis.plot_convergence
ConvergenceAnalysis.run
ConvergenceAnalysis.setup
```
### Spherical Convergence Tests

```{eval-rst}
Expand All @@ -178,13 +197,7 @@
:toctree: generated/
SphericalConvergenceForward
SphericalConvergenceAnalysis
SphericalConvergenceAnalysis.compute_error
SphericalConvergenceAnalysis.convergence_parameters
SphericalConvergenceAnalysis.exact_solution
SphericalConvergenceAnalysis.get_output_field
SphericalConvergenceAnalysis.run
SphericalConvergenceAnalysis.plot_convergence
SphericalConvergenceForward.compute_cell_count
```

### Ocean Model
Expand Down
79 changes: 41 additions & 38 deletions docs/developers_guide/ocean/framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ The function {py:func}`polaris.ocean.mesh.spherical.add_spherical_base_mesh_step
returns a step for for a spherical `qu` or `icos` mesh of a given resolution
(in km). The step can be shared between tasks.

(dev-ocean-spherical-convergence)=
(dev-ocean-convergence)=

## Spherical Convergence Tests
## Convergence Tests

Several tests that are in Polaris or which we plan to add are convergence
tests on {ref}`dev-ocean-spherical-meshes`. The ocean framework includes
shared config options and a base class for forward steps that are expected
to be useful across these tests.
tests on {ref}`dev-ocean-spherical-meshes` and planar meshes.
The ocean framework includes shared config options and base classes for
forward and analysis steps that are expected to be useful across these tests.

The shared config options are:
```cfg
Expand All @@ -156,18 +156,19 @@ icos_resolutions = 60, 120, 240, 480
# a list of quasi-uniform mesh resolutions (km) to test
qu_resolutions = 60, 90, 120, 150, 180, 210, 240
# Evaluation time for convergence analysis (in days)
convergence_eval_time = 1.0
[convergence]
# Evaluation time for convergence analysis (in hours)
convergence_eval_time = 24.0
# Convergence threshold below which a test fails
convergence_thresh = 1.0
# Type of error to compute
error_type = l2
# config options for spherical convergence forward steps
[spherical_convergence_forward]
# config options for convergence forward steps
[convergence_forward]
# time integrator: {'split_explicit', 'RK4'}
time_integrator = RK4
Expand All @@ -182,17 +183,19 @@ split_dt_per_km = 30.0
# since btr_dt is proportional to resolution
btr_dt_per_km = 1.5
# Run duration in days
run_duration = ${spherical_convergence:convergence_eval_time}
# Run duration in hours
run_duration = ${convergence:convergence_eval_time}
# Output interval in days
# Output interval in hours
output_interval = ${run_duration}
```
The first 2 are the default resolutions for icosahedral and quasi-uniform
base meshes, respectively. The `convergence_eval_time` will generally be
modified by each test case. The `convergence_thresh` will also be modified by
each test case, and will depend on the numerical methods being tested. The
`error_type` is the L2 norm by default.
base meshes, respectively.

The `convergence_eval_time` will generally be modified by each test case. The
`convergence_thresh` will also be modified by each test case, and will depend
on the numerical methods being tested. The `error_type` is the L2 norm by
default. The L-infty norm, `inf`, is also supported.

`time_integrator` will typically be overridden by the specific convergence
task's config options, and indicates which time integrator to use for the
Expand All @@ -201,7 +204,7 @@ forward run. Depending on the time integrator, either `rk4_dt_per_km` or
mesh resolution (proportional to the cell size). For split time integrators,
`btr_dt_per_km` will be used to compute the barotropic time step in a similar
way. The `run_duration` and `output_interval` are typically the same, and
they are given in days.
they are given in hours.

Each convergence test can override these defaults with its own defaults by
defining them in its own config file. Convergence tests should bring in this
Expand All @@ -218,6 +221,8 @@ def add_cosine_bell_tasks(component):

filepath = f'spherical/{prefix}/cosine_bell/cosine_bell.cfg'
config = PolarisConfigParser(filepath=filepath)
config.add_from_package('polaris.ocean.convergence',
'convergence.cfg')
config.add_from_package('polaris.ocean.convergence.spherical',
'spherical.cfg')
config.add_from_package('polaris.ocean.tasks.cosine_bell',
Expand All @@ -228,9 +233,13 @@ In addition, the {py:class}`polaris.ocean.convergence.spherical.SphericalConverg
step can serve as a parent class for forward steps in convergence tests. This
parent class takes care of setting the time step based on the `dt_per_km`
config option and computes the approximate number of cells in the mesh, used
for determining the computational resources required, using a heuristic
appropriate for approximately uniform spherical meshes. A convergence test's
`Forward` step should descend from this class like in this example:
for determining the computational resources required. When convergence tests
are run on spherical meshes,
the {py:class}`polaris.ocean.convergence.spherical.SphericalConvergenceForward`
should be invoked and overrides the `compute_cell_count` method with a
heuristic appropriate for approximately uniform spherical meshes. A
convergence test's `Forward` step should descend from this class like in this
example:

```python
from polaris.ocean.convergence.spherical import SphericalConvergenceForward
Expand All @@ -242,7 +251,7 @@ class Forward(SphericalConvergenceForward):
bell test case
"""

def __init__(self, component, name, subdir, resolution, base_mesh, init):
def __init__(self, component, name, subdir, resolution, mesh, init):
"""
Create a new step
Expand All @@ -260,7 +269,7 @@ class Forward(SphericalConvergenceForward):
resolution : float
The resolution of the (uniform) mesh in km
base_mesh : polaris.Step
mesh : polaris.Step
The base mesh step
init : polaris.Step
Expand All @@ -269,7 +278,7 @@ class Forward(SphericalConvergenceForward):
package = 'polaris.ocean.tasks.cosine_bell'
validate_vars = ['normalVelocity', 'tracer1']
super().__init__(component=component, name=name, subdir=subdir,
resolution=resolution, base_mesh=base_mesh,
resolution=resolution, mesh=mesh,
init=init, package=package,
yaml_filename='forward.yaml',
output_filename='output.nc',
Expand All @@ -286,9 +295,9 @@ analyze. The `validate_vars` are a list of variables to compare against a
baseline (if one is provided), and can be `None` if baseline validation should
not be performed.

The `base_mesh` step should be created with the function described in
The `mesh` step should be created with the function described in
{ref}`dev-ocean-spherical-meshes`, and the `init` step should produce a file
`initial_state.nc` that will be the initial condition for the forward run.
`init.nc` that will be the initial condition for the forward run.

The `forward.yaml` file should be a YAML file with Jinja templating for the
time integrator, time step, run duration and output interval, e.g.:
Expand Down Expand Up @@ -318,11 +327,11 @@ omega:
- normalVelocity
- layerThickness
```
`SphericalConvergenceForward` takes care of filling in the template based
`ConvergenceForward` takes care of filling in the template based
on the associated config options (first at setup and again at runtime in case
the config options have changed).

In addition, the {py:class}`polaris.ocean.convergence.spherical.SphericalConvergenceAnalysis`
In addition, the {py:class}`polaris.ocean.convergence.ConvergenceAnalysis`
step can serve as a parent class for analysis steps in convergence tests. This
parent class computes the error norm for the output from each resolution's
forward step. It also produces the convergence plot.
Expand All @@ -331,12 +340,11 @@ This is an example of how a task's analysis step can descend from the parent
class:

```python
class Analysis(SphericalConvergenceAnalysis):
class Analysis(ConvergenceAnalysis):
"""
A step for analyzing the output from the cosine bell test case
"""
def __init__(self, component, resolutions, icosahedral, subdir,
dependencies):
def __init__(self, component, resolutions, subdir, dependencies):
"""
Create the step
Expand All @@ -348,10 +356,6 @@ class Analysis(SphericalConvergenceAnalysis):
resolutions : list of float
The resolutions of the meshes that have been run
icosahedral : bool
Whether to use icosahedral, as opposed to less regular, JIGSAW
meshes
subdir : str
The subdirectory that the step resides in
Expand All @@ -363,18 +367,17 @@ class Analysis(SphericalConvergenceAnalysis):
'zidx': 0}]
super().__init__(component=component, subdir=subdir,
resolutions=resolutions,
icosahedral=icosahedral,
dependencies=dependencies,
convergence_vars=convergence_vars)
```

Many tasks will also need to override the
{py:meth}`polaris.ocean.convergence.spherical.SphericalConvergenceAnalysis.exact_solution()`
{py:meth}`polaris.ocean.convergence.ConvergenceAnalysis.exact_solution()`
method. If not overridden, the analysis step will compute the difference of the
output from the initial state.

In some cases, the child class will also need to override the
{py:meth}`polaris.ocean.convergence.spherical.SphericalConvergenceAnalysis.get_output_field()`
{py:meth}`polaris.ocean.convergence.ConvergenceAnalysis.get_output_field()`
method if the requested field is not available directly from the output put
rather needs to be computed. The default behavior is to read the requested
variable (the value associate the `'name'` key) at the time index closest to
Expand Down
8 changes: 4 additions & 4 deletions docs/developers_guide/ocean/tasks/cosine_bell.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ model config options related to drag and default horizontal and
vertical momentum and tracer diffusion, as well as defining `mesh`, `input`,
`restart`, and `output` streams. This file has Jinja templating that is
used to update model config options based on Polaris config options, see
{ref}`dev-ocean-spherical-convergence`.
{ref}`dev-ocean-convergence`.

### base_mesh

Expand All @@ -36,16 +36,16 @@ tracer distributed in a cosine-bell shape.
The class {py:class}`polaris.ocean.tasks.cosine_bell.forward.Forward`
descends from {py:class}`polaris.ocean.convergence.spherical.SphericalConvergenceForward`,
and defines a step for running MPAS-Ocean from an initial condition produced in
an `init` step. See {ref}`dev-ocean-spherical-convergence` for some relevant
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 `[spherical_convergences]`
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.spherical.SphericalConvergenceAnalysis`,
{py:class}`polaris.ocean.convergence.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`.
Expand Down
19 changes: 10 additions & 9 deletions docs/developers_guide/ocean/tasks/inertial_gravity_wave.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ the exact solution. The tracer and coriolis fields are uniform in space.
### forward

The class {py:class}`polaris.ocean.tasks.inertial_gravity_wave.forward.Forward`
defines a step for running MPAS-Ocean from the initial condition produced in the
`init` step. Namelist and streams files are updated in
{py:meth}`polaris.ocean.tasks.inertial_gravity_wave.forward.Forward.dynamic_model_config()`
with time steps determined algorithmically based on config options. The number
descends from
{py:class}`polaris.ocean.convergence.ConvergenceForward`
and runs MPAS-Ocean from the initial condition produced in the `init` step.
Namelist and streams files are updated by the parent class with time steps
determined algorithmically based on config options. The number
of cells is approximated from config options in
{py:meth}`polaris.ocean.tasks.inertial_gravity_wave.forward.Forward.compute_cell_count()`
so that this can be used to constrain the number of MPI tasks that Polaris
Expand All @@ -65,14 +66,14 @@ performed against a baseline if one is provided when calling
### analysis

The class
{py:class}`polaris.ocean.tasks.inertial_gravity_wave.analysis.Analysis` defines
a step for computing the root mean-square-error from the final simulated field
{py:class}`polaris.ocean.tasks.inertial_gravity_wave.analysis.Analysis`
descends from {py:class}`polaris.ocean.convergence.ConvergenceAnalysis`
a step for computing the error from the final simulated field
and the exact solution. It uses the config options to determine whether the
convergence rate falls within acceptable bounds.

### viz

The class {py:class}`polaris.ocean.tasks.inertial_gravity_wave.viz.Viz` defines
a step for visualization. It produces two plots: the convergence of the RMSE
with resolution and a plan-view of the simulated, exact, and (simulated - exact)
SSH fields.
a step for visualization. It produces a plan-view figure of the simulated,
exact, and (simulated - exact) SSH fields.
35 changes: 18 additions & 17 deletions docs/developers_guide/ocean/tasks/manufactured_solution.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,31 +45,32 @@ the exact solution. The tracer and coriolis fields are uniform in space.
### forward

The class {py:class}`polaris.ocean.tasks.manufactured_solution.forward.Forward`
defines a step for running MPAS-Ocean from the initial condition produced in
the `init` step. Namelist and streams files are updated in
{py:meth}`polaris.ocean.tasks.manufactured_solution.forward.Forward.dynamic_model_config()`
with time steps determined algorithmically based on config options. The
number of cells is approximated from config options in
{py:meth}`polaris.ocean.tasks.manufactured_solution.forward.Forward.compute_cell_count()`
so that this can be used to constrain the number of MPI tasks that Polaris tasks
have as their target and minimum (if the resources are not explicitly
prescribed). For MPAS-Ocean, PIO namelist options are modified and a
graph partition is generated as part of `runtime_setup()`. Next, the ocean
model is run. Finally, validation of `temperature`, `layerThickness` and
descends from
{py:class}`polaris.ocean.convergence.ConvergenceForward`
and runs MPAS-Ocean from the initial condition produced in the `init` step.
Namelist and streams files are updated by the parent class with time steps
determined algorithmically based on config options. The number
of cells is approximated from config options in
{py:meth}`polaris.ocean.tasks.inertial_gravity_wave.forward.Forward.compute_cell_count()`
so that this can be used to constrain the number of MPI tasks that Polaris
tasks have as their target and minimum (if the resources are not explicitly
prescribed). For MPAS-Ocean, PIO namelist options are modified and a graph
partition is generated as part of `runtime_setup()`. Then, the ocean model
is run. Finally, validation of `temperature`, `layerThickness` and
`normalVelocity` are performed against a baseline if one is provided when
calling {ref}`dev-polaris-setup`.

### analysis

The class {py:class}`polaris.ocean.tasks.manufactured_solution.analysis.Analysis`
defines a step for computing the root mean-square-error from the final
simulated field and the exact solution. It uses the config options to determine
whether the convergence rate falls within acceptable bounds.
descends from {py:class}`polaris.ocean.convergence.ConvergenceAnalysis`
a step for computing the error from the final simulated field
and the exact solution. It uses the config options to determine whether the
convergence rate falls within acceptable bounds.

### viz

The class {py:class}`polaris.ocean.tasks.manufactured_solution.viz.Viz`
defines a step for visualization. It produces two plots: the convergence of the
RMSE with resolution and a plan-view of the simulated, exact, and (simulated -
exact) SSH fields.
defines a step for visualization. It produces a plan-view figure of the
simulated, exact, and (simulated - exact) SSH fields.

Loading

0 comments on commit df86670

Please sign in to comment.