Commit 24a2f6ba authored by Dan Baston's avatar Dan Baston

Merge branch 'add-results-vars' into 'master'

Add results vars

See merge request !22
parents 692de30b afbd93d7
Pipeline #76581886 passed with stages
in 20 minutes and 21 seconds
from typing import List, Optional
from config_cfs import CFSConfig
from wsim_workflow import dates
class CFSConfigFast(CFSConfig):
# Skip 6-month, >12-month integration periods:
@staticmethod
def integration_windows() -> List[int]:
return [3, 12]
# Use only the first 3 forecast ensemble members:
def forecast_ensemble_members(self, yearmon, *, lag_hours: Optional[int] = None):
return CFSConfig.forecast_ensemble_members(self, yearmon, lag_hours=lag_hours)[:2]
# Forecast out only 3 months instead of 9:
def forecast_targets(self, yearmon):
return dates.get_next_yearmons(yearmon, 3)
config = CFSConfigFast
\ No newline at end of file
......@@ -99,6 +99,10 @@ class GLDAS20_NoahConfig(ConfigBase):
def forcing_rp_vars(cls, basis=None):
return []
@classmethod
def forcing_integrated_vars(cls, basis=None):
return {}
@classmethod
def lsm_rp_vars(cls, basis=None):
if not basis:
......@@ -111,13 +115,18 @@ class GLDAS20_NoahConfig(ConfigBase):
'Pr'
]
if basis == 'basin':
if basis == Basis.BASIN:
return [
'Bt_RO_m3'
]
assert False
@classmethod
def state_rp_vars(cls, basis=None):
return[]
@classmethod
def lsm_integrated_vars(cls, basis=None):
if not basis:
......@@ -137,6 +146,11 @@ class GLDAS20_NoahConfig(ConfigBase):
assert False
@classmethod
def forcing_integrated_vars(cls, basis=None):
return {}
def result_postprocess_steps(self, yearmon=None, target=None, member=None):
input_file = os.path.join(self._source,
'GLDAS_NOAH025_M.A{}.020.nc4'.format(yearmon))
......
......@@ -171,7 +171,7 @@ class TestCFSConfig(unittest.TestCase):
self.assertIsNotNone(step_for_target(steps, fname))
# forcing
assertBuilt(ws.forcing(yearmon=yearmon))
assertBuilt(ws.forcing(yearmon=yearmon, window=1))
assertBuilt(ws.forcing_summary(yearmon=yearmon, target=target))
# results
......
......@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function # Avoid bombing in Python 2 before we even hit our version check
from __future__ import print_function # Avoid bombing in Python 2 before we even hit our version check
import sys
......@@ -27,13 +27,33 @@ import argparse
from wsim_workflow import workflow
import importlib
import importlib.util
import types
from importlib.machinery import SourceFileLoader
import sys
def load_module(module):
return importlib.import_module('wsim_workflow.output.{}'.format(module))
def load_config(path, source, derived):
return SourceFileLoader("config", path).load_module().config(source, derived)
dirname = os.path.dirname(path)
# Temporarily add config module directory to the system path
# This feels wrong, but I can't find another method that allows
# one config to derive from another (as config_fast inherits from
# config_cfs)
sys.path.insert(0, dirname)
loader = SourceFileLoader("config", path)
mod = types.ModuleType(loader.name)
loader.exec_module(mod)
sys.path.remove(dirname)
return mod.config(source, derived)
def parse_args(args):
parser = argparse.ArgumentParser('Generate a Makefile for WSIM data processing')
......
......@@ -35,17 +35,30 @@ class TestWorkspacePaths(unittest.TestCase):
)
def test_forcing(self):
# Observed data
# Observed data:
self.assertEqual(
join(self.root, 'forcing', 'forcing_1mo_201612.nc'),
self.ws.forcing(yearmon='201612', window=1)
)
self.assertEqual(
join(self.root, 'forcing', 'forcing_201612.nc'),
self.ws.forcing(yearmon='201612')
join(self.root, 'forcing_integrated', 'forcing_24mo_201612.nc'),
self.ws.forcing(yearmon='201612', window=24)
)
# Observed basins:
self.assertEqual(
join(self.root, 'basin_forcing', 'basin_forcing_1mo_201612.nc'),
self.ws.forcing(yearmon='201612', window=1, basis=Basis.BASIN)
)
# Forecast data
self.assertEqual(
join(self.root, 'forcing', 'forcing_201612_trgt201703_fcstCFS13.nc'),
self.ws.forcing(yearmon='201612', target='201703', member='CFS13')
join(self.root, 'forcing', 'forcing_1mo_201612_trgt201703_fcstCFS13.nc'),
self.ws.forcing(yearmon='201612', target='201703', member='CFS13', window=1)
)
def test_results(self):
# Observed data
......
......@@ -60,7 +60,7 @@ def create_forcing_file(workspace: DefaultWorkspace,
'Pr:units',
'Pr:standard_name'
])),
output=workspace.forcing(yearmon=yearmon, target=target, member=member)
output=workspace.forcing(yearmon=yearmon, target=target, member=member, window=1)
)
]
......@@ -70,7 +70,7 @@ def compute_basin_results(workspace: DefaultWorkspace,
yearmon: str,
target: Optional[str]=None,
member: Optional[str]=None) -> List[Step]:
pixel_forcing = workspace.forcing(yearmon=yearmon, target=target, member=member)
pixel_forcing = workspace.forcing(yearmon=yearmon, target=target, member=member, window=1)
pixel_results = workspace.results(yearmon=yearmon, window=1, target=target, member=member)
basin_results = workspace.results(yearmon=yearmon, window=1, target=target, member=member, basis=Basis.BASIN)
......@@ -119,7 +119,7 @@ def run_lsm(workspace: DefaultWorkspace, static: Static, *,
next_state = workspace.state(yearmon=get_next_yearmon(yearmon))
results = workspace.results(yearmon=yearmon, window=1, target=target, member=member)
forcing = workspace.forcing(yearmon=yearmon, target=target, member=member)
forcing = workspace.forcing(yearmon=yearmon, target=target, member=member, window=1)
return [
wsim_lsm(
......@@ -137,6 +137,7 @@ def run_lsm(workspace: DefaultWorkspace, static: Static, *,
def time_integrate(workspace: DefaultWorkspace,
integrated_stats: Dict[str, List[str]],
*,
forcing: bool,
yearmon: str,
target: Optional[str]=None,
window: Optional[int]=None,
......@@ -153,21 +154,29 @@ def time_integrate(workspace: DefaultWorkspace,
window_observed = months
window_forecast = []
prev_results = [workspace.results(yearmon=x, window=1, basis=basis) for x in window_observed] + \
[workspace.results(yearmon=yearmon, member=member, target=x, window=1, basis=basis)
for x in window_forecast]
if forcing:
prev = [workspace.forcing(yearmon=x, window=1, basis=basis) for x in window_observed] + \
[workspace.forcing(yearmon=yearmon, member=member, target=x, window=1, basis=basis) for x in window_forecast]
else:
prev = [workspace.results(yearmon=x, window=1, basis=basis) for x in window_observed] + \
[workspace.results(yearmon=yearmon, member=member, target=x, window=1, basis=basis) for x in window_forecast]
return [
wsim_integrate(
inputs=[read_vars(f, *set(itertools.chain(*integrated_stats.values()))) for f in prev_results],
inputs=[read_vars(f, *set(itertools.chain(*integrated_stats.values()))) for f in prev],
stats=[stat + '::' + ','.join(varname) for stat, varname in integrated_stats.items()],
attrs=[attrs.integration_window(var='*', months=window)],
output=workspace.results(yearmon=yearmon,
output= workspace.results(yearmon=yearmon,
window=window,
target=target,
member=member,
temporary=False,
basis=basis)
basis=basis) if not forcing else workspace.forcing(yearmon=yearmon,
window=window,
member=member,
target=target,
basis=basis)
)
]
......@@ -175,6 +184,7 @@ def time_integrate(workspace: DefaultWorkspace,
def compute_return_periods(workspace: DefaultWorkspace, *,
forcing_vars: Optional[List[str]]=None,
result_vars: Optional[List[str]]=None,
state_vars: Optional[List[str]]=None,
yearmon: str,
window: int,
target: Optional[str]=None,
......@@ -189,23 +199,26 @@ def compute_return_periods(workspace: DefaultWorkspace, *,
forcing_vars = []
if result_vars is None:
result_vars = []
if state_vars is None:
state_vars = []
args = {'yearmon': yearmon, 'target': target, 'window': window, 'member': member, 'basis': basis}
if basis:
assert not forcing_vars
if state_vars:
assert window==1
return [
wsim_anom(
fits=[workspace.fit_obs(var=var,
window=window,
month=month,
basis=basis) for var in forcing_vars + result_vars],
basis=basis) for var in forcing_vars + result_vars + state_vars],
obs=[
read_vars(workspace.results(**args), *result_vars)
if result_vars else None,
read_vars(workspace.forcing(yearmon=yearmon, target=target, member=member), *forcing_vars)
if forcing_vars else None
read_vars(workspace.forcing(yearmon=yearmon, target=target, window=window, member=member), *forcing_vars)
if forcing_vars else None,
read_vars(workspace.state(yearmon=yearmon), *state_vars) if state_vars else None
],
rp=workspace.return_period(**args),
sa=workspace.standard_anomaly(**args)
......@@ -366,10 +379,14 @@ def fit_var(config: ConfigBase,
param_to_read = param
if param in config.forcing_rp_vars():
infile = config.workspace().forcing(yearmon=input_range, window=window, basis=basis)
elif param in config.state_rp_vars():
assert window == 1
assert basis is None
infile = config.workspace().forcing(yearmon=input_range)
infile = config.workspace().state(yearmon=input_range)
else:
infile = config.workspace().results(yearmon=input_range, window=window, basis=basis)
......@@ -389,7 +406,7 @@ def forcing_summary(workspace: DefaultWorkspace, ensemble_members: List[str], *,
-> List[Step]:
return [
wsim_integrate(
inputs=[workspace.forcing(yearmon=yearmon, target=target, member=member) for member in ensemble_members],
inputs=[workspace.forcing(yearmon=yearmon, target=target, member=member, window=1) for member in ensemble_members],
stats=['q25', 'q50', 'q75'],
output=workspace.forcing_summary(yearmon=yearmon, target=target)
)
......
......@@ -99,7 +99,7 @@ class ConfigBase(metaclass=abc.ABCMeta):
def result_postprocess_steps(self, yearmon=None, target=None, member=None) -> List[Step]:
"""
Provides a list of one or more postprocessing steps to be applied to LSM results
for a givem YYYYMM/forecast target/ensemble member
for a given YYYYMM/forecast target/ensemble member
"""
return []
......@@ -138,7 +138,10 @@ class ConfigBase(metaclass=abc.ABCMeta):
]
if basis == Basis.BASIN:
return []
return [
'T',
'Pr'
]
assert False
......@@ -168,6 +171,32 @@ class ConfigBase(metaclass=abc.ABCMeta):
assert False
@staticmethod
def state_rp_vars(*, basis: Optional[Basis]=None) -> List[str]:
"""
Provides a list of state variables for which return periods should be calculated
"""
if not basis:
return['Snowpack']
if basis == Basis.BASIN:
return []
@classmethod
def forcing_integrated_vars(cls, basis: Optional[Basis]=None) -> Dict[str, List[str]]:
"""
Provides a dictionary whose keys are forcing variables to be time-integrated, and whose
values are lists of stats to apply to each of those variables (min, max, ave, etc.)
"""
if not basis or basis == Basis.BASIN:
return {
'Pr' : ['sum'],
'T' : ['ave']
}
assert False
@classmethod
def lsm_integrated_vars(cls, basis: Optional[Basis]=None) -> Dict[str, List[str]]:
"""
......@@ -182,7 +211,8 @@ class ConfigBase(metaclass=abc.ABCMeta):
'PETmE': ['sum'],
'P_net': ['sum'],
'RO_mm': ['sum'],
'Ws': ['ave']
'Sa' : ['sum'],
'Ws': ['ave'],
}
if basis == Basis.BASIN:
......@@ -216,3 +246,44 @@ class ConfigBase(metaclass=abc.ABCMeta):
return [var + '_' + stat for var, stats in cls.lsm_integrated_vars(basis=basis).items() for stat in stats]
@classmethod
def forcing_integrated_stats(cls, basis: Optional[Basis]=None) -> Dict[str, List[str]]:
"""
Provides a dictionary whose keys are stat names and whose values are a list of variables
two which that stat should be applied. It can be thought of as the inverse of forcing_integrated_vars()
"""
integrated_stats = {}
for var, varstats in cls.forcing_integrated_vars(basis=basis).items():
for stat in varstats:
if stat not in integrated_stats:
integrated_stats[stat] = []
integrated_stats[stat].append(var)
return integrated_stats
@classmethod
def forcing_integrated_var_names(cls, basis: Optional[Basis]=None) -> List[str]:
"""
Provides a flat list of time-integrated variable names
"""
return [var + '_' + stat for var, stats in cls.forcing_integrated_vars(basis=basis).items() for stat in stats]
@classmethod
def all_integrated_stats(cls, basis: Optional[Basis]=None) -> Dict[str, List[str]]:
"""
Provides a dictionary whose keys are stat names and whose values are a list of variables
two which that stat should be applied. This combines the inverse of forcing_integrated_vars()
and lsm_integrated_vars().
"""
integrated_stats = cls.lsm_integrated_stats(basis=basis)
for var, varstats in cls.forcing_integrated_vars(basis=basis).items():
for stat in varstats:
if stat not in integrated_stats:
integrated_stats[stat] = []
integrated_stats[stat].append(var)
return integrated_stats
......@@ -49,7 +49,7 @@ def spinup(config: ConfigBase, meta_steps: Mapping[str, Step]) -> List[Step]:
steps += time_integrate_results(config, window, basis=Basis.BASIN)
# Compute monthly fits over the fit period
for param in config.lsm_rp_vars(basis=Basis.BASIN) + config.forcing_rp_vars(basis=Basis.BASIN):
for param in config.lsm_rp_vars(basis=Basis.BASIN):
for month in all_months:
steps += all_fits.require(actions.fit_var(config, param=param, month=month, basis=Basis.BASIN))
......@@ -134,6 +134,7 @@ def monthly_observed(config: ConfigBase, yearmon: str, meta_steps: Mapping[str,
config.lsm_integrated_stats(basis=Basis.BASIN),
yearmon=yearmon,
window=window,
forcing=False,
basis=Basis.BASIN)
if yearmon not in config.result_fit_yearmons():
......@@ -185,7 +186,8 @@ def monthly_forecast(config: ConfigBase, yearmon: str, meta_steps: Mapping[str,
target=target,
member=member,
window=window,
basis=Basis.BASIN)
basis=Basis.BASIN,
forcing=False)
steps += compute_basin_loss_factors(config.workspace(), yearmon=yearmon, target=target, member=member)
steps += compute_plant_losses(config.workspace(), yearmon=yearmon, target=target, member=member)
......@@ -333,7 +335,7 @@ def compute_plant_losses(workspace: DefaultWorkspace,
basis=Basis.POWER_PLANT)
basin_results = workspace.results(yearmon=yearmon, window=1, basis=Basis.BASIN, target=target, member=member)
forcing = workspace.forcing(yearmon=yearmon, target=target, member=member)
forcing = workspace.forcing(yearmon=yearmon, target=target, member=member, window=1)
rp = workspace.return_period(yearmon=yearmon, target=target, member=member, window=1)
loss_factors = workspace.basin_loss_factors(yearmon=yearmon, target=target, member=member)
......
......@@ -55,13 +55,15 @@ def monthly_observed(config: Config, yearmon: str, meta_steps: Dict[str, Step])
# Do time integration
for window in config.integration_windows():
steps += time_integrate(config.workspace(), config.lsm_integrated_stats(), yearmon=yearmon, window=window)
steps += time_integrate(config.workspace(), config.lsm_integrated_stats(), forcing = False, yearmon=yearmon, window=window)
steps += time_integrate(config.workspace(), config.forcing_integrated_stats(), forcing = True, yearmon=yearmon, window=window)
# Compute return periods
for window in [1] + config.integration_windows():
steps += compute_return_periods(config.workspace(),
result_vars=config.lsm_rp_vars() if window==1 else config.lsm_integrated_var_names(),
forcing_vars=config.forcing_rp_vars() if window==1 else None,
result_vars=config.lsm_rp_vars() if window == 1 else config.lsm_integrated_var_names(),
forcing_vars=config.forcing_rp_vars() if window==1 else config.forcing_integrated_var_names(),
state_vars=config.state_rp_vars() if window==1 else None,
yearmon=yearmon,
window=window)
......@@ -140,15 +142,15 @@ def monthly_forecast(config: Config,
for window in config.integration_windows():
# Time integrate the results
steps += time_integrate(config.workspace(), config.lsm_integrated_stats(),
window=window, yearmon=yearmon, target=target,
member=member)
steps += time_integrate(config.workspace(), config.lsm_integrated_stats(), forcing = False, yearmon=yearmon, window=window, member=member, target=target)
steps += time_integrate(config.workspace(), config.forcing_integrated_stats(), forcing = True, yearmon=yearmon, window=window, member=member, target=target)
# Compute return periods
for window in [1] + config.integration_windows():
steps += compute_return_periods(config.workspace(),
forcing_vars=config.forcing_rp_vars() if window==1 else None,
forcing_vars=config.forcing_rp_vars() if window==1 else config.forcing_integrated_var_names(),
result_vars=config.lsm_rp_vars() if window==1 else config.lsm_integrated_var_names(),
state_vars=config.state_rp_vars() if window==1 else None,
yearmon=yearmon,
window=window,
target=target,
......
......@@ -422,8 +422,13 @@ class DefaultWorkspace:
return self.make_path('state', sector=sector, yearmon=yearmon, member=member, target=target, window=None, method=method)
def forcing(self, *, yearmon: str, member: Optional[str]=None, target: Optional[str]=None) -> str:
return self.make_path('forcing', yearmon=yearmon, member=member, target=target, window=None)
def forcing(self, *,
yearmon: str,
window: int,
member: Optional[str]=None,
target: Optional[str]=None,
basis: Optional[Basis]=None) -> str:
return self.make_path('forcing', yearmon=yearmon, member=member, target=target, window=window, basis=basis)
def results(self, *,
sector: Optional[Sector]=None,
......
......@@ -38,10 +38,15 @@ def spinup(config, meta_steps):
steps += compute_climate_norms(config)
steps += run_lsm_with_monthly_norms(config, years=100)
forcing_1mo = Step.make_empty()
for yearmon in config.historical_yearmons():
steps += config.observed_data().prep_steps(yearmon=yearmon)
steps += create_forcing_file(config.workspace(), config.observed_data(), yearmon=yearmon)
for step in create_forcing_file(config.workspace(), config.observed_data(), yearmon=yearmon):
forcing_1mo = forcing_1mo.merge(step)
steps += create_tag(name=config.workspace().tag('spinup_1mo_forcing'),
dependencies=forcing_1mo.targets)
forcing_1mo.replace_targets_with_tag_file(config.workspace().tag('spinup_1mo_forcing'))
steps.append(forcing_1mo)
steps += run_lsm_from_final_norm_state(config)
for month in all_months:
......@@ -61,16 +66,17 @@ def spinup(config, meta_steps):
# Time-integrate the variables
for window in config.integration_windows():
steps += time_integrate_forcing(config, window)
steps += time_integrate_results(config, window)
# Compute monthly fits (and then anomalies) over the fit period
for param in config.lsm_rp_vars() + config.forcing_rp_vars():
for param in config.lsm_rp_vars() + config.forcing_rp_vars() + config.state_rp_vars():
for month in all_months:
steps += all_fits.require(fit_var(config, param=param, month=month))
# Compute fits for time-integrated parameters
for param in config.lsm_integrated_vars().keys():
for stat in config.lsm_integrated_vars()[param]:
for param in {**config.lsm_integrated_vars(), **config.forcing_integrated_vars()}.keys():
for stat in {**config.lsm_integrated_vars(), **config.forcing_integrated_vars()}[param]:
for window in config.integration_windows():
assert window > 1
for month in all_months:
......@@ -81,7 +87,8 @@ def spinup(config, meta_steps):
for yearmon in config.historical_yearmons()[window-1:]:
steps += compute_return_periods(config.workspace(),
result_vars=config.lsm_rp_vars() if window == 1 else config.lsm_integrated_var_names(),
forcing_vars=config.forcing_rp_vars() if window == 1 else None,
forcing_vars=config.forcing_rp_vars() if window == 1 else config.forcing_integrated_var_names(),
state_vars=config.state_rp_vars() if window==1 else None,
yearmon=yearmon,
window=window)
......@@ -209,7 +216,7 @@ def run_lsm_from_final_norm_state(config: Config) -> List[Step]:
# allow restarting in case of failure. But the runtime becomes dominated by the
# R startup and I/O, and takes about 5 seconds / iteration instead of 1 second /iteration.
run_lsm = wsim_lsm(
forcing=[config.workspace().forcing(yearmon=date_range(config.historical_yearmons()))],
forcing=[config.workspace().forcing(yearmon=date_range(config.historical_yearmons()), window=1)],
state=config.workspace().spinup_state(yearmon=initial_yearmon),
elevation=config.static_data().elevation(),
flowdir=config.static_data().flowdir(),
......@@ -248,7 +255,6 @@ def run_lsm_from_mean_spinup_state(config: Config) -> List[Step]:
first_month = int(first_timestep[4:])
postprocess_steps = list(itertools.chain(*[config.result_postprocess_steps(yearmon=yearmon)
for yearmon in config.historical_yearmons()]))
make_initial_state = Step(
comment="Create initial state file",
targets=config.workspace().state(yearmon=first_timestep),
......@@ -266,7 +272,7 @@ def run_lsm_from_mean_spinup_state(config: Config) -> List[Step]:
run_lsm = wsim_lsm(
comment="LSM run from mean spinup state",
forcing=[config.workspace().forcing(yearmon=date_range(config.historical_yearmons()))],
forcing=[config.workspace().forcing(yearmon=date_range(config.historical_yearmons()), window=1)],
state=config.workspace().state(yearmon=first_timestep),
elevation=config.static_data().elevation(),
flowdir=config.static_data().flowdir(),
......@@ -289,16 +295,49 @@ def run_lsm_from_mean_spinup_state(config: Config) -> List[Step]:
]
def time_integrate_forcing(config:Config, window: int, *, basis: Optional[Basis]=None) -> List[Step]:
"""
Integrate forcing variables over the given time window
"""
yearmons_in = config.historical_yearmons()
yearmons_out = yearmons_in[window-1:]
integrate = wsim_integrate(
inputs= [read_vars(config.workspace().forcing(window=1, yearmon=date_range(yearmons_in), basis=basis),
*config.forcing_integrated_vars(basis=basis).keys())
],
window=window,
stats=[stat + '::' + ','.join(varname) for stat, varname in config.forcing_integrated_stats(basis=basis).items()],
attrs=[attrs.integration_window(var='*', months=window)],
output=config.workspace().forcing(yearmon=date_range(yearmons_out),
window=window, basis=basis)
)
tag_name = config.workspace().tag('{}spinup_{}mo_forcing'.format((basis.value + '_' if basis else ''), window))
tag_steps = create_tag(name=tag_name, dependencies=integrate.targets)
integrate.replace_targets_with_tag_file(tag_name)
integrate.replace_dependencies(
config.workspace().tag('{}spinup_1mo_forcing'.format((basis.value + '_') if basis else '')))
return [
integrate,
*tag_steps
]
def time_integrate_results(config: Config, window: int, *, basis: Optional[Basis]=None) -> List[Step]:
"""
Integrate all LSM results and any specified forcing variables with the given time window
Integrate specified LSM results over the given time window
"""
yearmons_in = config.historical_yearmons()
yearmons_out = yearmons_in[window-1:]
integrate = wsim_integrate(
inputs=read_vars(config.workspace().results(window=1, yearmon=date_range(yearmons_in), basis=basis),
*config.lsm_integrated_vars(basis=basis).keys()),
inputs=[read_vars(config.workspace().results(window=1, yearmon=date_range(yearmons_in), basis=basis),
*config.lsm_integrated_vars(basis=basis).keys())
],
window=window,
stats=[stat + '::' + ','.join(varname) for stat, varname in config.lsm_integrated_stats(basis=basis).items()],
attrs=[attrs.integration_window(var='*', months=window)],
......
......@@ -89,21 +89,18 @@ run <- function(static, state, forcing) {
obs <- list(
extent= state$extent,
#dayLength= forcing$daylength,
dWdt= dWdt,
E= E,
EmPET= E - E0,
P_net= P,
PET= E0,
PETmE= E0 - E,
#Pr= forcing$Pr,
RO_mm= revised_runoff,
RO_m3= revised_runoff*area_m2/1000,
Runoff_mm= R,
Runoff_m3= R*area_m2/1000,
Sa= Sa,
Sm= ifelse(is.na(Sa), NA, Sm),
#T= forcing$T,
Ws= Ws_ave
)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment