Source code for pywatershed.hydrology.obsin_flow_node
import pandas as pd
from pywatershed.base.control import Control
from pywatershed.base.flow_graph import FlowNode, FlowNodeMaker
from pywatershed.base.parameters import Parameters
from pywatershed.constants import nan, zero
[docs]
class ObsInFlowNode(FlowNode):
"""A FlowNode that takes inflows but returns observed/specified flows.
This FlowNode replicates the obsin and obsout seg functionality in PRMS but
does so by inserting a new node in a FlowGraph rather than altering the
flow on an existing node. This node is NOT mass conservative and tracks
a sink_source term to describe mass lost and created at each subtimestep.
See :class:`FlowGraph` for examples and discussion.
"""
[docs]
def __init__(
self,
control: Control,
node_obs_data: pd.Series,
):
"""Initialize an ObsInFlowNode.
Args:
control: a Control object.
node_obs_data: A pandas Series object of observations at this
location given by pyPRMS.Streamflow. Negative flow observations
result in pass through of inflows.
"""
self.control = control
self._node_obs_data = node_obs_data
self._sink_source = zero
return
[docs]
def prepare_timestep(self):
ymd = self.control.current_datetime.strftime("%Y-%m-%d")
self._seg_outflow = self._node_obs_data[ymd]
self._sink_source_sum = zero
return
[docs]
def calculate_subtimestep(
self,
isubstep: int,
inflow_upstream: float,
inflow_lateral: float,
):
inflow = inflow_upstream + inflow_lateral
if self._seg_outflow >= zero:
self._sink_source_sum += self._seg_outflow - inflow
else:
self._seg_outflow = inflow
self._sink_source_sum += zero
# <
self._sink_source = self._sink_source_sum / (isubstep + 1)
return
[docs]
def finalize_timestep(self):
return
[docs]
def advance(self):
return
@property
def outflow(self):
return self._seg_outflow
@property
def outflow_substep(self):
return self._seg_outflow
@property
def storage_change(self):
return zero
@property
def storage(self):
return nan
@property
def sink_source(self):
"""Average sink and source through the last subtimestep
Sink is negative, indicating that incoming flow is being discarded (if
it were being stored, the storage change would be the opposite sign).
Source is positive, indicating that incoming flow is being augmented
(if it were being stored, the storage change would be the opposite
sign).
"""
return self._sink_source
[docs]
class ObsInFlowNodeMaker(FlowNodeMaker):
"""A FlowNodeMaker for ObsInFlowNode.
See :class:`FlowGraph` for related examples and discussion.
"""
[docs]
def __init__(
self,
parameters: Parameters,
obs_data: pd.DataFrame,
) -> None:
"""Initialize a ObsInFlowNodeMaker.
Args:
parameters: A pywatershed Parameters object.
obs_data: A pandas DataFrame of observations given by
pyPRMS.Streamflow.
"""
self.name = "ObsInNodeMaker"
self._parameters = parameters
self._obs_data = obs_data
[docs]
def get_node(self, control: Control, index: int):
node_poi_id = self._parameters.parameters["poi_gage_id"][index]
return ObsInFlowNode(control, self._obs_data[node_poi_id])