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 baroclinic gyre tests #547

Merged
merged 39 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
482abf5
added linear dz in vertical grid
alicebarthel Oct 30, 2023
67daaa1
added baroclinic gyre test case - main files
alicebarthel Oct 31, 2023
10081cf
added baroclinic gyre - documentation
alicebarthel Oct 31, 2023
d3fbb8a
corrected path to output
alicebarthel Nov 6, 2023
4b26fd5
suggested changes: salinity
alicebarthel Nov 6, 2023
b95ab4d
modified resolution to string in m
alicebarthel Nov 6, 2023
3964ace
updated documentation
alicebarthel Nov 6, 2023
f4d5676
added config parameters for initial temp profile
alicebarthel Nov 7, 2023
066ff24
updated documentation
alicebarthel Nov 7, 2023
e49060b
correction for T calculation
alicebarthel Nov 15, 2023
bf6dda0
corrected inconsistencies
alicebarthel Nov 15, 2023
a6ff455
added restart capabilities
alicebarthel Nov 15, 2023
ea94330
modifed 3_year test parameters
alicebarthel Mar 27, 2024
8b24a8e
minor changes to set-up
alicebarthel Mar 28, 2024
250fea8
added a finer resolution case
alicebarthel Mar 28, 2024
608047b
changes to set up
alicebarthel Mar 28, 2024
77664c8
updated documentation
alicebarthel Apr 1, 2024
41f12af
correct the restart set-up
alicebarthel Apr 1, 2024
974fe68
correct default run setup
alicebarthel Jul 29, 2024
9182875
added monthly output
alicebarthel Jul 30, 2024
08419be
added monthly streams
alicebarthel Jul 31, 2024
b03cb16
WIP - analysis and viz steps
alicebarthel Jul 30, 2024
d61771a
WIP - debugging moc analysis
alicebarthel Jul 31, 2024
dd7db80
debugging moc calc
alicebarthel Aug 1, 2024
517a81c
add time-varying moc
alicebarthel Aug 1, 2024
51e7422
added moc viz step
alicebarthel Aug 1, 2024
7e37003
added spinup timeseries plot
alicebarthel Aug 1, 2024
4f8ea0c
removed some static fields from output and monthly streams
alicebarthel Aug 1, 2024
a7776ba
adding the moc analysis and visualization steps (moc and spinup)
alicebarthel Aug 1, 2024
1e74310
added averaging period for moc
alicebarthel Aug 1, 2024
05f2b79
added time mean state plots
alicebarthel Aug 12, 2024
1964db4
WIP - correction restoring velocity
alicebarthel Aug 12, 2024
3d5fff7
modified doc for restoring correction and estimated spin-up time
alicebarthel Aug 12, 2024
e976366
correction to heat flux plot
alicebarthel Aug 12, 2024
77b6e27
WIP - added surface flux variable to streams as test
alicebarthel Aug 12, 2024
a85d655
correction to plot title
alicebarthel Aug 12, 2024
b7139d3
corrected the heat flux viz
alicebarthel Aug 13, 2024
5d571a9
best practice modif for strings
alicebarthel Aug 13, 2024
36f3f91
PR review changes
alicebarthel Aug 16, 2024
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.baroclinic_gyre import BaroclinicGyre
from compass.ocean.tests.buttermilk_bay import ButtermilkBay
from compass.ocean.tests.dam_break import DamBreak
from compass.ocean.tests.drying_slope import DryingSlope
Expand Down Expand Up @@ -38,6 +39,7 @@ def __init__(self):
super().__init__(name='ocean')

self.add_test_group(BaroclinicChannel(mpas_core=self))
self.add_test_group(BaroclinicGyre(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))
Expand Down
22 changes: 22 additions & 0 deletions compass/ocean/tests/baroclinic_gyre/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from compass.ocean.tests.baroclinic_gyre.gyre_test_case import GyreTestCase
from compass.testgroup import TestGroup


