Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add test cases for subgrid scale wetting and drying corrections #785

Merged
merged 14 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compass/ocean/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from compass.mpas_core import MpasCore
from compass.ocean.tests.baroclinic_channel import BaroclinicChannel
from compass.ocean.tests.buttermilk_bay import ButtermilkBay
from compass.ocean.tests.dam_break import DamBreak
from compass.ocean.tests.drying_slope import DryingSlope
from compass.ocean.tests.global_convergence import GlobalConvergence
Expand Down Expand Up @@ -37,6 +38,7 @@ def __init__(self):
super().__init__(name='ocean')

self.add_test_group(BaroclinicChannel(mpas_core=self))
self.add_test_group(ButtermilkBay(mpas_core=self))
self.add_test_group(DamBreak(mpas_core=self))
self.add_test_group(DryingSlope(mpas_core=self))
self.add_test_group(GlobalConvergence(mpas_core=self))
Expand Down
18 changes: 18 additions & 0 deletions compass/ocean/tests/buttermilk_bay/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from compass.ocean.tests.buttermilk_bay.default import Default
from compass.testgroup import TestGroup


class ButtermilkBay(TestGroup):
"""
A test group for Buttermilk Bay (subgrid wetting-and-drying) test cases
"""

def __init__(self, mpas_core):
"""
mpas_core : compass.MpasCore
the MPAS core that this test group belongs to
"""
super().__init__(mpas_core=mpas_core, name='buttermilk_bay')
for wetdry in ['standard', 'subgrid']:
self.add_test_case(
Default(test_group=self, wetdry=wetdry))
33 changes: 33 additions & 0 deletions compass/ocean/tests/buttermilk_bay/buttermilk_bay.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[job]

wall_time = 2:00:00


# config options for buttermilk bay
[buttermilk_bay]

# dimensions of domain in x and y directions (m)
Lx = 4608
Ly = 4608

# a list of resolutions (m) to test
resolutions = 256, 128, 64

# time step per resolution (s/m), since dt is proportional to resolution
dt_per_m = 0.02

# the number of cells per core to aim for
goal_cells_per_core = 300

# the approximate maximum number of cells per core (the test will fail if too
# few cores are available)
max_cells_per_core = 3000

# config options for visualizing drying slope ouptut
[buttermilk_bay_viz]

# coordinates (in km) for timeseries plot
points = [2.8, 0.53], [1.9, 1.66], [2.4, 3.029], [2.51, 3.027], [1.26, 1.56]

# generate contour plots at a specified interval between output timesnaps
plot_interval = 1
141 changes: 141 additions & 0 deletions compass/ocean/tests/buttermilk_bay/default/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import numpy as np

from compass.config import CompassConfigParser
from compass.ocean.tests.buttermilk_bay.forward import Forward
from compass.ocean.tests.buttermilk_bay.initial_state import InitialState
from compass.ocean.tests.buttermilk_bay.viz import Viz
from compass.testcase import TestCase
from compass.validate import compare_variables


class Default(TestCase):
"""
The default buttermilk_bay test case

Attributes
----------
wetdry : str
The type of wetting and drying (``standard``, ``subgrid``)
"""

def __init__(self, test_group, wetdry):
"""
Create the test case

Parameters
----------
test_group : compass.ocean.tests.buttermilk_bay.ButtermilkBay
The test group that this test case belongs to

wetdry : str
The type of wetting and drying used (``standard``, ``subgrid``)
"""
name = wetdry
subdir = wetdry
super().__init__(test_group=test_group, name=name,
subdir=subdir)

self.resolutions = None
self.wetdry = wetdry
# add the steps with default resolutions so they can be listed
config = CompassConfigParser()
config.add_from_package('compass.ocean.tests.buttermilk_bay',
'buttermilk_bay.cfg')
self._setup_steps(config)

def configure(self):
"""
Set config options for the test case
"""
config = self.config
# set up the steps again in case a user has provided new resolutions
self._setup_steps(config)

self.update_cores()

def update_cores(self):
""" Update the number of cores and min_tasks for each forward step """

config = self.config

goal_cells_per_core = config.getfloat('buttermilk_bay',
'goal_cells_per_core')
max_cells_per_core = config.getfloat('buttermilk_bay',
'max_cells_per_core')
lx = config.getfloat('buttermilk_bay', 'Lx')
ly = config.getfloat('buttermilk_bay', 'Ly')

for resolution in self.resolutions:

nx = 2 * int(0.5 * lx / resolution + 0.5)
ny = 2 * int(0.5 * ly * (2. / np.sqrt(3)) / resolution + 0.5)

approx_cells = nx * ny
# ideally, about 300 cells per core
# (make it a multiple of 4 because...it looks better?)
ntasks = max(1,
4 * round(approx_cells / (4 * goal_cells_per_core)))
# In a pinch, about 3000 cells per core
min_tasks = max(1,
round(approx_cells / max_cells_per_core))

