Skip to content

Commit

Permalink
Include warnings in set_components
Browse files Browse the repository at this point in the history
  • Loading branch information
enekomartinmartinez committed Dec 23, 2023
1 parent c0933c5 commit 1cd0cba
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 32 deletions.
4 changes: 3 additions & 1 deletion docs/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ Documentation

Performance
~~~~~~~~~~~
- Improved performace of :py:class:`pysd.py_backend.output.DataFrameHandler` by creating the dataframe at the end of the run (:issue:`374`). (`@easyas314159 <https://github.com/easyas314159>`_ and `@enekomartinmartinez <https://github.com/enekomartinmartinez>`_)
- Improved performace of :py:class:`pysd.py_backend.output.DataFrameHandler` by creating the dataframe at the end of the run (:issue:`374` and :issue:`330`). (`@easyas314159 <https://github.com/easyas314159>`_ and `@enekomartinmartinez <https://github.com/enekomartinmartinez>`_)

Internal Changes
~~~~~~~~~~~~~~~~
- Move old :py:meth:`pysd.py_backend.model.Macro.set_components` to :py:meth:`pysd.py_backend.model.Macro._set_components`, and create new method with the same name without the `new` argument. (`@enekomartinmartinez <https://github.com/enekomartinmartinez>`_)
- Move old :py:meth:`pysd.py_backend.model.Macro.set_stateful` to :py:meth:`pysd.py_backend.model.Macro._set_stateful`. (`@enekomartinmartinez <https://github.com/enekomartinmartinez>`_)
- Make integration tests filter only specific warnings. (`@enekomartinmartinez <https://github.com/enekomartinmartinez>`_)
- Include warnings in :py:meth:`pysd.py_backend.model.Macro.set_components` when changing the behaviour of the component (:issue:`58`). (`@enekomartinmartinez <https://github.com/enekomartinmartinez>`_)


v3.12.0 (2023/10/02)
--------------------
Expand Down
24 changes: 24 additions & 0 deletions pysd/py_backend/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,10 @@ def _set_components(self, params, new):
obj
).set_values(value)

if not isinstance(value, pd.Series):
warnings.warn(
"Replacing interpolation data with constant values.")

# Update dependencies
if func_type == "Data":
if isinstance(value, pd.Series):
Expand All @@ -1140,18 +1144,38 @@ def _set_components(self, params, new):

continue

if func_type == "Stateful":
warnings.warn(
"Replacing the value of Stateful variable with "
"an expression. To set initial conditions use "
"`set_initial_condition` instead..."
)

if isinstance(value, pd.Series):
if func_type == "Constant":
warnings.warn(
"Replacing a constant value with a "
"time-dependent value. The value will be "
"interpolated over time."
)
new_function, deps = self._timeseries_component(
value, dims)
self._dependencies[func_name] = deps
elif callable(value):
if func_type == "Constant":
warnings.warn(
"Replacing a constant value with a callable. "
"The value may not be constant anymore."
)
new_function = value
# Using step cache adding time as dependency
# TODO it would be better if we can parse the content
# of the function to get all the dependencies
self._dependencies[func_name] = {"time": 1}

else:
if func_type != "Constant":
warnings.warn("Replacing a variable by a constant value.")
new_function = self._constant_component(value, dims)
self._dependencies[func_name] = {}

Expand Down
91 changes: 65 additions & 26 deletions tests/pytest_pysd/pytest_pysd.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,14 @@ def test_set_timeseries_parameter(self, model):
index=timeseries,
data=(50 + np.random.rand(len(timeseries)).cumsum())
)
res = model.run(
params={"room_temperature": temp_timeseries},
return_columns=["room_temperature"],
return_timestamps=timeseries,
)
warn_message = "Replacing a constant value with a time-dependent "\
"value. The value will be interpolated over time."
with pytest.warns(UserWarning, match=warn_message):
res = model.run(
params={"room_temperature": temp_timeseries},
return_columns=["room_temperature"],
return_timestamps=timeseries,
)
assert (res["room_temperature"] == temp_timeseries).all()

@pytest.mark.parametrize("model_path", [test_model])
Expand All @@ -323,7 +326,11 @@ def test_set_timeseries_parameter_inline(self, model):
index=timeseries,
data=(50 + np.random.rand(len(timeseries)).cumsum())
)
model.components.room_temperature = temp_timeseries
warn_message = "Replacing a constant value with a time-dependent "\
"value. The value will be interpolated over time."
with pytest.warns(UserWarning, match=warn_message):
model.components.room_temperature = temp_timeseries