class BaroclinicGyre(TestGroup):
"""
A test group for baroclinic gyre 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='baroclinic_gyre')

for resolution in [20000., 80000.]:
self.add_test_case(
GyreTestCase(test_group=self, resolution=resolution,
long=False))
self.add_test_case(
GyreTestCase(test_group=self, resolution=resolution,
long=True))
55 changes: 55 additions & 0 deletions compass/ocean/tests/baroclinic_gyre/baroclinic_gyre.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

# Options related to the vertical grid
[vertical_grid]

# the type of vertical grid
grid_type = linear_dz

# the linear rate of thickness increase for linear_dz
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

units?

linear_dz_rate = 10.

# Number of vertical levels
vert_levels = 15

# Total water column depth
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Total water column depth
# Total water column depth in m

bottom_depth = 1800.

# The type of vertical coordinate (e.g. z-level, z-star)
coord_type = z-star

# Whether to use "partial" or "full", or "None" to not alter the topography
partial_cell_type = None

# The minimum fraction of a layer for partial cells
min_pc_fraction = 0.1


# config options for the baroclinic gyre
[baroclinic_gyre]
# Basin dimensions
lat_min = 15
lat_max = 75
lon_min = 0
lon_max = 60

# Initial vertical temperature profile
initial_temp_top = 33.
initial_temp_bot = 1.

# Constant salinity value (also used in restoring)
initial_salinity = 34.

# Maximum zonal wind stress value
wind_stress_max = 0.1

# Surface temperature restoring
restoring_temp_min = 0.
restoring_temp_max = 30.

# Restoring piston velocity for surface temperature (s-1)
restoring_temp_piston_vel = 3.86e-7

# config options for the mean state visualization
[mean_state_viz]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[mean_state_viz]
[baroclinic_gyre_viz]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I modified this to include both viz and moc calculation parameters.

# number of years to average over for the mean state plots
time_averaging_length = 1
61 changes: 61 additions & 0 deletions compass/ocean/tests/baroclinic_gyre/cull_mesh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import numpy as np
import xarray as xr
from mpas_tools.io import write_netcdf
from mpas_tools.mesh.conversion import convert, cull

from compass.step import Step


class CullMesh(Step):
"""
Cull a global mesh to only a signle basin
alicebarthel marked this conversation as resolved.
Show resolved Hide resolved
Attributes
----------
"""
def __init__(self, test_case):
"""
Create the step
Parameters
----------
test_case : compass.TestCase
The test case this step belongs to
"""
super().__init__(test_case=test_case, name='cull_mesh')

self.add_input_file(
filename='base_mesh.nc',
target='../base_mesh/base_mesh.nc')

for file in ['culled_mesh.nc', 'culled_graph.info']:
self.add_output_file(file)

def run(self):
"""
Run this step of the test case
"""
config = self.config
section = config['baroclinic_gyre']
logger = self.logger
ds_mesh = xr.open_dataset('base_mesh.nc')
ds_mask = xr.Dataset()

lon = np.rad2deg(ds_mesh.lonCell.values)
lat = np.rad2deg(ds_mesh.latCell.values)
lon_min = section.getfloat('lon_min')
lon_max = section.getfloat('lon_max')
lat_min = section.getfloat('lat_min')
lat_max = section.getfloat('lat_max')

mask = np.logical_and(
np.logical_and(lon >= lon_min, lon <= lon_max),
np.logical_and(lat >= lat_min, lat <= lat_max))

n_cells = ds_mesh.sizes['nCells']
ds_mask['regionCellMasks'] = (('nCells', 'nRegions'),
mask.astype(int).reshape(n_cells, 1))

ds_mesh = cull(ds_mesh, dsInverse=ds_mask, logger=logger)
ds_mesh = convert(ds_mesh, graphInfoFileName='culled_graph.info',
logger=logger)

write_netcdf(ds_mesh, 'culled_mesh.nc')
121 changes: 121 additions & 0 deletions compass/ocean/tests/baroclinic_gyre/forward.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
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
the baroclinic gyre test cases.

Attributes
----------
resolution : float
The resolution of the test case (m)

"""
def __init__(self, test_case, resolution, name='forward', subdir=None,
long=False):
"""
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 (m)

name : str
the name of the test case

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

long : bool, optional
Whether to run a long (3-year) simulation to quasi-equilibrium
"""
self.long = long
self.resolution = resolution
if resolution >= 1e3:
res_name = f'{int(resolution/1e3)}km'
else:
res_name = f'{int(resolution)}m'

res_params = {'80km': {'ntasks': 32,
'min_tasks': 3,
'dt': "'01:00:00'",
'btr_dt': "'00:03:00'",
'mom_del4': "5.0e11",
'run_duration': "'0000_03:00:00'"},
'20km': {'ntasks': 384,
'min_tasks': 32,
'dt': "'00:20:00'",
'btr_dt': "'0000_00:00:20'",
'mom_del4': "2.0e10 ",
'run_duration': "'0000_01:00:00'"}}

if res_name not in res_params:
raise ValueError(
f'Unsupported resolution {res_name}. Supported values are: '
f'{list(res_params)}')

res_params = res_params[res_name]

ntasks = res_params['ntasks']
min_tasks = res_params['min_tasks']

super().__init__(test_case=test_case, name=name, subdir=subdir,
ntasks=ntasks, min_tasks=min_tasks, openmp_threads=1)

