Skip to content

Port merry-go-round testcase from compass #284

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions polaris/tasks/ocean/add_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from polaris.tasks.ocean.manufactured_solution import (
add_manufactured_solution_tasks as add_manufactured_solution_tasks,
)
from polaris.tasks.ocean.merry_go_round import add_merry_go_round_tasks
from polaris.tasks.ocean.single_column import add_single_column_tasks
from polaris.tasks.ocean.sphere_transport import add_sphere_transport_tasks

Expand All @@ -32,6 +33,7 @@ def add_ocean_tasks(component):
add_internal_wave_tasks(component=component)
add_isomip_plus_tasks(component=component, mesh_type='planar')
add_manufactured_solution_tasks(component=component)
add_merry_go_round_tasks(component=component)

# single column tasks
add_single_column_tasks(component=component)
Expand Down
133 changes: 133 additions & 0 deletions polaris/tasks/ocean/merry_go_round/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import os
from math import ceil as ceil
from typing import Dict as Dict

from polaris import Step, Task
from polaris.config import PolarisConfigParser
from polaris.ocean.convergence import (
get_resolution_for_task,
get_timestep_for_task,
)
from polaris.resolution import resolution_to_string
from polaris.tasks.ocean.merry_go_round.analysis import Analysis
from polaris.tasks.ocean.merry_go_round.forward import Forward
from polaris.tasks.ocean.merry_go_round.init import Init


def add_merry_go_round_tasks(component):
basedir = 'planar/merry_go_round'

config_filename = 'merry_go_round.cfg'
filepath = os.path.join(component.name, basedir, config_filename)
config = PolarisConfigParser(filepath=filepath)
config.add_from_package('polaris.ocean.convergence', 'convergence.cfg')
config.add_from_package(
'polaris.tasks.ocean.merry_go_round', config_filename
)

component.add_task(
MerryGoRound(
component=component,
config=config,
refinement='both',
)
)


class MerryGoRound(Task):
"""
A test group for tracer advection test cases "merry-go-round"
"""

def __init__(self, component, config, refinement='both'):
"""
Create the test case

Parameters
----------
component : polaris.tasks.ocean.Ocean
The ocean component that this task belongs to

config : polaris.config.PolarisConfigParser
A shared config parser

refinement : str, optional
Whether to refine in space, time or both space and time
"""
name = f'merry_go_round_convergence_{refinement}'
basedir = 'planar/merry_go_round'
subdir = f'{basedir}/convergence_{refinement}/'
config_filename = 'merry_go_round.cfg'

super().__init__(component=component, name=name, subdir=subdir)
self.set_shared_config(config, link=config_filename)

analysis_dependencies: Dict[str, Dict[float, Step]] = dict(
mesh=dict(), init=dict(), forward=dict()
)

if refinement == 'time':
option = 'refinement_factors_time'
else:
option = 'refinement_factors_space'
refinement_factors = self.config.getlist(
'convergence', option, dtype=float
)
timesteps = list()
resolutions = list()
for refinement_factor in refinement_factors:
resolution = get_resolution_for_task(
self.config, refinement_factor, refinement=refinement
)
mesh_name = resolution_to_string(resolution)

subdir = f'{basedir}/init/{mesh_name}'
symlink = f'init/{mesh_name}'
if subdir in component.steps:
init_step = component.steps[subdir]
else:
init_step = Init(
component=component,
resolution=resolution,
name=f'init_{mesh_name}',
subdir=subdir,
)
init_step.set_shared_config(self.config, link=config_filename)
if resolution not in resolutions:
self.add_step(init_step, symlink=symlink)
resolutions.append(resolution)

timestep, _ = get_timestep_for_task(
config, refinement_factor, refinement=refinement
)
timestep = ceil(timestep)
timesteps.append(timestep)

subdir = f'{basedir}/forward/{mesh_name}_{timestep}s'
symlink = f'forward/{mesh_name}_{timestep}s'
if subdir in component.steps:
forward_step = component.steps[subdir]
else:
forward_step = Forward(
component=component,
refinement=refinement,
refinement_factor=refinement_factor,
name=f'forward_{mesh_name}_{timestep}s',
subdir=subdir,
init=init_step,
)
forward_step.set_shared_config(config, link=config_filename)
self.add_step(forward_step, symlink=symlink)

analysis_dependencies['mesh'][refinement_factor] = init_step
analysis_dependencies['init'][refinement_factor] = init_step
analysis_dependencies['forward'][refinement_factor] = forward_step

