from datetime import timedelta
from compass.model import run_model
from compass.step import Step
[docs]
class Forward(Step):
"""
A step for performing forward MPAS-Ocean runs as part of the
nondivergent_2d test case.
Attributes
----------
resolution : int
The resolution of the (uniform) mesh in km
"""
[docs]
def __init__(self, test_case, resolution, dt_minutes):
"""
Create a new step
Parameters
----------
test_case : compass.ocean.tests.global_convergence.nondivergent_2d.Nondivergent2D
The test case this step belongs to
resolution : int
The resolution of the (uniform) mesh in km
dt_minutes : int
The time step size in minutes. **must divide 1 day (24*60)**
""" # noqa: E501
super().__init__(test_case=test_case,
name='QU{}_forward'.format(resolution),
subdir='QU{}/forward'.format(resolution))
self.resolution = resolution
self.dt_minutes = dt_minutes
package = 'compass.ocean.tests.sphere_transport.nondivergent_2d'
self.add_namelist_file(package, 'namelist.forward', mode='forward')
self.add_streams_file(package, 'streams.forward', mode='forward')
self.add_input_file(filename='init.nc',
target='../init/initial_state.nc')
self.add_input_file(filename='graph.info',
target='../mesh/graph.info')
self.add_model_as_input()
self.add_output_file(filename='output.nc')
[docs]
def setup(self):
"""
Set namelist options base on config options
"""
config = self.config
dtstr = self.get_timestep_str()
self.add_namelist_options({'config_dt': dtstr,
'config_time_integrator': config.get(
'nondivergent_2d', 'time_integrator')})
self._get_resources()
def constrain_resources(self, available_resources):
"""
Update resources at runtime from config options
"""
self._get_resources()
super().constrain_resources(available_resources)
[docs]
def run(self):
"""
Run this step of the testcase
"""
config = self.config
dt = self.get_timestep_str()
self.update_namelist_at_runtime(
options={
'config_dt': dt,
'config_time_integrator': config.get(
'nondivergent_2d',
'time_integrator')},
out_name='namelist.ocean')
run_model(self)
[docs]
def get_timestep_str(self):
"""
These tests expect the time step to be input in units of minutes,
but MPAS requires an "HH:MM:SS" string. This function converts the
time step input into the formatted string used by MPAS.
"""
dtminutes = self.dt_minutes
dt = timedelta(minutes=dtminutes)
if dtminutes < 1:
dtstr = "00:00:" + str(dt.total_seconds())[:2]
elif dtminutes >= 60:
dthours = dt / timedelta(hours=1)
dt = dt - timedelta(hours=int(dthours))
dtminutes = dt / timedelta(minutes=1)
dt = dt - timedelta(minutes=int(dtminutes))
dtstr = str(int(dthours))[:2].zfill(2) + ":" + str(int(dtminutes))[
:2].zfill(2) + ":" + str(int(dt.total_seconds()))[:2].zfill(2)
else:
dtminutes = dt / timedelta(minutes=1)
dtstr = "00:" + str(int(dtminutes))[:2].zfill(2) + ":00"
return dtstr
def _get_resources(self):
resolution = self.resolution
config = self.config
self.ntasks = config.getint('nondivergent_2d',
f'QU{resolution}_ntasks')
self.min_tasks = config.getint('nondivergent_2d',
f'QU{resolution}_min_tasks')