# make sure output is double precision
self.add_streams_file('compass.ocean.streams', 'streams.output')

self.add_namelist_file('compass.ocean.tests.baroclinic_gyre',
'namelist.forward')
if long:
output_interval = "0000-01-00_00:00:00"
restart_interval = "0001-00-00_00:00:00"
else:
output_interval = res_params['run_duration'].replace("'", "")
restart_interval = "0030_00:00:00"
replacements = dict(
output_interval=output_interval, restart_interval=restart_interval)
self.add_streams_file(package='compass.ocean.tests.baroclinic_gyre',
streams='streams.forward',
template_replacements=replacements)
options = dict()
for option in ['dt', 'btr_dt', 'mom_del4', 'run_duration']:
options[f'config_{option}'] = res_params[option]
if long:
# run for 3 years instead of 3 time steps
options['config_start_time'] = "'0001-01-01_00:00:00'"
options['config_stop_time'] = "'0004-01-01_00:00:00'"
options['config_run_duration'] = "'none'"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
options['config_start_time'] = "'0001-01-01_00:00:00'"
options['config_stop_time'] = "'0004-01-01_00:00:00'"
options['config_run_duration'] = "'none'"
options['config_start_time'] = "'0001-01-01_00:00:00'"
options['config_stop_time'] = "'0004-01-01_00:00:00'"
options['config_run_duration'] = "'none'"

I wonder if it might not be better to set a 3-year run duration here. The problem with setting a start and stop time is that it somewhat defeats the purpose of switching to "restart" mode at the end of the first run of the forward step. If a user were to run a second time, the simulation would immediately finish because it's already at year 4. But if you instead set run duration, you can run for another 3 years each time you rerun.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point, @xylar, thanks for noticing. I've usually modified it manually each time, but setting up for re-running it for 3years is much more user-friendly.


self.add_namelist_options(options=options)
self.add_input_file(filename='init.nc',
target='../initial_state/initial_state.nc')
self.add_input_file(filename='forcing.nc',
target='../initial_state/forcing.nc')
self.add_input_file(filename='graph.info',
target='../initial_state/culled_graph.info')

self.add_model_as_input()

self.add_output_file(filename='output/output.0001-01-01_00.00.00.nc')
self.add_output_file(filename='output/'
'timeSeriesStatsMonthly.0001-01-01.nc')

# no setup() is needed

def run(self):
"""
Run this step of the test case
"""
run_model(self, partition_graph=True)

if self.long:
replacements = {'config_do_restart': '.true.',
'config_start_time': "'file'"}
self.update_namelist_at_runtime(replacements)
81 changes: 81 additions & 0 deletions compass/ocean/tests/baroclinic_gyre/gyre_test_case.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from compass.mesh import QuasiUniformSphericalMeshStep
from compass.ocean.tests.baroclinic_gyre.cull_mesh import CullMesh
from compass.ocean.tests.baroclinic_gyre.forward import Forward
from compass.ocean.tests.baroclinic_gyre.initial_state import InitialState
from compass.ocean.tests.baroclinic_gyre.moc import Moc
from compass.ocean.tests.baroclinic_gyre.viz import Viz
from compass.testcase import TestCase
from compass.validate import compare_variables


class GyreTestCase(TestCase):
"""
A class to define the baroclinic gyre test cases

Attributes
----------
resolution : float
The resolution of the test case (m)
"""

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

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

resolution : float
The resolution of the test case (m)

long : bool
Whether to run a long (3-year) simulation to quasi-equilibrium
"""
name = 'performance_test'
self.resolution = resolution
self.long = long

if long:
name = '3_year_test'

if resolution >= 1e3:
res_name = f'{int(resolution/1e3)}km'
else:
res_name = f'{int(resolution)}m'
subdir = f'{res_name}/{name}'
super().__init__(test_group=test_group, name=name,
subdir=subdir)

self.add_step(QuasiUniformSphericalMeshStep(
test_case=self, cell_width=int(resolution / 1e3)))
self.add_step(CullMesh(test_case=self))
self.add_step(
InitialState(test_case=self, resolution=resolution))
self.add_step(
Forward(test_case=self, resolution=resolution,
long=long))
if long:
self.add_step(
Moc(test_case=self, resolution=resolution))
self.add_step(
Viz(test_case=self, resolution=resolution))

def configure(self):
"""
Set config options for the test case
"""
config = self.config
config.add_from_package('compass.mesh', 'mesh.cfg')

def validate(self):
"""
Validate variables against a baseline
"""
compare_variables(test_case=self,
variables=['layerThickness', 'temperature',
'ssh'],
filename1='forward/output/'
'output.0001-01-01_00.00.00.nc')
Loading
Loading