Skip to content

Commit

Permalink
Merge pull request #785 from sbrus89/subgrid_pr
Browse files Browse the repository at this point in the history
Add test cases for subgrid scale wetting and drying corrections
  • Loading branch information
xylar authored May 11, 2024
2 parents dae9a81 + 5681e8a commit e7267c0
Show file tree
Hide file tree
Showing 39 changed files with 1,195 additions and 24 deletions.
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

0 comments on commit e7267c0

Please sign in to comment.