self.add_step(
Analysis(
component=component,
subdir=f'{self.subdir}/analysis',
dependencies=analysis_dependencies,
refinement=refinement,
)
)
47 changes: 47 additions & 0 deletions polaris/tasks/ocean/merry_go_round/analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from polaris.ocean.convergence.analysis import ConvergenceAnalysis


class Analysis(ConvergenceAnalysis):
"""
A step for analyzing the output from merry-go-round test case

Attributes
----------
resolutions : list of float
The resolutions of the meshes that have been run

case_name : str
The name of the test case
"""

def __init__(self, component, subdir, dependencies, refinement='both'):
"""
Create the step

Parameters
----------
component : polaris.Component
The component the step belongs to

subdir : str
The subdirectory that the step resides in

dependencies : dict of dict of polaris.Steps
The dependencies of this step

refinement : str, optional
Refinement type. One of 'space', 'time' or 'both' indicating both
space and time
"""
convergence_vars = [
{'name': 'tracer1', 'title': 'tracer1', 'zidx': 0},
]
super().__init__(
component=component,
subdir=subdir,
dependencies=dependencies,
convergence_vars=convergence_vars,
refinement=refinement,
)
# Note: there is no need to overwrite the default method exact_solution
# which uses the initial condition
47 changes: 47 additions & 0 deletions polaris/tasks/ocean/merry_go_round/default/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from polaris import Task
from polaris.ocean.tasks.merry_go_round.forward import Forward


class Default(Task):
"""
The default test case for the merry-go-round simply ...
"""

def __init__(self, component, resolution, indir, init):
"""
Create the test case

Parameters
----------
component : polaris.ocean.Ocean
The ocean component that this task belongs to

resolution : float
The resolution of the test case in km

indir : str
The directory the task is in, to which ``name`` will be appended

init : polaris.ocean.tasks.merry_go_round.init.Init
A shared step for creating the initial state
"""
super().__init__(component=component, name='default', indir=indir)

self.add_step(init, symlink='init')

self.add_step(
Forward(
component=component,
refinement='both',
refinement_factor=1,
name='default',
subdir=self.subdir,
init=init,
)
)

"""
self.add_step(
Viz(component=component, indir=self.subdir)
)
"""
96 changes: 96 additions & 0 deletions polaris/tasks/ocean/merry_go_round/forward.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from polaris.mesh.planar import compute_planar_hex_nx_ny
from polaris.ocean.convergence import get_resolution_for_task
from polaris.ocean.convergence.forward import ConvergenceForward


class Forward(ConvergenceForward):
"""
A step for performing forward ocean component runs as part of
merry-go-round test cases.

Attributes
----------
refinement_factor : float
The factor by which to scale space, time or both

refinement : str
Refinement type. One of 'space', 'time' or 'both' indicating both
space and time

resolution : float
The resolution of the test case in km
"""

def __init__(
self,
component,
name,
refinement_factor,
subdir,
init,
refinement='both',
):
"""
Create a new test case

Parameters
----------
component : polaris.Component
The component the step belongs to

name : str
The name of the step

refinement_factor : float
The factor by which to scale space, time or both

subdir : str
The subdirectory that the task belongs to

init : polaris.Step
The step which generates the mesh and initial condition

refinement : str, optional
Refinement type. One of 'space', 'time' or 'both' indicating both
space and time
"""

validate_vars = ['normalVelocity', 'tracer1', 'tracer2', 'tracer3']
super().__init__(
component=component,
name=name,
subdir=subdir,
refinement_factor=refinement_factor,
mesh=init,
init=init,
refinement=refinement,
package='polaris.tasks.ocean.merry_go_round',
yaml_filename='forward.yaml',
graph_target=f'{init.path}/culled_graph.info',
output_filename='output.nc',
validate_vars=validate_vars,
)

def compute_cell_count(self):
"""
Compute the approximate number of cells in the mesh, used to constrain
resources

Returns
-------
cell_count : int or None
The approximate number of cells in the mesh
"""
section = self.config['merry_go_round']

# no file to read from, so we'll compute it based on config options
resolution = get_resolution_for_task(
self.config, self.refinement_factor, refinement=self.refinement
)

lx = section.getfloat('lx')
ly = section.getfloat('ly')

nx, ny = compute_planar_hex_nx_ny(lx, ly, resolution)

return nx * ny
Loading
Loading