res = model.run(
return_columns=["room_temperature"],
return_timestamps=timeseries,
Expand Down Expand Up @@ -352,8 +359,10 @@ def test_set_components_warnings(self, model):
def test_set_components_with_function(self, model):
def test_func():
return 5

model.set_components({"Room Temperature": test_func})
warn_message = "Replacing a constant value with a callable. "\
"The value may not be constant anymore."
with pytest.warns(UserWarning, match=warn_message):
model.set_components({"Room Temperature": test_func})
res = model.run(return_columns=["Room Temperature"])
assert test_func() == res["Room Temperature"].iloc[0]

Expand Down Expand Up @@ -549,12 +558,15 @@ def test_set_subscripted_timeseries_parameter_with_constant(self, model):
xr_series = [xr.DataArray(val, coords, dims) for val in val_series]

temp_timeseries = pd.Series(index=timeseries, data=val_series)
res = model.run(
params={"initial_values": temp_timeseries, "final_time": 10},
return_columns=["initial_values"],
return_timestamps=timeseries,
flatten_output=False
)
warn_message = "Replacing a constant value with a time-dependent "\
"value. The value will be interpolated over time."
with pytest.warns(UserWarning, match=warn_message):
res = model.run(
params={"initial_values": temp_timeseries, "final_time": 10},
return_columns=["initial_values"],
return_timestamps=timeseries,
flatten_output=False
)

assert np.all(
[
Expand Down Expand Up @@ -583,8 +595,11 @@ def test_set_subscripted_timeseries_parameter_with_partial_xarray(self,
).cumsum()]
temp_timeseries = pd.Series(index=timeseries, data=val_series)
out_series = [out_b + val for val in val_series]
model.set_components({"initial_values": temp_timeseries,
"final_time": 10})
warn_message = "Replacing a constant value with a time-dependent "\
"value. The value will be interpolated over time."
with pytest.warns(UserWarning, match=warn_message):
model.set_components({"initial_values": temp_timeseries,
"final_time": 10})
res = model.run(
return_columns=["initial_values"], flatten_output=False)
assert np.all(
Expand All @@ -610,12 +625,15 @@ def test_set_subscripted_timeseries_parameter_with_xarray(self, model):
data=[init_val + rd for rd in np.random.rand(len(timeseries)
).cumsum()],
)
res = model.run(
params={"initial_values": temp_timeseries, "final_time": 10},
return_columns=["initial_values"],
return_timestamps=timeseries,
flatten_output=False
)
warn_message = "Replacing a constant value with a time-dependent "\
"value. The value will be interpolated over time."
with pytest.warns(UserWarning, match=warn_message):
res = model.run(
params={"initial_values": temp_timeseries, "final_time": 10},
return_columns=["initial_values"],
return_timestamps=timeseries,
flatten_output=False
)

assert np.all(
[
Expand Down Expand Up @@ -947,10 +965,24 @@ def test_set_initial_value_subscripted_value_with_numpy_error(self, model):
with pytest.raises(TypeError):
model.set_initial_value(new_time + 2, {'_integ_stock_a': input3})

@pytest.mark.parametrize("model_path", [test_model])
def test_replace_stateful(self, model):
warn_message = "Replacing the value of Stateful variable with "\
"an expression. To set initial conditions use "\
"`set_initial_condition` instead..."
with pytest.warns(UserWarning, match=warn_message):
model.components.teacup_temperature = 3

stocks = model.run()
assert np.all(stocks["Teacup Temperature"] == 3)

@pytest.mark.parametrize("model_path", [test_model])
def test_replace_element(self, model):
stocks1 = model.run()
model.components.characteristic_time = lambda: 3
warn_message = "Replacing a constant value with a callable. "\
"The value may not be constant anymore."
with pytest.warns(UserWarning, match=warn_message):
model.components.characteristic_time = lambda: 3
stocks2 = model.run()
assert stocks1["Teacup Temperature"].loc[10]\
> stocks2["Teacup Temperature"].loc[10]
Expand Down Expand Up @@ -1341,7 +1373,10 @@ def test_no_crosstalk(self, _root):
model_2 = pysd.read_vensim(
_root.joinpath("test-models/samples/SIR/SIR.mdl"))

model_1.components.initial_time = lambda: 10
warn_message = "Replacing a constant value with a callable. "\
"The value may not be constant anymore."
with pytest.warns(UserWarning, match=warn_message):
model_1.components.initial_time = lambda: 10
assert model_2.components.initial_time != 10

# check that the model time is not shared between the two objects
Expand Down Expand Up @@ -1480,9 +1515,13 @@ def test_change_constant_pipe(self, model):

# we should ensure that the constant_cache is removed
# when passing new param
out2 = model.run(params={"constant1": new_var})
warn_message = "Replacing a constant value with a "\
"time-dependent value. The value will "\
"be interpolated over time."
with pytest.warns(UserWarning, match=warn_message):
out2 = model.run(params={"constant1": new_var})

assert not np.all(out1 - out2 == 0)
assert not np.array_equal(out1, out2)

for key in pipe:
assert model.cache_type[key] == "step"
Expand Down
5 changes: 4 additions & 1 deletion tests/pytest_pysd/pytest_select_submodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,10 @@ def test_select_submodel(self, model, variables, modules,
assert var in str(record[-1].message)
assert np.any(np.isnan(model.run()))
# redefine dependencies
assert not np.any(np.isnan(model.run(params=dep_vars)))
warn_message = "Replacing a variable by a constant value."
with pytest.warns(UserWarning, match=warn_message):
out = model.run(params=dep_vars)
assert not np.any(np.isnan(out))

# select submodel using contour values
model.reload()
Expand Down
9 changes: 5 additions & 4 deletions tests/pytest_types/data/pytest_data_with_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,11 @@ def test_get_data_and_run(self, model, expected):
expected)

def test_modify_data(self, model, expected):
out = model.run(params={
"var1": pd.Series(index=[1, 3, 7], data=[10, 20, 30]),
"var2": 10
})
with pytest.warns(UserWarning, match="Replacing .*"):
out = model.run(params={
"var1": pd.Series(index=[1, 3, 7], data=[10, 20, 30]),
"var2": 10
})

assert (out["var2"] == 10).all()
assert (
Expand Down

0 comments on commit 1cd0cba

Please sign in to comment.