Skip to content

Commit

Permalink
Dynamically update forcing paths in model driver
Browse files Browse the repository at this point in the history
For longer running simulations (such as spinup), it is a common use case
to repeat the forcing over however many years of forcing we have
throughout a model run.

This change modifies the CABLE driver to dynamically update the forcing
paths in the CABLE namelist file when an optional config file
(cable.forcing_year.yaml) is present in the experiment directory. The
optional configuration file can be used to specify which years of
forcing should be run and or repeated via the `offset` and `repeat`
keys.

This was originally inside a setup script, however we decided this is
better off being implemented within the driver as: 1. the script was
duplicating a lot of the logic in payu when inferring the current
simulation year, and 2. running python in a setup script requires the
user to have the correct python environment activated which may be prone
to errors - implementing the script in the driver avoids this issue.
  • Loading branch information
SeanBryan51 committed Nov 21, 2023
1 parent b6e7efc commit 2f02b68
Showing 1 changed file with 43 additions and 3 deletions.
46 changes: 43 additions & 3 deletions payu/models/cable.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""

# Standard Library
import glob
import os
import shutil

Expand Down Expand Up @@ -36,7 +37,39 @@ def __init__(self, expt, name, config):
'pft_params.nml',
]

self.restart_info = {}
self.forcing_year_config = 'cable.forcing_year.yaml'
self.optional_config_files = [self.forcing_year_config]

self.met_forcing_vars = [
"Rainf",
"Snowf",
"LWdown",
"SWdown",
"PSurf",
"Qair",
"Tair",
"Wind",
]

def _get_forcing_path(self, variable, year):
"""Return the met forcing file path for a given variable and year."""
pattern = os.path.join(self.work_input_path, f"*{variable}*{year}*.nc")
for path in glob.glob(pattern):
return path
msg = f"Unable to infer met forcing path for variable {variable} for year {year}."
raise FileNotFoundError(msg)

def _update_forcing(self, year, offset=None, repeat=None):
"""Update the CABLE namelist file to use the correct met forcing."""
if offset:
year += offset[1] - offset[0]
if repeat:
year = repeat[0] + ((year - repeat[0]) % (repeat[1] - repeat[0] + 1))
for var in self.met_forcing_vars:
path = self._get_forcing_path(var, year)
self.cable_nml["cable"]["gswpfile"][var] = (
os.path.relpath(path, start=self.work_path)
)

def set_model_pathnames(self):
super(Cable, self).set_model_pathnames()
Expand All @@ -61,9 +94,16 @@ def setup(self):
if self.prior_restart_path:
with open(self.restart_calendar_path, 'r') as restart_file:
self.restart_info = yaml.safe_load(restart_file)
self.cable_nml['cable']['ncciy'] = self.restart_info['year']
else:
self.restart_info['year'] = self.cable_nml['cable']['ncciy']
self.restart_info = {'year': self.cable_nml['cable']['ncciy']}

year = self.cable_nml['cable']['ncciy'] = self.restart_info['year']

forcing_year_config_path = os.path.join(self.work_path, self.forcing_year_config)
if os.path.exists(forcing_year_config_path):
with open(forcing_year_config_path, 'r') as file:
forcing_year_config = yaml.safe_load(file)
self._update_forcing(year, **forcing_year_config)

# Write modified namelist file to work dir
self.cable_nml.write(
Expand Down

0 comments on commit 2f02b68

Please sign in to comment.