res_name = f'{resolution}m'
step = self.steps[f'forward_{res_name}']
step.ntasks = ntasks
step.min_tasks = min_tasks

config.set('buttermilk_bay', f'{res_name}_ntasks', str(ntasks),
comment=f'Target core count for {res_name} mesh')
config.set('buttermilk_bay', f'{res_name}_min_tasks',
str(min_tasks),
comment=f'Minimum core count for {res_name} mesh')

def _setup_steps(self, config):
""" setup steps given resolutions """

default_resolutions = '256, 128, 64'

# set the default values that a user may change before setup
config.set('buttermilk_bay', 'resolutions', default_resolutions,
comment='a list of resolutions (m) to test')

# get the resolutions back, perhaps with values set in the user's
# config file
resolutions = config.getlist('buttermilk_bay',
'resolutions', dtype=int)

if self.resolutions is not None and self.resolutions == resolutions:
return

# start fresh with no steps
self.steps = dict()
self.steps_to_run = list()

self.resolutions = resolutions

for resolution in self.resolutions:

res_name = f'{resolution}m'

init_step = InitialState(test_case=self,
name=f'initial_state_{res_name}',
resolution=resolution,
wetdry=self.wetdry)
self.add_step(init_step)
self.add_step(Forward(test_case=self,
name=f'forward_{res_name}',
resolution=resolution,
wetdry=self.wetdry))
self.add_step(Viz(test_case=self,
wetdry=self.wetdry,
resolutions=resolutions))

def validate(self):
"""
Validate variables against a baseline
"""
super().validate()
variables = ['layerThickness', 'normalVelocity']
for res in self.resolutions:
compare_variables(test_case=self, variables=variables,
filename1=f'forward_{res}m/output.nc')
122 changes: 122 additions & 0 deletions compass/ocean/tests/buttermilk_bay/forward.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import time

from compass.model import run_model
from compass.step import Step


class Forward(Step):
"""
A step for performing forward MPAS-Ocean runs as part of buttermilk bay
test cases.
"""
def __init__(self, test_case, resolution,
name,
coord_type='single_layer',
wetdry='standard'):
"""
Create a new test case

Parameters
----------
test_case : compass.TestCase
The test case this step belongs to

resolution : float
The resolution of the test case

name : str
The name of the test case

subdir : str, optional
The subdirectory for the step. The default is ``name``

coord_type : str, optional
Vertical coordinate configuration
"""

self.resolution = resolution

super().__init__(test_case=test_case, name=name)

self.add_namelist_file('compass.ocean.tests.buttermilk_bay',
'namelist.forward')

res_name = f'{resolution}m'
self.add_namelist_file('compass.ocean.tests.buttermilk_bay',
f'namelist.{coord_type}.forward')

if wetdry == 'subgrid':
self.add_namelist_file('compass.ocean.tests.buttermilk_bay',
'namelist.subgrid.forward')
self.add_streams_file('compass.ocean.tests.buttermilk_bay',
'streams.subgrid.forward')
else:
self.add_streams_file('compass.ocean.tests.buttermilk_bay',
'streams.forward')
input_path = f'../initial_state_{res_name}'
self.add_input_file(filename='mesh.nc',
target=f'{input_path}/culled_mesh.nc')
self.add_input_file(filename='init.nc',
target=f'{input_path}/ocean.nc')
self.add_input_file(filename='graph.info',
target=f'{input_path}/culled_graph.info')
self.add_input_file(filename='forcing.nc',
target=f'{input_path}/init_mode_forcing_data.nc')

self.add_model_as_input()

self.add_output_file(filename='output.nc')

def setup(self):
"""
Set namelist options based on config options
"""
dt = self.get_dt()
self.add_namelist_options({'config_dt': dt})
self._get_resources()

def constrain_resources(self, available_cores):
"""
Update resources at runtime from config options
"""
self._get_resources()
super().constrain_resources(available_cores)

def run(self):
"""
Run this step of the testcase
"""
# update dt in case the user has changed dt_per_m
dt = self.get_dt()
self.update_namelist_at_runtime(options={'config_dt': dt},
out_name='namelist.ocean')

run_model(self)

def get_dt(self):
"""
Get the time step

Returns
-------
dt : str
the time step in HH:MM:SS
"""
config = self.config
# dt is proportional to resolution
dt_per_m = config.getfloat('buttermilk_bay', 'dt_per_m')

dt = dt_per_m * self.resolution
# https://stackoverflow.com/a/1384565/7728169
dt = time.strftime('%H:%M:%S', time.gmtime(dt))

return dt

def _get_resources(self):
""" get the these properties from the config options """
config = self.config
self.ntasks = config.getint('buttermilk_bay',
f'{self.resolution}m_ntasks')
self.min_tasks = config.getint('buttermilk_bay',
f'{self.resolution}m_min_tasks')
self.openmp_threads = 1
Loading
Loading