Skip to content

Commit

Permalink
Solve active_initial dependencies bug
Browse files Browse the repository at this point in the history
  • Loading branch information
enekomartinmartinez committed Dec 16, 2021
1 parent 312fb2b commit 6cc60ed
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 7 deletions.
9 changes: 6 additions & 3 deletions pysd/py_backend/statefuls.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ def _get_initialize_order(self):
self.stateful_initial_dependencies = {
ext: set()
for ext in self.components._dependencies
if ext.startswith("_")
if (ext.startswith("_") and not ext.startswith("_active_initial_"))
}
for element in self.stateful_initial_dependencies:
self._get_full_dependencies(
Expand All @@ -712,8 +712,10 @@ def _get_initialize_order(self):
# get the full dependencies of stateful objects taking into account
# only other objects
current_deps = {
element: [dep for dep in deps if dep.startswith("_")]
for element, deps in self.stateful_initial_dependencies.items()
element: [
dep for dep in deps
if dep in self.stateful_initial_dependencies
] for element, deps in self.stateful_initial_dependencies.items()
}

# get initialization order of the stateful elements
Expand Down Expand Up @@ -887,6 +889,7 @@ def _isdynamic(self, dependencies):
return True
for dep in dependencies:
if dep.startswith("_") and not dep.startswith("_initial_")\
and not dep.startswith("_active_initial_")\
and not dep.startswith("__"):
return True
return False
Expand Down
92 changes: 89 additions & 3 deletions pysd/translation/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,10 @@ def build_element(element, subscript_dict):
# external objecets via .add method
py_expr_no_ADD = ["ADD" not in py_expr for py_expr in element["py_expr"]]

if sum(py_expr_no_ADD) > 1 and element["kind"] not in [
if element["kind"] == "dependencies":
# element only used to update dependencies
return ""
elif sum(py_expr_no_ADD) > 1 and element["kind"] not in [
"stateful",
"external",
"external_add",
Expand Down Expand Up @@ -817,6 +820,63 @@ def _merge_dependencies(current, new):
current[dep] = new[dep]


def build_active_initial_deps(identifier, arguments, deps):
"""
Creates new model element dictionaries for the model elements associated
with a stock.
Parameters
----------
identifier: str
The python-safe name of the stock.
expression: str
Formula which forms the regular value for active initial.
initial: str
Formula which forms the initial value for active initial.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
reference: str
A reference to the gost variable that defines the dependencies.
new_structure: list
List of additional model element dictionaries.
"""
deps = build_dependencies(
deps,
{
"initial": [arguments[1]],
"step": [arguments[0]]
})

py_name = "_active_initial_%s" % identifier

# describe the stateful object
new_structure = [{
"py_name": py_name,
"parent_name": "",
"real_name": "",
"doc": "",
"py_expr": "",
"unit": "",
"lims": "",
"eqn": "",
"subs": "",
"merge_subs": None,
"dependencies": deps,
"kind": "dependencies",
"arguments": "",
}]

return py_name, new_structure


def add_stock(identifier, expression, initial_condition, subs, merge_subs,
deps):
"""
Expand All @@ -842,6 +902,9 @@ def add_stock(identifier, expression, initial_condition, subs, merge_subs,
List of the final subscript range of the python array after
merging with other objects.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
reference: str
Expand Down Expand Up @@ -974,6 +1037,9 @@ def add_delay(identifier, delay_input, delay_time, initial_value, order,
List of the final subscript range of the python array after
merging with other objects.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
reference: str
Expand Down Expand Up @@ -1092,6 +1158,9 @@ def add_delay_f(identifier, delay_input, delay_time, initial_value, deps):
We initialize the stocks with equal values so that the outflow in
the first timestep is equal to this value.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
reference: str
Expand Down Expand Up @@ -1182,6 +1251,9 @@ def add_n_delay(identifier, delay_input, delay_time, initial_value, order,
List of the final subscript range of the python array after
merging with other objects.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
reference: str
Expand Down Expand Up @@ -1300,6 +1372,9 @@ def add_forecast(identifier, forecast_input, average_time, horizon,
List of the final subscript range of the python array after
merging with other objects.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
reference: str
Expand Down Expand Up @@ -1403,6 +1478,9 @@ def add_sample_if_true(identifier, condition, actual_value, initial_value,
List of the final subscript range of the python array after
merging with other objects.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
reference: str
Expand Down Expand Up @@ -1507,8 +1585,10 @@ def add_n_smooth(identifier, smooth_input, smooth_time, initial_value, order,
list of expressions, and collectively define the shape of the output
merge_subs: list of strings
List of the final subscript range of the python array after
.
List of the final subscript range of the python array after.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
Expand Down Expand Up @@ -1628,6 +1708,9 @@ def add_n_trend(identifier, trend_input, average_time, initial_trend,
List of the final subscript range of the python array after
merging with other objects.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
reference: str
Expand Down Expand Up @@ -1714,6 +1797,9 @@ def add_initial(identifier, value, deps):
The expression which will be evaluated, and the first value of
which returned.
deps: dict
The dictionary with all the denpendencies in the expression.
Returns
-------
reference: str
Expand Down
6 changes: 6 additions & 0 deletions pysd/translation/vensim/vensim2py.py
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,12 @@ def visit_call(self, n, vc):
arguments += ["dim=" + str(tuple(self.apply_dim))]
self.apply_dim = set()

if re.match(r"active(_|\s)initial", function_name):
ghost_name, new_structure = builder.build_active_initial_deps(
element["py_name"], arguments, element["dependencies"])
element["dependencies"] = {ghost_name: 1}
self.new_structure += new_structure

return builder.build_function_call(
functions[function_name],
arguments, element["dependencies"])
Expand Down
4 changes: 4 additions & 0 deletions tests/integration_test_vensim_pathway.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def test_active_initial(self):
output, canon = runner(test_models + '/active_initial/test_active_initial.mdl')
assert_frames_close(output, canon, rtol=rtol)

def test_active_initial_circular(self):
output, canon = runner(test_models + '/active_initial_circular/test_active_initial_circular.mdl')
assert_frames_close(output, canon, rtol=rtol)

def test_arguments(self):
with warnings.catch_warnings():
warnings.simplefilter("ignore")
Expand Down

0 comments on commit 6cc60ed

Please sign in to comment.