From 395b826fb7f15699b60d85b568865243a5ee2fe5 Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 27 Feb 2024 23:41:53 -0500 Subject: [PATCH 1/9] Implement ifthenelse to piecewise --- mira/sources/system_dynamics/pysd.py | 25 +++++++++++++++++++++++++ mira/sources/system_dynamics/vensim.py | 5 ++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/mira/sources/system_dynamics/pysd.py b/mira/sources/system_dynamics/pysd.py index 985e560c5..d6a99f18a 100644 --- a/mira/sources/system_dynamics/pysd.py +++ b/mira/sources/system_dynamics/pysd.py @@ -433,3 +433,28 @@ def preprocess_expression_text(expr_text): .lower() ) return expr_text + + +def ifthenelse_to_piecewise(expr_text): + """Convert Vensim if then else expression to sympy Piecewise string + + Parameters + ---------- + expr_text : str + The string expression + + Returns + ------- + : str + The sympy Piecewise expression as a string + """ + # We find the three components and rearrange them for piecewise + if_then_else_regex = r"IF THEN ELSE\((.*),(.*)\,(.*)\)" + match = re.search(if_then_else_regex, expr_text) + if match: + condition, then, else_ = match.groups() + piecewise = f"Piecewise(({then}, {condition}), ({else_}, True))" + # We also need to replace single = with == but make sure if there is a ==, we don't replace + # it and also, we need to make sure we don't replace <= or >= + piecewise = re.sub(r"(?)=(?!=)", "==", piecewise) + return piecewise diff --git a/mira/sources/system_dynamics/vensim.py b/mira/sources/system_dynamics/vensim.py index febfe6615..7b6ca1a9e 100644 --- a/mira/sources/system_dynamics/vensim.py +++ b/mira/sources/system_dynamics/vensim.py @@ -17,7 +17,7 @@ from mira.metamodel import TemplateModel from mira.sources.system_dynamics.pysd import ( - template_model_from_pysd_model, + template_model_from_pysd_model, ifthenelse_to_piecewise ) __all__ = ["template_model_from_mdl_file", "template_model_from_mdl_url"] @@ -141,8 +141,7 @@ def extract_vensim_variable_expressions(model_text): # "INTEG" is the keyword used to define a state/stock # however, we can't yet handle if/then/else constructs, so we skip them if "if then else" in text_expression.lower(): - expression_map[old_var_name] = "0" - continue + text_expression = ifthenelse_to_piecewise(text_expression) # If we come across a state, get the expression for the state only From 63741919da7d0866b4b899a11704fa61ee46c679 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Wed, 28 Feb 2024 13:40:52 +0100 Subject: [PATCH 2/9] First pass at parsing out initial values --- mira/sources/system_dynamics/pysd.py | 22 ++++++++++++-- mira/sources/system_dynamics/vensim.py | 41 ++++++++++++++++++++------ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/mira/sources/system_dynamics/pysd.py b/mira/sources/system_dynamics/pysd.py index d6a99f18a..d23f5e6ee 100644 --- a/mira/sources/system_dynamics/pysd.py +++ b/mira/sources/system_dynamics/pysd.py @@ -5,6 +5,8 @@ import copy import re +import typing as t +import logging import pandas as pd import sympy @@ -18,6 +20,8 @@ get_sympy, ) +logger = logging.getLogger(__name__) + CONTROL_VARIABLE_NAMES = { "FINALTIME", "INITIALTIME", @@ -36,7 +40,13 @@ SYMPY_FLOW_RATE_PLACEHOLDER = safe_parse_expr("xxplaceholderxx") -def template_model_from_pysd_model(pysd_model, expression_map, *, grounding_map=None) -> TemplateModel: +def template_model_from_pysd_model( + pysd_model, + expression_map, + *, + grounding_map=None, + initials_map: t.Optional[t.Dict[str, float]] = None, +) -> TemplateModel: """Given a model and its accompanying expression_map, extract information from the arguments to create an equivalent MIRA template model. @@ -48,6 +58,8 @@ def template_model_from_pysd_model(pysd_model, expression_map, *, grounding_map= Map of variable name to expression grounding_map: dict[str, Concept] A grounding map, a map from label to Concept + initials_map: dict[str, float] + A pre-populated mapping of initial values Returns ------- @@ -154,10 +166,16 @@ def template_model_from_pysd_model(pysd_model, expression_map, *, grounding_map= for state_initial_value, (state_name, state_concept) in zip( pysd_model.state, concepts.items() ): + if initials_map and (mapped_value := initials_map.get(state_name)): + initial = Initial( + concept=concepts[state_name].copy(deep=True), + expression=SympyExprStr(sympy.Float(mapped_value)), + ) # if the state value is not a number - if not isinstance(state_initial_value, int) and not isinstance( + elif not isinstance(state_initial_value, int) and not isinstance( state_initial_value, float ): + logger.warning(f"got non-numeric state value for {state_name}: {state_initial_value}") initial = Initial( concept=concepts[state_name].copy(deep=True), expression=SympyExprStr(sympy.Float("0")), diff --git a/mira/sources/system_dynamics/vensim.py b/mira/sources/system_dynamics/vensim.py index 7b6ca1a9e..65390e38d 100644 --- a/mira/sources/system_dynamics/vensim.py +++ b/mira/sources/system_dynamics/vensim.py @@ -10,12 +10,13 @@ import tempfile import re +import typing as t import pysd from pysd.translators.vensim.vensim_file import VensimFile import requests -from mira.metamodel import TemplateModel +from mira.metamodel import TemplateModel, Initial from mira.sources.system_dynamics.pysd import ( template_model_from_pysd_model, ifthenelse_to_piecewise ) @@ -34,7 +35,7 @@ CONTROL_VARIABLES = {"SAVEPER", "FINAL TIME", "INITIAL TIME", "TIME STEP"} -def template_model_from_mdl_file(fname, *, grounding_map=None) -> TemplateModel: +def template_model_from_mdl_file(fname, *, grounding_map=None, initials=None) -> TemplateModel: """Return a template model from a local Vensim file Parameters @@ -51,12 +52,18 @@ def template_model_from_mdl_file(fname, *, grounding_map=None) -> TemplateModel: """ pysd_model = pysd.read_vensim(fname) vensim_file = VensimFile(fname) - expression_map = extract_vensim_variable_expressions(vensim_file.model_text) - - return template_model_from_pysd_model(pysd_model, expression_map, grounding_map=grounding_map) + expression_map, initials_map = extract_vensim_variable_expressions(vensim_file.model_text) + if initials: + initials_map.update(initials) + return template_model_from_pysd_model( + pysd_model, + expression_map, + grounding_map=grounding_map, + initials_map=initials_map, + ) -def template_model_from_mdl_url(url, *, grounding_map=None) -> TemplateModel: +def template_model_from_mdl_url(url, *, grounding_map=None, initials=None) -> TemplateModel: """Return a template model from a Vensim file provided by an url Parameters @@ -79,7 +86,7 @@ def template_model_from_mdl_url(url, *, grounding_map=None) -> TemplateModel: with temp_file as file: file.write(data) - return template_model_from_mdl_file(temp_file.name, grounding_map=grounding_map) + return template_model_from_mdl_file(temp_file.name, grounding_map=grounding_map, initials=initials) # look past control section @@ -143,19 +150,35 @@ def extract_vensim_variable_expressions(model_text): if "if then else" in text_expression.lower(): text_expression = ifthenelse_to_piecewise(text_expression) + # If there's an INTEG expression, we can extract an initial value, + # otherwise, it is none. + initial = None + # If we come across a state, get the expression for the state only # For the hackathon Vensim file, we can use a new regex that gets not only the expression # but the initial value as well. Because when pysd ingests the hackathon Vensim file, # it will have 44 initial values for only 19 states. if "INTEG" in text_expression: - text_expression = re.search(r"\(([^,]+),", text_expression).group(1) + match = re.search(r"\(([^,]+),\s*(.*)?\)", text_expression) + text_expression = match.group(1) + initial = match.group(2) + print(old_var_name, initial) expression_map[old_var_name] = text_expression + # CTH: I couldn't find where this normalization happens + # between the vensim and pysd code, so I added it again here. + # also, the initial value might be an expression that needs normalizing + initial_values[_norm(old_var_name)] = initial and _norm(initial) + # remove any control variables listed past the control section that were added to the # expression map for control_var in CONTROL_VARIABLES: expression_map.pop(control_var) - return expression_map + return expression_map, initial_values + + +def _norm(s): + return s.lower().replace(" ", "_") From 843a7df08f394c901241bc7adb76dd5ea9e97560 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Wed, 28 Feb 2024 14:40:14 +0100 Subject: [PATCH 3/9] Handle lookup tables --- mira/sources/system_dynamics/pysd.py | 85 ++ mira/sources/system_dynamics/vensim.py | 6 +- .../scenario5/Scenario 5 Notebook.ipynb | 733 ++++++++++-------- .../scenario5/scenario_5_stockflow.json | 172 ++-- setup.cfg | 1 + tests/test_pysd.py | 15 + 6 files changed, 572 insertions(+), 440 deletions(-) create mode 100644 tests/test_pysd.py diff --git a/mira/sources/system_dynamics/pysd.py b/mira/sources/system_dynamics/pysd.py index d23f5e6ee..311f7364b 100644 --- a/mira/sources/system_dynamics/pysd.py +++ b/mira/sources/system_dynamics/pysd.py @@ -1,12 +1,14 @@ """This module implements parsing of a generic pysd model irrespective of source and source type and extracting its contents to create an equivalent MIRA template model. """ + __all__ = ["template_model_from_pysd_model"] import copy import re import typing as t import logging +from more_itertools import chunked import pandas as pd import sympy @@ -476,3 +478,86 @@ def ifthenelse_to_piecewise(expr_text): # it and also, we need to make sure we don't replace <= or >= piecewise = re.sub(r"(?)=(?!=)", "==", piecewise) return piecewise + + +def with_lookup_to_piecewise(expr_text: str) -> str: + """Convert a Vensim WITH LOOKUP expression to a piecewise function. + + The semantics of the ``WITH LOOKUP`` element are documented at + https://www.vensim.com/documentation/fn_with_lookup.html. + + For example, this could come from: + + .. code-block:: + + Infection Rate new arrivals= WITH LOOKUP ( + Time, + ( + [(0,0)-(500,100)],(0,0),(1,2),(2,1),(3,0),(4,2),(5,1),(6,2),(7,3),(8,6),(9,2),(10,7\ + ),(11,10),(12,4),(13,10),(14,5),(15, + 11),(16,14),(17,14),(18,26),(19,34),(20,35),(21,45),(22,55),(23,38),(24,34),(25,24)\ + ,(26,40),(27,16),(28,20),(29,12),(30, + 23),(31,14),(32,8),(33,14),(34,12),(35,5),(36,9),(37,6),(38,0),(39,0),(40,0),(1000,\ + 0) + )) + ~ Persons/Day + ~ | + + Which ends up being the text (after normalization from vensim) + + .. code-block:: + + with_lookup(time,([(0,0)-(500,100)],(0,0),(1,2),(2,1),(3,0),(4,2),(5,1),(6,2),\ + (7,3),(8,6),(9,2),(10,7),(11,10),(12,4),(13,10),(1000,0))) + """ + # there's a variety of ways this is written WITH LOOKUP, with_lookup, withlookup + # so just normalize it all out + expr_text = expr_text.strip().replace(" ", "").replace("_", "").lower() + if not expr_text.lower().startswith("withlookup"): + raise ValueError(expr_text) + expr_text = expr_text[len("withlookup") :].lstrip("(").rstrip(")") + # The first input is either a value or an input variable name + # The second input is a list of X,Y pairs + variable, second = (x.strip() for x in expr_text.split(",", 1)) + second: str = second.lstrip("(").rstrip(")") + + # This is an undocumented part of this function, but it appears that the + # list begins with some kind of definition of the form [(a,b)-(c,d)] + if second.startswith("["): + box, rest = second.lstrip("[").split("]", 1) + x1, y1, x2, y2 = [ + float(x.strip()) + for x in box.replace("(", "") + .replace(")", "") + .replace("-", ",") + .split(",") + ] + else: + rest = second + x1, y1, x2, y2 = [None] * 4 + + # TODO how to use x1, y1, x2, and y2? It's not clear if/how these are used + # to fill in gaps in the lookup table + + # This gets the list of x,y pairs in order from the lookup table + pairs = list( + chunked( + ( + float(x.strip()) + for x in rest.strip() + .replace("(", "") + .replace(")", "") + .split(",") + if x.strip() + ), + 2, + ) + ) + # print(variable, x1, y1, x2, y2, pairs) + + # construct the sympy string as a simple lookup, where the y + # value gets returned if the target variable is the given x value + # for each x,y pair + conditions = ",".join(f"({y}, {variable} == {x})" for x, y in pairs) + sympy_str = f"Piecewise({conditions})" + return sympy_str diff --git a/mira/sources/system_dynamics/vensim.py b/mira/sources/system_dynamics/vensim.py index 65390e38d..1d0fa5786 100644 --- a/mira/sources/system_dynamics/vensim.py +++ b/mira/sources/system_dynamics/vensim.py @@ -18,7 +18,7 @@ from mira.metamodel import TemplateModel, Initial from mira.sources.system_dynamics.pysd import ( - template_model_from_pysd_model, ifthenelse_to_piecewise + template_model_from_pysd_model, ifthenelse_to_piecewise, with_lookup_to_piecewise ) __all__ = ["template_model_from_mdl_file", "template_model_from_mdl_url"] @@ -150,6 +150,9 @@ def extract_vensim_variable_expressions(model_text): if "if then else" in text_expression.lower(): text_expression = ifthenelse_to_piecewise(text_expression) + if "with lookup" in text_expression.lower(): + text_expression = with_lookup_to_piecewise(text_expression) + # If there's an INTEG expression, we can extract an initial value, # otherwise, it is none. initial = None @@ -163,7 +166,6 @@ def extract_vensim_variable_expressions(model_text): match = re.search(r"\(([^,]+),\s*(.*)?\)", text_expression) text_expression = match.group(1) initial = match.group(2) - print(old_var_name, initial) expression_map[old_var_name] = text_expression diff --git a/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb b/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb index c281b2e80..b14ab6b0f 100644 --- a/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb +++ b/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb @@ -12,6 +12,7 @@ "from mira.sources.system_dynamics.vensim import template_model_from_mdl_url, template_model_from_mdl_file\n", "from mira.modeling.amr.stockflow import template_model_to_stockflow_json\n", "import os\n", + "from urllib.request import urlretrieve\n", "import json\n", "from pathlib import Path" ] @@ -41,204 +42,10 @@ "execution_count": 3, "id": "3229560d-21d1-4fde-a16a-8b3a37f3111f", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
namemapped_identifiersmapped_context
0susceptiblesido:0000514/susceptible populationNaN
1exposedapollosv:00000154/exposed populationNaN
2cumulative_cases_reportedncit:C175885/Number of CasesNaN
3infectious_asymptomaticsido:0000511/infected populationdisease_severity=ncit:C3833/Asymptomatic
4deadncit:C28554/DeadNaN
5isolated_asymptomaticsido:0000511/infected populationdisease_severity=ncit:C3833/Asymptomatic|isola...
6quarantined_asymptomaticsido:0000511/infected populationdisease_severity=ncit:C3833/Asymptomatic|quara...
7recovered_asymtomaticsido:0000592/immune populationdisease_severity=ncit:C3833/Asymptomatic
8isolated_symtomatics_mildido:0000511/infected populationisolation=ncit:C25549|disease_severity=ncit:C7...
9recovered_isolated_symptomatic_mildido:0000592/immune populationisolation=ncit:C25549|disease_severity=ncit:C7...
10infectious_symptomatics_mildido:0000511/infected populationdisease_severity=ncit:C70666
11quarantined_symtomatics_mildido:0000511/infected populationquarantined=ncit:C71902/Quarantine|disease_sev...
12recovered_mildido:0000592/immune populationdisease_severity=ncit:C70666
13incoming_demand_on_hospitalncit:C25179/HospitalizationNaN
14infected_sym_hospital_overflowido:0000511/infected populationhospitalization=ncit:C25179/Hospitalization|su...
15infected_sym_serious_hospitalido:0000511/infected populationhospitalization=ncit:C25179/Hospitalization|se...
16infected_sym_icu_overflowido:0000511/infected populationintensive_care_unit=ncit:C53511|surge_capacity...
17infected_sym_extreme_icuido:0000511/infected populationintensive_care_unit=ncit:C53511|immediately_li...
18recovered_from_hospitalido:0000592/immune populationhospitalization=ncit:C25179/Hospitalization|pa...
\n", - "
" - ], - "text/plain": [ - " name mapped_identifiers \\\n", - "0 susceptibles ido:0000514/susceptible population \n", - "1 exposed apollosv:00000154/exposed population \n", - "2 cumulative_cases_reported ncit:C175885/Number of Cases \n", - "3 infectious_asymptomatics ido:0000511/infected population \n", - "4 dead ncit:C28554/Dead \n", - "5 isolated_asymptomatics ido:0000511/infected population \n", - "6 quarantined_asymptomatics ido:0000511/infected population \n", - "7 recovered_asymtomatics ido:0000592/immune population \n", - "8 isolated_symtomatics_mild ido:0000511/infected population \n", - "9 recovered_isolated_symptomatic_mild ido:0000592/immune population \n", - "10 infectious_symptomatics_mild ido:0000511/infected population \n", - "11 quarantined_symtomatics_mild ido:0000511/infected population \n", - "12 recovered_mild ido:0000592/immune population \n", - "13 incoming_demand_on_hospital ncit:C25179/Hospitalization \n", - "14 infected_sym_hospital_overflow ido:0000511/infected population \n", - "15 infected_sym_serious_hospital ido:0000511/infected population \n", - "16 infected_sym_icu_overflow ido:0000511/infected population \n", - "17 infected_sym_extreme_icu ido:0000511/infected population \n", - "18 recovered_from_hospital ido:0000592/immune population \n", - "\n", - " mapped_context \n", - "0 NaN \n", - "1 NaN \n", - "2 NaN \n", - "3 disease_severity=ncit:C3833/Asymptomatic \n", - "4 NaN \n", - "5 disease_severity=ncit:C3833/Asymptomatic|isola... \n", - "6 disease_severity=ncit:C3833/Asymptomatic|quara... \n", - "7 disease_severity=ncit:C3833/Asymptomatic \n", - "8 isolation=ncit:C25549|disease_severity=ncit:C7... \n", - "9 isolation=ncit:C25549|disease_severity=ncit:C7... \n", - "10 disease_severity=ncit:C70666 \n", - "11 quarantined=ncit:C71902/Quarantine|disease_sev... \n", - "12 disease_severity=ncit:C70666 \n", - "13 NaN \n", - "14 hospitalization=ncit:C25179/Hospitalization|su... \n", - "15 hospitalization=ncit:C25179/Hospitalization|se... \n", - "16 intensive_care_unit=ncit:C53511|surge_capacity... \n", - "17 intensive_care_unit=ncit:C53511|immediately_li... \n", - "18 hospitalization=ncit:C25179/Hospitalization|pa... " - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "grounding_map_df = pd.read_csv(\"grounding_map.csv\")\n", - "grounding_map_df" + "# grounding_map_df" ] }, { @@ -296,11 +103,14 @@ " \"https://raw.githubusercontent.com/DARPA-ASKEM/program-milestones/main/18-\"\n", " \"month-milestone/hackathon/epi/Scenario%205%20Supplemental/IndiaNonSubscriptedPulsed.mdl\"\n", ")\n", + "initials = {\"susceptibles\": 1.3392e+09}\n", "\n", + "if not os.path.exists(hackathon_file_path):\n", + " urlretrieve(hackathon_file_url, hackathon_file_path)\n", "if os.path.exists(hackathon_file_path):\n", - " tm = template_model_from_mdl_file(hackathon_file_path, grounding_map=grounding_map)\n", + " tm = template_model_from_mdl_file(hackathon_file_path, grounding_map=grounding_map, initials=initials)\n", "else:\n", - " tm = template_model_from_mdl_url(hackathon_file_url, grounding_map=grounding_map)" + " tm = template_model_from_mdl_url(hackathon_file_url, grounding_map=grounding_map, initials=initials)" ] }, { @@ -377,18 +187,18 @@ " \n", " \n", " 2\n", - " high_contact_tracing_and_isolation\n", - " 0.0\n", + " april_14\n", + " 50.0\n", " \n", " \n", " 3\n", - " infection_rate_new_arrivals\n", + " policy_high_contact_tracing_and_isolation\n", " 0.0\n", " \n", " \n", " 4\n", - " contacts_per_day\n", - " 0.0\n", + " base_contacts_other\n", + " 6.0\n", " \n", " \n", " ...\n", @@ -396,50 +206,50 @@ " ...\n", " \n", " \n", - " 87\n", + " 82\n", " sum_in_icu_overflow\n", " 0.0\n", " \n", " \n", - " 88\n", + " 83\n", " sum_quarantine_asym\n", " 0.0\n", " \n", " \n", - " 89\n", + " 84\n", " sum_recovered_asym\n", " 0.0\n", " \n", " \n", - " 90\n", + " 85\n", " sum_recovered_mild\n", " 0.0\n", " \n", " \n", - " 91\n", + " 86\n", " sumnoninfectives\n", - " 0.0\n", + " 5.0\n", " \n", " \n", "\n", - "

92 rows × 2 columns

\n", + "

87 rows × 2 columns

\n", "" ], "text/plain": [ - " name value\n", - "0 open_duration 10.0\n", - "1 end_lockdown_time 400.0\n", - "2 high_contact_tracing_and_isolation 0.0\n", - "3 infection_rate_new_arrivals 0.0\n", - "4 contacts_per_day 0.0\n", - ".. ... ...\n", - "87 sum_in_icu_overflow 0.0\n", - "88 sum_quarantine_asym 0.0\n", - "89 sum_recovered_asym 0.0\n", - "90 sum_recovered_mild 0.0\n", - "91 sumnoninfectives 0.0\n", + " name value\n", + "0 open_duration 10.0\n", + "1 end_lockdown_time 400.0\n", + "2 april_14 50.0\n", + "3 policy_high_contact_tracing_and_isolation 0.0\n", + "4 base_contacts_other 6.0\n", + ".. ... ...\n", + "82 sum_in_icu_overflow 0.0\n", + "83 sum_quarantine_asym 0.0\n", + "84 sum_recovered_asym 0.0\n", + "85 sum_recovered_mild 0.0\n", + "86 sumnoninfectives 5.0\n", "\n", - "[92 rows x 2 columns]" + "[87 rows x 2 columns]" ] }, "execution_count": 7, @@ -451,10 +261,185 @@ "rows = [\n", " (parameter_name, parameter.value)\n", " for parameter_name, parameter in tm.parameters.items()\n", - "]\n", + " ]\n", "pd.DataFrame(rows, columns=[\"name\", \"value\"])" ] }, + { + "cell_type": "markdown", + "id": "1e2ab397-8a18-4276-aa5c-9257b545369b", + "metadata": {}, + "source": [ + "## Print Initial Values" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "27269349-4a85-4542-9872-4ac7d72835d0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
nameexpression
0cumulative_cases_reported0.0
1dead0.0
2exposed5.0
3incoming_demand_on_hospital0.0
4infected_sym_extreme_icu0.0
5infected_sym_hospital_overflow0.0
6infected_sym_icu_overflow0.0
7infected_sym_serious_hospital0.0
8infectious_asymptomatics0.0
9infectious_symptomatics_mild0.0
10isolated_asymptomatics0.0
11isolated_symtomatics_mild0.0
12quarantined_asymptomatics0.0
13quarantined_symtomatics_mild0.0
14recovered_asymtomatics0.0
15recovered_isolated_symptomatic_mild0.0
16recovered_from_hospital0.0
17recovered_mild0.0
18susceptibles1339200000.0
\n", + "
" + ], + "text/plain": [ + " name expression\n", + "0 cumulative_cases_reported 0.0\n", + "1 dead 0.0\n", + "2 exposed 5.0\n", + "3 incoming_demand_on_hospital 0.0\n", + "4 infected_sym_extreme_icu 0.0\n", + "5 infected_sym_hospital_overflow 0.0\n", + "6 infected_sym_icu_overflow 0.0\n", + "7 infected_sym_serious_hospital 0.0\n", + "8 infectious_asymptomatics 0.0\n", + "9 infectious_symptomatics_mild 0.0\n", + "10 isolated_asymptomatics 0.0\n", + "11 isolated_symtomatics_mild 0.0\n", + "12 quarantined_asymptomatics 0.0\n", + "13 quarantined_symtomatics_mild 0.0\n", + "14 recovered_asymtomatics 0.0\n", + "15 recovered_isolated_symptomatic_mild 0.0\n", + "16 recovered_from_hospital 0.0\n", + "17 recovered_mild 0.0\n", + "18 susceptibles 1339200000.0" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rows = [\n", + " (parameter_name, str(parameter.expression))\n", + " for parameter_name, parameter in tm.initials.items()\n", + "]\n", + "pd.DataFrame(rows, columns=[\"name\", \"expression\"])" + ] + }, { "cell_type": "markdown", "id": "7e9e0c9a-c0cf-403e-bed2-bebab9db97b6", @@ -467,7 +452,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "id": "a52c41fe-d9c7-4664-91f6-732f2f201818", "metadata": {}, "outputs": [ @@ -493,6 +478,7 @@ " \n", " \n", " name\n", + " display_name\n", " rate_law\n", " \n", " \n", @@ -500,156 +486,187 @@ " \n", " 0\n", " 1\n", + " new_cases_reported\n", " disease_progression + isolation_rate_asym + is...\n", " \n", " \n", " 1\n", " 2\n", + " deaths\n", " fr_fatality*delay3i(icu_admissions, delay_for_...\n", " \n", " \n", " 2\n", " 3\n", + " deaths_overflow\n", " net_fr_fatality*delay3i(into_inf_sym_icu_overf...\n", " \n", " \n", " 3\n", " 4\n", + " exposure\n", " contacts_total_per_susceptible*infectivity*sus...\n", " \n", " \n", " 4\n", " 5\n", + " infectivity_setting\n", " delay3i(exposure, delay_asymp_noninfective_to_...\n", " \n", " \n", " 5\n", " 6\n", + " disease_progression\n", " (-fr_iso + q_sym + 1)*delay3i(symptom_setting,...\n", " \n", " \n", " 6\n", " 7\n", + " iso_disease_progress_rate\n", " fr_becoming_serious*delay3i(into_iso_sym, net_...\n", " \n", " \n", " 7\n", " 8\n", + " q_disease_progress_rate\n", " delay3i(into_q_sym, net_delay_dprogress_q_sym,...\n", " \n", " \n", " 8\n", " 9\n", + " hospital_admissions\n", " Min(managable_hospital_inflow, incoming_demand...\n", " \n", " \n", " 9\n", " 10\n", + " hospital_overflow\n", " Max(0, incoming_demand_on_hospital/time_step -...\n", " \n", " \n", " 10\n", " 11\n", + " icu_admissions\n", " Min(managable_icu_inflow, typical_worsening_rate)\n", " \n", " \n", " 11\n", " 12\n", + " recoveries_icu\n", " (1 - Min(0.9, fr_fatality))*delay3i(icu_admiss...\n", " \n", " \n", " 12\n", " 13\n", + " recoveries_serioush_overflow\n", " (1 - net_fr_requiring_icu)*delay3i(hospital_ov...\n", " \n", " \n", " 13\n", " 14\n", + " worsening_rate_overflow\n", " net_fr_requiring_icu*delay3i(hospital_overflow...\n", " \n", " \n", " 14\n", " 15\n", + " icu_overflow\n", " Max(0, -managable_icu_inflow + typical_worseni...\n", " \n", " \n", " 15\n", " 16\n", + " recoveries_icu_overflow\n", " (1 - net_fr_fatality)*delay3i(into_inf_sym_icu...\n", " \n", " \n", " 16\n", " 17\n", + " recoveries_serioush\n", " (1 - fr_requiring_icu)*delay3i(hospital_admiss...\n", " \n", " \n", " 17\n", " 18\n", - " None\n", + " infection_rate_new_arrivals\n", + " piecewise((0.0, False), (2.0, False), (1.0, Fa...\n", " \n", " \n", " 18\n", " 19\n", + " isolation_rate_asym\n", " fraction_iso_asym*delay3i(infection_rate_sum, ...\n", " \n", " \n", " 19\n", " 20\n", + " quarantining_rate_asym\n", " fraction_q_asym*delay3i(infection_rate_sum, de...\n", " \n", " \n", " 20\n", " 21\n", + " recoveries_asym\n", " (1 - fr_developing_symptoms)*(-fr_iso + q_asym...\n", " \n", " \n", " 21\n", " 22\n", + " symptom_setting\n", " fr_developing_symptoms*(-fr_iso + q_asym + 1)*...\n", " \n", " \n", " 22\n", " 23\n", + " isolation_rate_sym\n", " None\n", " \n", " \n", " 23\n", " 24\n", + " quarantine_rate_sym\n", " fraction_q_sym*delay3i(symptom_setting, delay_...\n", " \n", " \n", " 24\n", " 25\n", + " recoveries_infected_sym_mild\n", " (1 - Min(0.9, fr_becoming_serious))*(-fr_iso +...\n", " \n", " \n", " 25\n", " 26\n", + " incubation_rate_iso\n", " fr_developing_symptoms*delay3i(isolation_rate_...\n", " \n", " \n", " 26\n", " 27\n", + " recoveries_iso_asym\n", " (1 - fr_developing_symptoms)*delay3i(isolation...\n", " \n", " \n", " 27\n", " 28\n", + " recoveries_iso_sym\n", " (1 - Min(0.9, fr_becoming_serious))*delay3i(in...\n", " \n", " \n", " 28\n", " 29\n", + " incubation_rate_quarantines\n", " fr_developing_symptoms*delay3i(quarantining_ra...\n", " \n", " \n", " 29\n", " 30\n", + " recoveries_q_asym\n", " (1 - fr_developing_symptoms)*delay3i(quarantin...\n", " \n", " \n", " 30\n", " 31\n", + " recoveries_qsym\n", " (1 - fr_becoming_serious)*delay3i(into_q_sym, ...\n", " \n", " \n", @@ -657,51 +674,84 @@ "" ], "text/plain": [ - " name rate_law\n", - "0 1 disease_progression + isolation_rate_asym + is...\n", - "1 2 fr_fatality*delay3i(icu_admissions, delay_for_...\n", - "2 3 net_fr_fatality*delay3i(into_inf_sym_icu_overf...\n", - "3 4 contacts_total_per_susceptible*infectivity*sus...\n", - "4 5 delay3i(exposure, delay_asymp_noninfective_to_...\n", - "5 6 (-fr_iso + q_sym + 1)*delay3i(symptom_setting,...\n", - "6 7 fr_becoming_serious*delay3i(into_iso_sym, net_...\n", - "7 8 delay3i(into_q_sym, net_delay_dprogress_q_sym,...\n", - "8 9 Min(managable_hospital_inflow, incoming_demand...\n", - "9 10 Max(0, incoming_demand_on_hospital/time_step -...\n", - "10 11 Min(managable_icu_inflow, typical_worsening_rate)\n", - "11 12 (1 - Min(0.9, fr_fatality))*delay3i(icu_admiss...\n", - "12 13 (1 - net_fr_requiring_icu)*delay3i(hospital_ov...\n", - "13 14 net_fr_requiring_icu*delay3i(hospital_overflow...\n", - "14 15 Max(0, -managable_icu_inflow + typical_worseni...\n", - "15 16 (1 - net_fr_fatality)*delay3i(into_inf_sym_icu...\n", - "16 17 (1 - fr_requiring_icu)*delay3i(hospital_admiss...\n", - "17 18 None\n", - "18 19 fraction_iso_asym*delay3i(infection_rate_sum, ...\n", - "19 20 fraction_q_asym*delay3i(infection_rate_sum, de...\n", - "20 21 (1 - fr_developing_symptoms)*(-fr_iso + q_asym...\n", - "21 22 fr_developing_symptoms*(-fr_iso + q_asym + 1)*...\n", - "22 23 None\n", - "23 24 fraction_q_sym*delay3i(symptom_setting, delay_...\n", - "24 25 (1 - Min(0.9, fr_becoming_serious))*(-fr_iso +...\n", - "25 26 fr_developing_symptoms*delay3i(isolation_rate_...\n", - "26 27 (1 - fr_developing_symptoms)*delay3i(isolation...\n", - "27 28 (1 - Min(0.9, fr_becoming_serious))*delay3i(in...\n", - "28 29 fr_developing_symptoms*delay3i(quarantining_ra...\n", - "29 30 (1 - fr_developing_symptoms)*delay3i(quarantin...\n", - "30 31 (1 - fr_becoming_serious)*delay3i(into_q_sym, ..." + " name display_name \\\n", + "0 1 new_cases_reported \n", + "1 2 deaths \n", + "2 3 deaths_overflow \n", + "3 4 exposure \n", + "4 5 infectivity_setting \n", + "5 6 disease_progression \n", + "6 7 iso_disease_progress_rate \n", + "7 8 q_disease_progress_rate \n", + "8 9 hospital_admissions \n", + "9 10 hospital_overflow \n", + "10 11 icu_admissions \n", + "11 12 recoveries_icu \n", + "12 13 recoveries_serioush_overflow \n", + "13 14 worsening_rate_overflow \n", + "14 15 icu_overflow \n", + "15 16 recoveries_icu_overflow \n", + "16 17 recoveries_serioush \n", + "17 18 infection_rate_new_arrivals \n", + "18 19 isolation_rate_asym \n", + "19 20 quarantining_rate_asym \n", + "20 21 recoveries_asym \n", + "21 22 symptom_setting \n", + "22 23 isolation_rate_sym \n", + "23 24 quarantine_rate_sym \n", + "24 25 recoveries_infected_sym_mild \n", + "25 26 incubation_rate_iso \n", + "26 27 recoveries_iso_asym \n", + "27 28 recoveries_iso_sym \n", + "28 29 incubation_rate_quarantines \n", + "29 30 recoveries_q_asym \n", + "30 31 recoveries_qsym \n", + "\n", + " rate_law \n", + "0 disease_progression + isolation_rate_asym + is... \n", + "1 fr_fatality*delay3i(icu_admissions, delay_for_... \n", + "2 net_fr_fatality*delay3i(into_inf_sym_icu_overf... \n", + "3 contacts_total_per_susceptible*infectivity*sus... \n", + "4 delay3i(exposure, delay_asymp_noninfective_to_... \n", + "5 (-fr_iso + q_sym + 1)*delay3i(symptom_setting,... \n", + "6 fr_becoming_serious*delay3i(into_iso_sym, net_... \n", + "7 delay3i(into_q_sym, net_delay_dprogress_q_sym,... \n", + "8 Min(managable_hospital_inflow, incoming_demand... \n", + "9 Max(0, incoming_demand_on_hospital/time_step -... \n", + "10 Min(managable_icu_inflow, typical_worsening_rate) \n", + "11 (1 - Min(0.9, fr_fatality))*delay3i(icu_admiss... \n", + "12 (1 - net_fr_requiring_icu)*delay3i(hospital_ov... \n", + "13 net_fr_requiring_icu*delay3i(hospital_overflow... \n", + "14 Max(0, -managable_icu_inflow + typical_worseni... \n", + "15 (1 - net_fr_fatality)*delay3i(into_inf_sym_icu... \n", + "16 (1 - fr_requiring_icu)*delay3i(hospital_admiss... \n", + "17 piecewise((0.0, False), (2.0, False), (1.0, Fa... \n", + "18 fraction_iso_asym*delay3i(infection_rate_sum, ... \n", + "19 fraction_q_asym*delay3i(infection_rate_sum, de... \n", + "20 (1 - fr_developing_symptoms)*(-fr_iso + q_asym... \n", + "21 fr_developing_symptoms*(-fr_iso + q_asym + 1)*... \n", + "22 None \n", + "23 fraction_q_sym*delay3i(symptom_setting, delay_... \n", + "24 (1 - Min(0.9, fr_becoming_serious))*(-fr_iso +... \n", + "25 fr_developing_symptoms*delay3i(isolation_rate_... \n", + "26 (1 - fr_developing_symptoms)*delay3i(isolation... \n", + "27 (1 - Min(0.9, fr_becoming_serious))*delay3i(in... \n", + "28 fr_developing_symptoms*delay3i(quarantining_ra... \n", + "29 (1 - fr_developing_symptoms)*delay3i(quarantin... \n", + "30 (1 - fr_becoming_serious)*delay3i(into_q_sym, ... " ] }, - "execution_count": 8, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "template_rate_laws = [\n", - " (template.name, template.rate_law)\n", + " (template.name, template.display_name, template.rate_law)\n", " for template in tm.templates\n", "]\n", - "pd.DataFrame(template_rate_laws, columns=[\"name\", \"rate_law\"])" + "pd.DataFrame(template_rate_laws, columns=[\"name\", \"display_name\", \"rate_law\"])" ] }, { @@ -714,7 +764,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "e4b46435-01b1-4c90-9eb4-9cb9e98a4a88", "metadata": {}, "outputs": [ @@ -832,7 +882,9 @@ " {'id': '18',\n", " 'name': 'infection_rate_new_arrivals',\n", " 'upstream_stock': None,\n", - " 'downstream_stock': 'infectious_asymptomatics'},\n", + " 'downstream_stock': 'infectious_asymptomatics',\n", + " 'rate_expression': 'piecewise((0.0, False), (2.0, False), (1.0, False), (0.0, False), (2.0, False), (1.0, False), (2.0, False), (3.0, False), (6.0, False), (2.0, False), (7.0, False), (10.0, False), (4.0, False), (10.0, False), (5.0, False), (11.0, False), (14.0, False), (14.0, False), (26.0, False), (34.0, False), (35.0, False), (45.0, False), (55.0, False), (38.0, False), (34.0, False), (24.0, False), (40.0, False), (16.0, False), (20.0, False), (12.0, False), (23.0, False), (14.0, False), (8.0, False), (14.0, False), (12.0, False), (5.0, False), (9.0, False), (6.0, False), (0.0, False), (0.0, False), (0.0, False), (0.0, False))',\n", + " 'rate_expression_mathml': '0.02.01.00.02.01.02.03.06.02.07.010.04.010.05.011.014.014.026.034.035.045.055.038.034.024.040.016.020.012.023.014.08.014.012.05.09.06.00.00.00.00.0'},\n", " {'id': '19',\n", " 'name': 'isolation_rate_asym',\n", " 'upstream_stock': 'infectious_asymptomatics',\n", @@ -1099,69 +1151,69 @@ " 'expression': 'delay_recovery_sym_mild',\n", " 'expression_mathml': 'delay_recovery_sym_mild'}],\n", " 'links': [{'id': 'link1', 'source': 'isolation_rate_sym', 'target': 'flow1'},\n", - " {'id': 'link2', 'source': 'disease_progression', 'target': 'flow1'},\n", - " {'id': 'link3', 'source': 'q_disease_progress_rate', 'target': 'flow1'},\n", - " {'id': 'link4', 'source': 'isolation_rate_asym', 'target': 'flow1'},\n", + " {'id': 'link2', 'source': 'isolation_rate_asym', 'target': 'flow1'},\n", + " {'id': 'link3', 'source': 'disease_progression', 'target': 'flow1'},\n", + " {'id': 'link4', 'source': 'q_disease_progress_rate', 'target': 'flow1'},\n", " {'id': 'link5', 'source': 'fr_fatality', 'target': 'flow2'},\n", " {'id': 'link6', 'source': 'icu_admissions', 'target': 'flow2'},\n", " {'id': 'link7', 'source': 'delay_for_death', 'target': 'flow2'},\n", " {'id': 'link8', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow3'},\n", - " {'id': 'link9', 'source': 'delay_for_death', 'target': 'flow3'},\n", - " {'id': 'link10', 'source': 'net_fr_fatality', 'target': 'flow3'},\n", - " {'id': 'link11', 'source': 'susceptibles', 'target': 'flow4'},\n", - " {'id': 'link12', 'source': 'infectivity', 'target': 'flow4'},\n", - " {'id': 'link13',\n", + " {'id': 'link9', 'source': 'net_fr_fatality', 'target': 'flow3'},\n", + " {'id': 'link10', 'source': 'delay_for_death', 'target': 'flow3'},\n", + " {'id': 'link11', 'source': 'infectivity', 'target': 'flow4'},\n", + " {'id': 'link12',\n", " 'source': 'contacts_total_per_susceptible',\n", " 'target': 'flow4'},\n", + " {'id': 'link13', 'source': 'susceptibles', 'target': 'flow4'},\n", " {'id': 'link14',\n", " 'source': 'delay_asymp_noninfective_to_infective',\n", " 'target': 'flow5'},\n", " {'id': 'link15', 'source': 'exposure', 'target': 'flow5'},\n", - " {'id': 'link16', 'source': 'fr_becoming_serious', 'target': 'flow6'},\n", - " {'id': 'link17', 'source': 'fr_iso', 'target': 'flow6'},\n", - " {'id': 'link18', 'source': 'delay_disease_diagnosis', 'target': 'flow6'},\n", - " {'id': 'link19', 'source': 'symptom_setting', 'target': 'flow6'},\n", - " {'id': 'link20', 'source': 'q_sym', 'target': 'flow6'},\n", - " {'id': 'link21', 'source': 'fr_becoming_serious', 'target': 'flow7'},\n", - " {'id': 'link22',\n", + " {'id': 'link16', 'source': 'delay_disease_diagnosis', 'target': 'flow6'},\n", + " {'id': 'link17', 'source': 'fr_becoming_serious', 'target': 'flow6'},\n", + " {'id': 'link18', 'source': 'symptom_setting', 'target': 'flow6'},\n", + " {'id': 'link19', 'source': 'q_sym', 'target': 'flow6'},\n", + " {'id': 'link20', 'source': 'fr_iso', 'target': 'flow6'},\n", + " {'id': 'link21', 'source': 'into_iso_sym', 'target': 'flow7'},\n", + " {'id': 'link22', 'source': 'fr_becoming_serious', 'target': 'flow7'},\n", + " {'id': 'link23',\n", " 'source': 'net_delay_dprogress_iso_sym',\n", " 'target': 'flow7'},\n", - " {'id': 'link23', 'source': 'into_iso_sym', 'target': 'flow7'},\n", " {'id': 'link24', 'source': 'fr_becoming_serious', 'target': 'flow8'},\n", - " {'id': 'link25', 'source': 'net_delay_dprogress_q_sym', 'target': 'flow8'},\n", - " {'id': 'link26', 'source': 'into_q_sym', 'target': 'flow8'},\n", - " {'id': 'link27', 'source': 'managable_hospital_inflow', 'target': 'flow9'},\n", - " {'id': 'link28',\n", + " {'id': 'link25', 'source': 'into_q_sym', 'target': 'flow8'},\n", + " {'id': 'link26', 'source': 'net_delay_dprogress_q_sym', 'target': 'flow8'},\n", + " {'id': 'link27', 'source': 'time_step', 'target': 'flow9'},\n", + " {'id': 'link28', 'source': 'managable_hospital_inflow', 'target': 'flow9'},\n", + " {'id': 'link29',\n", " 'source': 'incoming_demand_on_hospital',\n", " 'target': 'flow9'},\n", - " {'id': 'link29', 'source': 'time_step', 'target': 'flow9'},\n", - " {'id': 'link30', 'source': 'managable_hospital_inflow', 'target': 'flow10'},\n", - " {'id': 'link31',\n", + " {'id': 'link30', 'source': 'time_step', 'target': 'flow10'},\n", + " {'id': 'link31', 'source': 'managable_hospital_inflow', 'target': 'flow10'},\n", + " {'id': 'link32',\n", " 'source': 'incoming_demand_on_hospital',\n", " 'target': 'flow10'},\n", - " {'id': 'link32', 'source': 'time_step', 'target': 'flow10'},\n", - " {'id': 'link33', 'source': 'typical_worsening_rate', 'target': 'flow11'},\n", - " {'id': 'link34', 'source': 'managable_icu_inflow', 'target': 'flow11'},\n", + " {'id': 'link33', 'source': 'managable_icu_inflow', 'target': 'flow11'},\n", + " {'id': 'link34', 'source': 'typical_worsening_rate', 'target': 'flow11'},\n", " {'id': 'link35', 'source': 'fr_fatality', 'target': 'flow12'},\n", - " {'id': 'link36', 'source': 'icu_admissions', 'target': 'flow12'},\n", - " {'id': 'link37',\n", + " {'id': 'link36',\n", " 'source': 'delay_recovery_sym_extreme',\n", " 'target': 'flow12'},\n", - " {'id': 'link38',\n", + " {'id': 'link37', 'source': 'icu_admissions', 'target': 'flow12'},\n", + " {'id': 'link38', 'source': 'hospital_overflow', 'target': 'flow13'},\n", + " {'id': 'link39', 'source': 'net_fr_requiring_icu', 'target': 'flow13'},\n", + " {'id': 'link40',\n", " 'source': 'delay_recovery_sym_serious',\n", " 'target': 'flow13'},\n", - " {'id': 'link39', 'source': 'hospital_overflow', 'target': 'flow13'},\n", - " {'id': 'link40', 'source': 'net_fr_requiring_icu', 'target': 'flow13'},\n", " {'id': 'link41', 'source': 'hospital_overflow', 'target': 'flow14'},\n", - " {'id': 'link42', 'source': 'delay_worsening', 'target': 'flow14'},\n", - " {'id': 'link43', 'source': 'net_fr_requiring_icu', 'target': 'flow14'},\n", - " {'id': 'link44', 'source': 'typical_worsening_rate', 'target': 'flow15'},\n", - " {'id': 'link45', 'source': 'managable_icu_inflow', 'target': 'flow15'},\n", + " {'id': 'link42', 'source': 'net_fr_requiring_icu', 'target': 'flow14'},\n", + " {'id': 'link43', 'source': 'delay_worsening', 'target': 'flow14'},\n", + " {'id': 'link44', 'source': 'managable_icu_inflow', 'target': 'flow15'},\n", + " {'id': 'link45', 'source': 'typical_worsening_rate', 'target': 'flow15'},\n", " {'id': 'link46', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow16'},\n", - " {'id': 'link47',\n", + " {'id': 'link47', 'source': 'net_fr_fatality', 'target': 'flow16'},\n", + " {'id': 'link48',\n", " 'source': 'delay_recovery_sym_extreme',\n", " 'target': 'flow16'},\n", - " {'id': 'link48', 'source': 'net_fr_fatality', 'target': 'flow16'},\n", " {'id': 'link49', 'source': 'hospital_admissions', 'target': 'flow17'},\n", " {'id': 'link50', 'source': 'fr_requiring_icu', 'target': 'flow17'},\n", " {'id': 'link51',\n", @@ -1170,54 +1222,54 @@ " {'id': 'link52', 'source': 'fraction_iso_asym', 'target': 'flow19'},\n", " {'id': 'link53', 'source': 'infection_rate_sum', 'target': 'flow19'},\n", " {'id': 'link54', 'source': 'delay_iso_asym', 'target': 'flow19'},\n", - " {'id': 'link55', 'source': 'fraction_q_asym', 'target': 'flow20'},\n", - " {'id': 'link56', 'source': 'delay_q_asym', 'target': 'flow20'},\n", - " {'id': 'link57', 'source': 'infection_rate_sum', 'target': 'flow20'},\n", - " {'id': 'link58', 'source': 'fr_iso', 'target': 'flow21'},\n", + " {'id': 'link55', 'source': 'delay_q_asym', 'target': 'flow20'},\n", + " {'id': 'link56', 'source': 'infection_rate_sum', 'target': 'flow20'},\n", + " {'id': 'link57', 'source': 'fraction_q_asym', 'target': 'flow20'},\n", + " {'id': 'link58', 'source': 'delay_recovery_asym', 'target': 'flow21'},\n", " {'id': 'link59', 'source': 'q_asym', 'target': 'flow21'},\n", - " {'id': 'link60', 'source': 'delay_recovery_asym', 'target': 'flow21'},\n", + " {'id': 'link60', 'source': 'fr_iso', 'target': 'flow21'},\n", " {'id': 'link61', 'source': 'fr_developing_symptoms', 'target': 'flow21'},\n", " {'id': 'link62', 'source': 'infection_rate_sum', 'target': 'flow21'},\n", - " {'id': 'link63', 'source': 'fr_iso', 'target': 'flow22'},\n", - " {'id': 'link64', 'source': 'q_asym', 'target': 'flow22'},\n", - " {'id': 'link65', 'source': 'fr_developing_symptoms', 'target': 'flow22'},\n", - " {'id': 'link66',\n", + " {'id': 'link63', 'source': 'q_asym', 'target': 'flow22'},\n", + " {'id': 'link64',\n", " 'source': 'delay_asymp_infective_to_symp',\n", " 'target': 'flow22'},\n", + " {'id': 'link65', 'source': 'fr_iso', 'target': 'flow22'},\n", + " {'id': 'link66', 'source': 'fr_developing_symptoms', 'target': 'flow22'},\n", " {'id': 'link67', 'source': 'infection_rate_sum', 'target': 'flow22'},\n", - " {'id': 'link68', 'source': 'symptom_setting', 'target': 'flow24'},\n", - " {'id': 'link69', 'source': 'fraction_q_sym', 'target': 'flow24'},\n", - " {'id': 'link70', 'source': 'delay_q_sym', 'target': 'flow24'},\n", + " {'id': 'link68', 'source': 'fraction_q_sym', 'target': 'flow24'},\n", + " {'id': 'link69', 'source': 'delay_q_sym', 'target': 'flow24'},\n", + " {'id': 'link70', 'source': 'symptom_setting', 'target': 'flow24'},\n", " {'id': 'link71', 'source': 'fr_becoming_serious', 'target': 'flow25'},\n", - " {'id': 'link72', 'source': 'fr_iso', 'target': 'flow25'},\n", - " {'id': 'link73', 'source': 'symptom_setting', 'target': 'flow25'},\n", + " {'id': 'link72', 'source': 'symptom_setting', 'target': 'flow25'},\n", + " {'id': 'link73', 'source': 'q_sym', 'target': 'flow25'},\n", " {'id': 'link74', 'source': 'delay_recovery_sym_mild', 'target': 'flow25'},\n", - " {'id': 'link75', 'source': 'q_sym', 'target': 'flow25'},\n", - " {'id': 'link76', 'source': 'fr_developing_symptoms', 'target': 'flow26'},\n", - " {'id': 'link77', 'source': 'isolation_rate_asym', 'target': 'flow26'},\n", - " {'id': 'link78',\n", + " {'id': 'link75', 'source': 'fr_iso', 'target': 'flow25'},\n", + " {'id': 'link76', 'source': 'isolation_rate_asym', 'target': 'flow26'},\n", + " {'id': 'link77',\n", " 'source': 'net_delay_incubation_iso_asym',\n", " 'target': 'flow26'},\n", - " {'id': 'link79', 'source': 'fr_developing_symptoms', 'target': 'flow27'},\n", - " {'id': 'link80',\n", + " {'id': 'link78', 'source': 'fr_developing_symptoms', 'target': 'flow26'},\n", + " {'id': 'link79',\n", " 'source': 'net_delay_recovery_iso_asym',\n", " 'target': 'flow27'},\n", - " {'id': 'link81', 'source': 'isolation_rate_asym', 'target': 'flow27'},\n", - " {'id': 'link82', 'source': 'fr_becoming_serious', 'target': 'flow28'},\n", - " {'id': 'link83', 'source': 'into_iso_sym', 'target': 'flow28'},\n", + " {'id': 'link80', 'source': 'isolation_rate_asym', 'target': 'flow27'},\n", + " {'id': 'link81', 'source': 'fr_developing_symptoms', 'target': 'flow27'},\n", + " {'id': 'link82', 'source': 'into_iso_sym', 'target': 'flow28'},\n", + " {'id': 'link83', 'source': 'fr_becoming_serious', 'target': 'flow28'},\n", " {'id': 'link84',\n", " 'source': 'net_delay_recovery_iso_sym',\n", " 'target': 'flow28'},\n", - " {'id': 'link85', 'source': 'fr_developing_symptoms', 'target': 'flow29'},\n", - " {'id': 'link86',\n", + " {'id': 'link85',\n", " 'source': 'net_delay_incubation_q_asym',\n", " 'target': 'flow29'},\n", - " {'id': 'link87', 'source': 'quarantining_rate_asym', 'target': 'flow29'},\n", - " {'id': 'link88', 'source': 'fr_developing_symptoms', 'target': 'flow30'},\n", - " {'id': 'link89', 'source': 'net_delay_recovery_q_asym', 'target': 'flow30'},\n", - " {'id': 'link90', 'source': 'quarantining_rate_asym', 'target': 'flow30'},\n", - " {'id': 'link91', 'source': 'fr_becoming_serious', 'target': 'flow31'},\n", - " {'id': 'link92', 'source': 'net_delay_recovery_q_sym', 'target': 'flow31'},\n", + " {'id': 'link86', 'source': 'quarantining_rate_asym', 'target': 'flow29'},\n", + " {'id': 'link87', 'source': 'fr_developing_symptoms', 'target': 'flow29'},\n", + " {'id': 'link88', 'source': 'net_delay_recovery_q_asym', 'target': 'flow30'},\n", + " {'id': 'link89', 'source': 'quarantining_rate_asym', 'target': 'flow30'},\n", + " {'id': 'link90', 'source': 'fr_developing_symptoms', 'target': 'flow30'},\n", + " {'id': 'link91', 'source': 'net_delay_recovery_q_sym', 'target': 'flow31'},\n", + " {'id': 'link92', 'source': 'fr_becoming_serious', 'target': 'flow31'},\n", " {'id': 'link93', 'source': 'into_q_sym', 'target': 'flow31'}]},\n", " 'semantics': {'ode': {'parameters': [{'id': 'isolation_rate_sym',\n", " 'value': 0.0,\n", @@ -1277,11 +1329,6 @@ " 'units': {'expression': 'day', 'expression_mathml': 'day'}},\n", " {'id': 'open_duration', 'value': 10.0},\n", " {'id': 'end_lockdown_time', 'value': 400.0},\n", - " {'id': 'high_contact_tracing_and_isolation', 'value': 0.0},\n", - " {'id': 'infection_rate_new_arrivals', 'value': 0.0},\n", - " {'id': 'contacts_per_day', 'value': 0.0},\n", - " {'id': 'hygiene_mask_impact', 'value': 0.0},\n", - " {'id': 'interaction_intensity', 'value': 0.0},\n", " {'id': 'april_14', 'value': 50.0},\n", " {'id': 'policy_high_contact_tracing_and_isolation', 'value': 0.0},\n", " {'id': 'base_contacts_other', 'value': 6.0},\n", @@ -1341,7 +1388,7 @@ " {'id': 'sum_isolation_sym_mild', 'value': 0.0},\n", " {'id': 'sum_quarantine_sym_mild', 'value': 0.0},\n", " {'id': 'sum_recovered_via_hosp', 'value': 0.0},\n", - " {'id': 'sum_susceptibles', 'value': 0.0},\n", + " {'id': 'sum_susceptibles', 'value': 1339200000.0},\n", " {'id': 'sum_symtomatics', 'value': 0.0},\n", " {'id': 'sum_asymtomatics', 'value': 0.0},\n", " {'id': 'sum_cumulative_cases_reported', 'value': 0.0},\n", @@ -1354,7 +1401,7 @@ " {'id': 'sum_quarantine_asym', 'value': 0.0},\n", " {'id': 'sum_recovered_asym', 'value': 0.0},\n", " {'id': 'sum_recovered_mild', 'value': 0.0},\n", - " {'id': 'sumnoninfectives', 'value': 0.0}],\n", + " {'id': 'sumnoninfectives', 'value': 5.0}],\n", " 'initials': [{'target': 'cumulative_cases_reported',\n", " 'expression': '0.0',\n", " 'expression_mathml': '0.0'},\n", @@ -1368,11 +1415,11 @@ " 'expression': '0.0',\n", " 'expression_mathml': '0.0'},\n", " {'target': 'susceptibles',\n", - " 'expression': '0.0',\n", - " 'expression_mathml': '0.0'},\n", + " 'expression': '1339200000.0',\n", + " 'expression_mathml': '1339200000.0'},\n", " {'target': 'exposed',\n", - " 'expression': '0.0',\n", - " 'expression_mathml': '0.0'},\n", + " 'expression': '5.0',\n", + " 'expression_mathml': '5.0'},\n", " {'target': 'infectious_asymptomatics',\n", " 'expression': '0.0',\n", " 'expression_mathml': '0.0'},\n", @@ -1417,7 +1464,7 @@ " 'metadata': {}}" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } diff --git a/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json b/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json index b542fa551..f073e4baf 100644 --- a/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json +++ b/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json @@ -149,7 +149,9 @@ "id": "18", "name": "infection_rate_new_arrivals", "upstream_stock": null, - "downstream_stock": "infectious_asymptomatics" + "downstream_stock": "infectious_asymptomatics", + "rate_expression": "piecewise((0.0, False), (2.0, False), (1.0, False), (0.0, False), (2.0, False), (1.0, False), (2.0, False), (3.0, False), (6.0, False), (2.0, False), (7.0, False), (10.0, False), (4.0, False), (10.0, False), (5.0, False), (11.0, False), (14.0, False), (14.0, False), (26.0, False), (34.0, False), (35.0, False), (45.0, False), (55.0, False), (38.0, False), (34.0, False), (24.0, False), (40.0, False), (16.0, False), (20.0, False), (12.0, False), (23.0, False), (14.0, False), (8.0, False), (14.0, False), (12.0, False), (5.0, False), (9.0, False), (6.0, False), (0.0, False), (0.0, False), (0.0, False), (0.0, False))", + "rate_expression_mathml": "0.02.01.00.02.01.02.03.06.02.07.010.04.010.05.011.014.014.026.034.035.045.055.038.034.024.040.016.020.012.023.014.08.014.012.05.09.06.00.00.00.00.0" }, { "id": "19", @@ -674,17 +676,17 @@ }, { "id": "link2", - "source": "disease_progression", + "source": "isolation_rate_asym", "target": "flow1" }, { "id": "link3", - "source": "q_disease_progress_rate", + "source": "disease_progression", "target": "flow1" }, { "id": "link4", - "source": "isolation_rate_asym", + "source": "q_disease_progress_rate", "target": "flow1" }, { @@ -709,27 +711,27 @@ }, { "id": "link9", - "source": "delay_for_death", + "source": "net_fr_fatality", "target": "flow3" }, { "id": "link10", - "source": "net_fr_fatality", + "source": "delay_for_death", "target": "flow3" }, { "id": "link11", - "source": "susceptibles", + "source": "infectivity", "target": "flow4" }, { "id": "link12", - "source": "infectivity", + "source": "contacts_total_per_susceptible", "target": "flow4" }, { "id": "link13", - "source": "contacts_total_per_susceptible", + "source": "susceptibles", "target": "flow4" }, { @@ -744,42 +746,42 @@ }, { "id": "link16", - "source": "fr_becoming_serious", + "source": "delay_disease_diagnosis", "target": "flow6" }, { "id": "link17", - "source": "fr_iso", + "source": "fr_becoming_serious", "target": "flow6" }, { "id": "link18", - "source": "delay_disease_diagnosis", + "source": "symptom_setting", "target": "flow6" }, { "id": "link19", - "source": "symptom_setting", + "source": "q_sym", "target": "flow6" }, { "id": "link20", - "source": "q_sym", + "source": "fr_iso", "target": "flow6" }, { "id": "link21", - "source": "fr_becoming_serious", + "source": "into_iso_sym", "target": "flow7" }, { "id": "link22", - "source": "net_delay_dprogress_iso_sym", + "source": "fr_becoming_serious", "target": "flow7" }, { "id": "link23", - "source": "into_iso_sym", + "source": "net_delay_dprogress_iso_sym", "target": "flow7" }, { @@ -789,52 +791,52 @@ }, { "id": "link25", - "source": "net_delay_dprogress_q_sym", + "source": "into_q_sym", "target": "flow8" }, { "id": "link26", - "source": "into_q_sym", + "source": "net_delay_dprogress_q_sym", "target": "flow8" }, { "id": "link27", - "source": "managable_hospital_inflow", + "source": "time_step", "target": "flow9" }, { "id": "link28", - "source": "incoming_demand_on_hospital", + "source": "managable_hospital_inflow", "target": "flow9" }, { "id": "link29", - "source": "time_step", + "source": "incoming_demand_on_hospital", "target": "flow9" }, { "id": "link30", - "source": "managable_hospital_inflow", + "source": "time_step", "target": "flow10" }, { "id": "link31", - "source": "incoming_demand_on_hospital", + "source": "managable_hospital_inflow", "target": "flow10" }, { "id": "link32", - "source": "time_step", + "source": "incoming_demand_on_hospital", "target": "flow10" }, { "id": "link33", - "source": "typical_worsening_rate", + "source": "managable_icu_inflow", "target": "flow11" }, { "id": "link34", - "source": "managable_icu_inflow", + "source": "typical_worsening_rate", "target": "flow11" }, { @@ -844,27 +846,27 @@ }, { "id": "link36", - "source": "icu_admissions", + "source": "delay_recovery_sym_extreme", "target": "flow12" }, { "id": "link37", - "source": "delay_recovery_sym_extreme", + "source": "icu_admissions", "target": "flow12" }, { "id": "link38", - "source": "delay_recovery_sym_serious", + "source": "hospital_overflow", "target": "flow13" }, { "id": "link39", - "source": "hospital_overflow", + "source": "net_fr_requiring_icu", "target": "flow13" }, { "id": "link40", - "source": "net_fr_requiring_icu", + "source": "delay_recovery_sym_serious", "target": "flow13" }, { @@ -874,22 +876,22 @@ }, { "id": "link42", - "source": "delay_worsening", + "source": "net_fr_requiring_icu", "target": "flow14" }, { "id": "link43", - "source": "net_fr_requiring_icu", + "source": "delay_worsening", "target": "flow14" }, { "id": "link44", - "source": "typical_worsening_rate", + "source": "managable_icu_inflow", "target": "flow15" }, { "id": "link45", - "source": "managable_icu_inflow", + "source": "typical_worsening_rate", "target": "flow15" }, { @@ -899,12 +901,12 @@ }, { "id": "link47", - "source": "delay_recovery_sym_extreme", + "source": "net_fr_fatality", "target": "flow16" }, { "id": "link48", - "source": "net_fr_fatality", + "source": "delay_recovery_sym_extreme", "target": "flow16" }, { @@ -939,22 +941,22 @@ }, { "id": "link55", - "source": "fraction_q_asym", + "source": "delay_q_asym", "target": "flow20" }, { "id": "link56", - "source": "delay_q_asym", + "source": "infection_rate_sum", "target": "flow20" }, { "id": "link57", - "source": "infection_rate_sum", + "source": "fraction_q_asym", "target": "flow20" }, { "id": "link58", - "source": "fr_iso", + "source": "delay_recovery_asym", "target": "flow21" }, { @@ -964,7 +966,7 @@ }, { "id": "link60", - "source": "delay_recovery_asym", + "source": "fr_iso", "target": "flow21" }, { @@ -979,22 +981,22 @@ }, { "id": "link63", - "source": "fr_iso", + "source": "q_asym", "target": "flow22" }, { "id": "link64", - "source": "q_asym", + "source": "delay_asymp_infective_to_symp", "target": "flow22" }, { "id": "link65", - "source": "fr_developing_symptoms", + "source": "fr_iso", "target": "flow22" }, { "id": "link66", - "source": "delay_asymp_infective_to_symp", + "source": "fr_developing_symptoms", "target": "flow22" }, { @@ -1004,17 +1006,17 @@ }, { "id": "link68", - "source": "symptom_setting", + "source": "fraction_q_sym", "target": "flow24" }, { "id": "link69", - "source": "fraction_q_sym", + "source": "delay_q_sym", "target": "flow24" }, { "id": "link70", - "source": "delay_q_sym", + "source": "symptom_setting", "target": "flow24" }, { @@ -1024,12 +1026,12 @@ }, { "id": "link72", - "source": "fr_iso", + "source": "symptom_setting", "target": "flow25" }, { "id": "link73", - "source": "symptom_setting", + "source": "q_sym", "target": "flow25" }, { @@ -1039,47 +1041,47 @@ }, { "id": "link75", - "source": "q_sym", + "source": "fr_iso", "target": "flow25" }, { "id": "link76", - "source": "fr_developing_symptoms", + "source": "isolation_rate_asym", "target": "flow26" }, { "id": "link77", - "source": "isolation_rate_asym", + "source": "net_delay_incubation_iso_asym", "target": "flow26" }, { "id": "link78", - "source": "net_delay_incubation_iso_asym", + "source": "fr_developing_symptoms", "target": "flow26" }, { "id": "link79", - "source": "fr_developing_symptoms", + "source": "net_delay_recovery_iso_asym", "target": "flow27" }, { "id": "link80", - "source": "net_delay_recovery_iso_asym", + "source": "isolation_rate_asym", "target": "flow27" }, { "id": "link81", - "source": "isolation_rate_asym", + "source": "fr_developing_symptoms", "target": "flow27" }, { "id": "link82", - "source": "fr_becoming_serious", + "source": "into_iso_sym", "target": "flow28" }, { "id": "link83", - "source": "into_iso_sym", + "source": "fr_becoming_serious", "target": "flow28" }, { @@ -1089,42 +1091,42 @@ }, { "id": "link85", - "source": "fr_developing_symptoms", + "source": "net_delay_incubation_q_asym", "target": "flow29" }, { "id": "link86", - "source": "net_delay_incubation_q_asym", + "source": "quarantining_rate_asym", "target": "flow29" }, { "id": "link87", - "source": "quarantining_rate_asym", + "source": "fr_developing_symptoms", "target": "flow29" }, { "id": "link88", - "source": "fr_developing_symptoms", + "source": "net_delay_recovery_q_asym", "target": "flow30" }, { "id": "link89", - "source": "net_delay_recovery_q_asym", + "source": "quarantining_rate_asym", "target": "flow30" }, { "id": "link90", - "source": "quarantining_rate_asym", + "source": "fr_developing_symptoms", "target": "flow30" }, { "id": "link91", - "source": "fr_becoming_serious", + "source": "net_delay_recovery_q_sym", "target": "flow31" }, { "id": "link92", - "source": "net_delay_recovery_q_sym", + "source": "fr_becoming_serious", "target": "flow31" }, { @@ -1290,26 +1292,6 @@ "id": "end_lockdown_time", "value": 400.0 }, - { - "id": "high_contact_tracing_and_isolation", - "value": 0.0 - }, - { - "id": "infection_rate_new_arrivals", - "value": 0.0 - }, - { - "id": "contacts_per_day", - "value": 0.0 - }, - { - "id": "hygiene_mask_impact", - "value": 0.0 - }, - { - "id": "interaction_intensity", - "value": 0.0 - }, { "id": "april_14", "value": 50.0 @@ -1527,7 +1509,7 @@ }, { "id": "sum_susceptibles", - "value": 0.0 + "value": 1339200000.0 }, { "id": "sum_symtomatics", @@ -1579,7 +1561,7 @@ }, { "id": "sumnoninfectives", - "value": 0.0 + "value": 5.0 } ], "initials": [ @@ -1605,13 +1587,13 @@ }, { "target": "susceptibles", - "expression": "0.0", - "expression_mathml": "0.0" + "expression": "1339200000.0", + "expression_mathml": "1339200000.0" }, { "target": "exposed", - "expression": "0.0", - "expression_mathml": "0.0" + "expression": "5.0", + "expression_mathml": "5.0" }, { "target": "infectious_asymptomatics", diff --git a/setup.cfg b/setup.cfg index 4a373432f..0e4e1ec55 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,6 +22,7 @@ install_requires = jsonschema tabulate pysd + more_itertools zip_safe = false include_package_data = True diff --git a/tests/test_pysd.py b/tests/test_pysd.py new file mode 100644 index 000000000..803b3c152 --- /dev/null +++ b/tests/test_pysd.py @@ -0,0 +1,15 @@ +import unittest + +import sympy +from mira.metamodel import safe_parse_expr +from mira.sources.system_dynamics.pysd import with_lookup_to_piecewise + + +class TestPySDUtils(unittest.TestCase): + """Test pysd utility functions.""" + + def test_with_lookup_to_piecewise(self): + data = "WITH LOOKUP(time,([(0,0)-(500,100)],(0,0),(1,2),(2,1),(3,0),(4,2),(5,1),(1000,0)))" + val = with_lookup_to_piecewise(data) + rv = safe_parse_expr(val) + self.assertIsInstance(rv, sympy.Expr) From 67241e1c4092bae29aec37a066b0191c028d0549 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Wed, 28 Feb 2024 14:51:22 +0100 Subject: [PATCH 4/9] Update docs --- mira/sources/system_dynamics/vensim.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mira/sources/system_dynamics/vensim.py b/mira/sources/system_dynamics/vensim.py index 1d0fa5786..49600b267 100644 --- a/mira/sources/system_dynamics/vensim.py +++ b/mira/sources/system_dynamics/vensim.py @@ -10,7 +10,6 @@ import tempfile import re -import typing as t import pysd from pysd.translators.vensim.vensim_file import VensimFile @@ -44,6 +43,9 @@ def template_model_from_mdl_file(fname, *, grounding_map=None, initials=None) -> The path to the local Vensim file grounding_map: dict[str, Concept] A grounding map, a map from label to Concept + initials: dict[str, float] + Explicit initial values to use for compartments in the + model. Will overwrite model-internal definitions. Returns ------- @@ -72,6 +74,9 @@ def template_model_from_mdl_url(url, *, grounding_map=None, initials=None) -> Te The url to the mdl file grounding_map: dict[str, Concept] A grounding map, a map from label to Concept + initials: dict[str, float] + Explicit initial values to use for compartments in the + model. Will overwrite model-internal definitions. Returns ------- From c53ceeeca17485e33dd09ecc6ccfee96cc46ce02 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Wed, 28 Feb 2024 14:54:28 +0100 Subject: [PATCH 5/9] Switch piecewise from discrete to continuous --- mira/sources/system_dynamics/pysd.py | 3 +- .../scenario5/Scenario 5 Notebook.ipynb | 129 +++++++++-------- .../scenario5/scenario_5_stockflow.json | 135 +++++++++--------- 3 files changed, 139 insertions(+), 128 deletions(-) diff --git a/mira/sources/system_dynamics/pysd.py b/mira/sources/system_dynamics/pysd.py index 311f7364b..7c1b99cae 100644 --- a/mira/sources/system_dynamics/pysd.py +++ b/mira/sources/system_dynamics/pysd.py @@ -558,6 +558,7 @@ def with_lookup_to_piecewise(expr_text: str) -> str: # construct the sympy string as a simple lookup, where the y # value gets returned if the target variable is the given x value # for each x,y pair - conditions = ",".join(f"({y}, {variable} == {x})" for x, y in pairs) + # FIXME what's the right way to write the conditional here + conditions = ",".join(f"({y}, {variable} >= {x})" for x, y in pairs) sympy_str = f"Piecewise({conditions})" return sympy_str diff --git a/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb b/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb index b14ab6b0f..f5921134c 100644 --- a/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb +++ b/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb @@ -589,7 +589,7 @@ " 17\n", " 18\n", " infection_rate_new_arrivals\n", - " piecewise((0.0, False), (2.0, False), (1.0, Fa...\n", + " piecewise((0.0, time >= 0.0), (2.0, time >= 1....\n", " \n", " \n", " 18\n", @@ -725,7 +725,7 @@ "14 Max(0, -managable_icu_inflow + typical_worseni... \n", "15 (1 - net_fr_fatality)*delay3i(into_inf_sym_icu... \n", "16 (1 - fr_requiring_icu)*delay3i(hospital_admiss... \n", - "17 piecewise((0.0, False), (2.0, False), (1.0, Fa... \n", + "17 piecewise((0.0, time >= 0.0), (2.0, time >= 1.... \n", "18 fraction_iso_asym*delay3i(infection_rate_sum, ... \n", "19 fraction_q_asym*delay3i(infection_rate_sum, de... \n", "20 (1 - fr_developing_symptoms)*(-fr_iso + q_asym... \n", @@ -883,8 +883,8 @@ " 'name': 'infection_rate_new_arrivals',\n", " 'upstream_stock': None,\n", " 'downstream_stock': 'infectious_asymptomatics',\n", - " 'rate_expression': 'piecewise((0.0, False), (2.0, False), (1.0, False), (0.0, False), (2.0, False), (1.0, False), (2.0, False), (3.0, False), (6.0, False), (2.0, False), (7.0, False), (10.0, False), (4.0, False), (10.0, False), (5.0, False), (11.0, False), (14.0, False), (14.0, False), (26.0, False), (34.0, False), (35.0, False), (45.0, False), (55.0, False), (38.0, False), (34.0, False), (24.0, False), (40.0, False), (16.0, False), (20.0, False), (12.0, False), (23.0, False), (14.0, False), (8.0, False), (14.0, False), (12.0, False), (5.0, False), (9.0, False), (6.0, False), (0.0, False), (0.0, False), (0.0, False), (0.0, False))',\n", - " 'rate_expression_mathml': '0.02.01.00.02.01.02.03.06.02.07.010.04.010.05.011.014.014.026.034.035.045.055.038.034.024.040.016.020.012.023.014.08.014.012.05.09.06.00.00.00.00.0'},\n", + " 'rate_expression': 'piecewise((0.0, time >= 0.0), (2.0, time >= 1.0), (1.0, time >= 2.0), (0.0, time >= 3.0), (2.0, time >= 4.0), (1.0, time >= 5.0), (2.0, time >= 6.0), (3.0, time >= 7.0), (6.0, time >= 8.0), (2.0, time >= 9.0), (7.0, time >= 10.0), (10.0, time >= 11.0), (4.0, time >= 12.0), (10.0, time >= 13.0), (5.0, time >= 14.0), (11.0, time >= 15.0), (14.0, time >= 16.0), (14.0, time >= 17.0), (26.0, time >= 18.0), (34.0, time >= 19.0), (35.0, time >= 20.0), (45.0, time >= 21.0), (55.0, time >= 22.0), (38.0, time >= 23.0), (34.0, time >= 24.0), (24.0, time >= 25.0), (40.0, time >= 26.0), (16.0, time >= 27.0), (20.0, time >= 28.0), (12.0, time >= 29.0), (23.0, time >= 30.0), (14.0, time >= 31.0), (8.0, time >= 32.0), (14.0, time >= 33.0), (12.0, time >= 34.0), (5.0, time >= 35.0), (9.0, time >= 36.0), (6.0, time >= 37.0), (0.0, time >= 38.0), (0.0, time >= 39.0), (0.0, time >= 40.0), (0.0, time >= 1000.0))',\n", + " 'rate_expression_mathml': '0.0time0.02.0time1.01.0time2.00.0time3.02.0time4.01.0time5.02.0time6.03.0time7.06.0time8.02.0time9.07.0time10.010.0time11.04.0time12.010.0time13.05.0time14.011.0time15.014.0time16.014.0time17.026.0time18.034.0time19.035.0time20.045.0time21.055.0time22.038.0time23.034.0time24.024.0time25.040.0time26.016.0time27.020.0time28.012.0time29.023.0time30.014.0time31.08.0time32.014.0time33.012.0time34.05.0time35.09.0time36.06.0time37.00.0time38.00.0time39.00.0time40.00.0time1000.0'},\n", " {'id': '19',\n", " 'name': 'isolation_rate_asym',\n", " 'upstream_stock': 'infectious_asymptomatics',\n", @@ -1150,16 +1150,18 @@ " 'name': 'delay_recovery_sym_mild',\n", " 'expression': 'delay_recovery_sym_mild',\n", " 'expression_mathml': 'delay_recovery_sym_mild'}],\n", - " 'links': [{'id': 'link1', 'source': 'isolation_rate_sym', 'target': 'flow1'},\n", - " {'id': 'link2', 'source': 'isolation_rate_asym', 'target': 'flow1'},\n", - " {'id': 'link3', 'source': 'disease_progression', 'target': 'flow1'},\n", + " 'links': [{'id': 'link1',\n", + " 'source': 'isolation_rate_asym',\n", + " 'target': 'flow1'},\n", + " {'id': 'link2', 'source': 'disease_progression', 'target': 'flow1'},\n", + " {'id': 'link3', 'source': 'isolation_rate_sym', 'target': 'flow1'},\n", " {'id': 'link4', 'source': 'q_disease_progress_rate', 'target': 'flow1'},\n", - " {'id': 'link5', 'source': 'fr_fatality', 'target': 'flow2'},\n", + " {'id': 'link5', 'source': 'delay_for_death', 'target': 'flow2'},\n", " {'id': 'link6', 'source': 'icu_admissions', 'target': 'flow2'},\n", - " {'id': 'link7', 'source': 'delay_for_death', 'target': 'flow2'},\n", + " {'id': 'link7', 'source': 'fr_fatality', 'target': 'flow2'},\n", " {'id': 'link8', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow3'},\n", - " {'id': 'link9', 'source': 'net_fr_fatality', 'target': 'flow3'},\n", - " {'id': 'link10', 'source': 'delay_for_death', 'target': 'flow3'},\n", + " {'id': 'link9', 'source': 'delay_for_death', 'target': 'flow3'},\n", + " {'id': 'link10', 'source': 'net_fr_fatality', 'target': 'flow3'},\n", " {'id': 'link11', 'source': 'infectivity', 'target': 'flow4'},\n", " {'id': 'link12',\n", " 'source': 'contacts_total_per_susceptible',\n", @@ -1169,13 +1171,13 @@ " 'source': 'delay_asymp_noninfective_to_infective',\n", " 'target': 'flow5'},\n", " {'id': 'link15', 'source': 'exposure', 'target': 'flow5'},\n", - " {'id': 'link16', 'source': 'delay_disease_diagnosis', 'target': 'flow6'},\n", + " {'id': 'link16', 'source': 'q_sym', 'target': 'flow6'},\n", " {'id': 'link17', 'source': 'fr_becoming_serious', 'target': 'flow6'},\n", - " {'id': 'link18', 'source': 'symptom_setting', 'target': 'flow6'},\n", - " {'id': 'link19', 'source': 'q_sym', 'target': 'flow6'},\n", + " {'id': 'link18', 'source': 'delay_disease_diagnosis', 'target': 'flow6'},\n", + " {'id': 'link19', 'source': 'symptom_setting', 'target': 'flow6'},\n", " {'id': 'link20', 'source': 'fr_iso', 'target': 'flow6'},\n", - " {'id': 'link21', 'source': 'into_iso_sym', 'target': 'flow7'},\n", - " {'id': 'link22', 'source': 'fr_becoming_serious', 'target': 'flow7'},\n", + " {'id': 'link21', 'source': 'fr_becoming_serious', 'target': 'flow7'},\n", + " {'id': 'link22', 'source': 'into_iso_sym', 'target': 'flow7'},\n", " {'id': 'link23',\n", " 'source': 'net_delay_dprogress_iso_sym',\n", " 'target': 'flow7'},\n", @@ -1194,19 +1196,19 @@ " 'target': 'flow10'},\n", " {'id': 'link33', 'source': 'managable_icu_inflow', 'target': 'flow11'},\n", " {'id': 'link34', 'source': 'typical_worsening_rate', 'target': 'flow11'},\n", - " {'id': 'link35', 'source': 'fr_fatality', 'target': 'flow12'},\n", - " {'id': 'link36',\n", + " {'id': 'link35', 'source': 'icu_admissions', 'target': 'flow12'},\n", + " {'id': 'link36', 'source': 'fr_fatality', 'target': 'flow12'},\n", + " {'id': 'link37',\n", " 'source': 'delay_recovery_sym_extreme',\n", " 'target': 'flow12'},\n", - " {'id': 'link37', 'source': 'icu_admissions', 'target': 'flow12'},\n", - " {'id': 'link38', 'source': 'hospital_overflow', 'target': 'flow13'},\n", - " {'id': 'link39', 'source': 'net_fr_requiring_icu', 'target': 'flow13'},\n", - " {'id': 'link40',\n", + " {'id': 'link38',\n", " 'source': 'delay_recovery_sym_serious',\n", " 'target': 'flow13'},\n", - " {'id': 'link41', 'source': 'hospital_overflow', 'target': 'flow14'},\n", + " {'id': 'link39', 'source': 'net_fr_requiring_icu', 'target': 'flow13'},\n", + " {'id': 'link40', 'source': 'hospital_overflow', 'target': 'flow13'},\n", + " {'id': 'link41', 'source': 'delay_worsening', 'target': 'flow14'},\n", " {'id': 'link42', 'source': 'net_fr_requiring_icu', 'target': 'flow14'},\n", - " {'id': 'link43', 'source': 'delay_worsening', 'target': 'flow14'},\n", + " {'id': 'link43', 'source': 'hospital_overflow', 'target': 'flow14'},\n", " {'id': 'link44', 'source': 'managable_icu_inflow', 'target': 'flow15'},\n", " {'id': 'link45', 'source': 'typical_worsening_rate', 'target': 'flow15'},\n", " {'id': 'link46', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow16'},\n", @@ -1219,58 +1221,61 @@ " {'id': 'link51',\n", " 'source': 'delay_recovery_sym_serious',\n", " 'target': 'flow17'},\n", - " {'id': 'link52', 'source': 'fraction_iso_asym', 'target': 'flow19'},\n", - " {'id': 'link53', 'source': 'infection_rate_sum', 'target': 'flow19'},\n", - " {'id': 'link54', 'source': 'delay_iso_asym', 'target': 'flow19'},\n", - " {'id': 'link55', 'source': 'delay_q_asym', 'target': 'flow20'},\n", + " {'id': 'link52', 'source': 'time', 'target': 'flow18'},\n", + " {'id': 'link53', 'source': 'fraction_iso_asym', 'target': 'flow19'},\n", + " {'id': 'link54', 'source': 'infection_rate_sum', 'target': 'flow19'},\n", + " {'id': 'link55', 'source': 'delay_iso_asym', 'target': 'flow19'},\n", " {'id': 'link56', 'source': 'infection_rate_sum', 'target': 'flow20'},\n", - " {'id': 'link57', 'source': 'fraction_q_asym', 'target': 'flow20'},\n", - " {'id': 'link58', 'source': 'delay_recovery_asym', 'target': 'flow21'},\n", - " {'id': 'link59', 'source': 'q_asym', 'target': 'flow21'},\n", - " {'id': 'link60', 'source': 'fr_iso', 'target': 'flow21'},\n", - " {'id': 'link61', 'source': 'fr_developing_symptoms', 'target': 'flow21'},\n", - " {'id': 'link62', 'source': 'infection_rate_sum', 'target': 'flow21'},\n", - " {'id': 'link63', 'source': 'q_asym', 'target': 'flow22'},\n", - " {'id': 'link64',\n", + " {'id': 'link57', 'source': 'delay_q_asym', 'target': 'flow20'},\n", + " {'id': 'link58', 'source': 'fraction_q_asym', 'target': 'flow20'},\n", + " {'id': 'link59', 'source': 'infection_rate_sum', 'target': 'flow21'},\n", + " {'id': 'link60', 'source': 'q_asym', 'target': 'flow21'},\n", + " {'id': 'link61', 'source': 'delay_recovery_asym', 'target': 'flow21'},\n", + " {'id': 'link62', 'source': 'fr_developing_symptoms', 'target': 'flow21'},\n", + " {'id': 'link63', 'source': 'fr_iso', 'target': 'flow21'},\n", + " {'id': 'link64', 'source': 'infection_rate_sum', 'target': 'flow22'},\n", + " {'id': 'link65',\n", " 'source': 'delay_asymp_infective_to_symp',\n", " 'target': 'flow22'},\n", - " {'id': 'link65', 'source': 'fr_iso', 'target': 'flow22'},\n", - " {'id': 'link66', 'source': 'fr_developing_symptoms', 'target': 'flow22'},\n", - " {'id': 'link67', 'source': 'infection_rate_sum', 'target': 'flow22'},\n", - " {'id': 'link68', 'source': 'fraction_q_sym', 'target': 'flow24'},\n", - " {'id': 'link69', 'source': 'delay_q_sym', 'target': 'flow24'},\n", - " {'id': 'link70', 'source': 'symptom_setting', 'target': 'flow24'},\n", - " {'id': 'link71', 'source': 'fr_becoming_serious', 'target': 'flow25'},\n", - " {'id': 'link72', 'source': 'symptom_setting', 'target': 'flow25'},\n", - " {'id': 'link73', 'source': 'q_sym', 'target': 'flow25'},\n", - " {'id': 'link74', 'source': 'delay_recovery_sym_mild', 'target': 'flow25'},\n", - " {'id': 'link75', 'source': 'fr_iso', 'target': 'flow25'},\n", - " {'id': 'link76', 'source': 'isolation_rate_asym', 'target': 'flow26'},\n", - " {'id': 'link77',\n", - " 'source': 'net_delay_incubation_iso_asym',\n", - " 'target': 'flow26'},\n", + " {'id': 'link66', 'source': 'q_asym', 'target': 'flow22'},\n", + " {'id': 'link67', 'source': 'fr_developing_symptoms', 'target': 'flow22'},\n", + " {'id': 'link68', 'source': 'fr_iso', 'target': 'flow22'},\n", + " {'id': 'link69', 'source': 'fraction_q_sym', 'target': 'flow24'},\n", + " {'id': 'link70', 'source': 'delay_q_sym', 'target': 'flow24'},\n", + " {'id': 'link71', 'source': 'symptom_setting', 'target': 'flow24'},\n", + " {'id': 'link72', 'source': 'q_sym', 'target': 'flow25'},\n", + " {'id': 'link73', 'source': 'fr_becoming_serious', 'target': 'flow25'},\n", + " {'id': 'link74', 'source': 'symptom_setting', 'target': 'flow25'},\n", + " {'id': 'link75', 'source': 'delay_recovery_sym_mild', 'target': 'flow25'},\n", + " {'id': 'link76', 'source': 'fr_iso', 'target': 'flow25'},\n", + " {'id': 'link77', 'source': 'isolation_rate_asym', 'target': 'flow26'},\n", " {'id': 'link78', 'source': 'fr_developing_symptoms', 'target': 'flow26'},\n", " {'id': 'link79',\n", - " 'source': 'net_delay_recovery_iso_asym',\n", - " 'target': 'flow27'},\n", + " 'source': 'net_delay_incubation_iso_asym',\n", + " 'target': 'flow26'},\n", " {'id': 'link80', 'source': 'isolation_rate_asym', 'target': 'flow27'},\n", " {'id': 'link81', 'source': 'fr_developing_symptoms', 'target': 'flow27'},\n", - " {'id': 'link82', 'source': 'into_iso_sym', 'target': 'flow28'},\n", + " {'id': 'link82',\n", + " 'source': 'net_delay_recovery_iso_asym',\n", + " 'target': 'flow27'},\n", " {'id': 'link83', 'source': 'fr_becoming_serious', 'target': 'flow28'},\n", - " {'id': 'link84',\n", + " {'id': 'link84', 'source': 'into_iso_sym', 'target': 'flow28'},\n", + " {'id': 'link85',\n", " 'source': 'net_delay_recovery_iso_sym',\n", " 'target': 'flow28'},\n", - " {'id': 'link85',\n", - " 'source': 'net_delay_incubation_q_asym',\n", - " 'target': 'flow29'},\n", " {'id': 'link86', 'source': 'quarantining_rate_asym', 'target': 'flow29'},\n", " {'id': 'link87', 'source': 'fr_developing_symptoms', 'target': 'flow29'},\n", - " {'id': 'link88', 'source': 'net_delay_recovery_q_asym', 'target': 'flow30'},\n", + " {'id': 'link88',\n", + " 'source': 'net_delay_incubation_q_asym',\n", + " 'target': 'flow29'},\n", " {'id': 'link89', 'source': 'quarantining_rate_asym', 'target': 'flow30'},\n", - " {'id': 'link90', 'source': 'fr_developing_symptoms', 'target': 'flow30'},\n", - " {'id': 'link91', 'source': 'net_delay_recovery_q_sym', 'target': 'flow31'},\n", + " {'id': 'link90', 'source': 'net_delay_recovery_q_asym', 'target': 'flow30'},\n", + " {'id': 'link91', 'source': 'fr_developing_symptoms', 'target': 'flow30'},\n", " {'id': 'link92', 'source': 'fr_becoming_serious', 'target': 'flow31'},\n", - " {'id': 'link93', 'source': 'into_q_sym', 'target': 'flow31'}]},\n", + " {'id': 'link93', 'source': 'into_q_sym', 'target': 'flow31'},\n", + " {'id': 'link94',\n", + " 'source': 'net_delay_recovery_q_sym',\n", + " 'target': 'flow31'}]},\n", " 'semantics': {'ode': {'parameters': [{'id': 'isolation_rate_sym',\n", " 'value': 0.0,\n", " 'units': {'expression': 'person/day',\n", diff --git a/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json b/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json index f073e4baf..5d39d5425 100644 --- a/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json +++ b/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json @@ -150,8 +150,8 @@ "name": "infection_rate_new_arrivals", "upstream_stock": null, "downstream_stock": "infectious_asymptomatics", - "rate_expression": "piecewise((0.0, False), (2.0, False), (1.0, False), (0.0, False), (2.0, False), (1.0, False), (2.0, False), (3.0, False), (6.0, False), (2.0, False), (7.0, False), (10.0, False), (4.0, False), (10.0, False), (5.0, False), (11.0, False), (14.0, False), (14.0, False), (26.0, False), (34.0, False), (35.0, False), (45.0, False), (55.0, False), (38.0, False), (34.0, False), (24.0, False), (40.0, False), (16.0, False), (20.0, False), (12.0, False), (23.0, False), (14.0, False), (8.0, False), (14.0, False), (12.0, False), (5.0, False), (9.0, False), (6.0, False), (0.0, False), (0.0, False), (0.0, False), (0.0, False))", - "rate_expression_mathml": "0.02.01.00.02.01.02.03.06.02.07.010.04.010.05.011.014.014.026.034.035.045.055.038.034.024.040.016.020.012.023.014.08.014.012.05.09.06.00.00.00.00.0" + "rate_expression": "piecewise((0.0, time >= 0.0), (2.0, time >= 1.0), (1.0, time >= 2.0), (0.0, time >= 3.0), (2.0, time >= 4.0), (1.0, time >= 5.0), (2.0, time >= 6.0), (3.0, time >= 7.0), (6.0, time >= 8.0), (2.0, time >= 9.0), (7.0, time >= 10.0), (10.0, time >= 11.0), (4.0, time >= 12.0), (10.0, time >= 13.0), (5.0, time >= 14.0), (11.0, time >= 15.0), (14.0, time >= 16.0), (14.0, time >= 17.0), (26.0, time >= 18.0), (34.0, time >= 19.0), (35.0, time >= 20.0), (45.0, time >= 21.0), (55.0, time >= 22.0), (38.0, time >= 23.0), (34.0, time >= 24.0), (24.0, time >= 25.0), (40.0, time >= 26.0), (16.0, time >= 27.0), (20.0, time >= 28.0), (12.0, time >= 29.0), (23.0, time >= 30.0), (14.0, time >= 31.0), (8.0, time >= 32.0), (14.0, time >= 33.0), (12.0, time >= 34.0), (5.0, time >= 35.0), (9.0, time >= 36.0), (6.0, time >= 37.0), (0.0, time >= 38.0), (0.0, time >= 39.0), (0.0, time >= 40.0), (0.0, time >= 1000.0))", + "rate_expression_mathml": "0.0time0.02.0time1.01.0time2.00.0time3.02.0time4.01.0time5.02.0time6.03.0time7.06.0time8.02.0time9.07.0time10.010.0time11.04.0time12.010.0time13.05.0time14.011.0time15.014.0time16.014.0time17.026.0time18.034.0time19.035.0time20.045.0time21.055.0time22.038.0time23.034.0time24.024.0time25.040.0time26.016.0time27.020.0time28.012.0time29.023.0time30.014.0time31.08.0time32.014.0time33.012.0time34.05.0time35.09.0time36.06.0time37.00.0time38.00.0time39.00.0time40.00.0time1000.0" }, { "id": "19", @@ -671,17 +671,17 @@ "links": [ { "id": "link1", - "source": "isolation_rate_sym", + "source": "isolation_rate_asym", "target": "flow1" }, { "id": "link2", - "source": "isolation_rate_asym", + "source": "disease_progression", "target": "flow1" }, { "id": "link3", - "source": "disease_progression", + "source": "isolation_rate_sym", "target": "flow1" }, { @@ -691,7 +691,7 @@ }, { "id": "link5", - "source": "fr_fatality", + "source": "delay_for_death", "target": "flow2" }, { @@ -701,7 +701,7 @@ }, { "id": "link7", - "source": "delay_for_death", + "source": "fr_fatality", "target": "flow2" }, { @@ -711,12 +711,12 @@ }, { "id": "link9", - "source": "net_fr_fatality", + "source": "delay_for_death", "target": "flow3" }, { "id": "link10", - "source": "delay_for_death", + "source": "net_fr_fatality", "target": "flow3" }, { @@ -746,7 +746,7 @@ }, { "id": "link16", - "source": "delay_disease_diagnosis", + "source": "q_sym", "target": "flow6" }, { @@ -756,12 +756,12 @@ }, { "id": "link18", - "source": "symptom_setting", + "source": "delay_disease_diagnosis", "target": "flow6" }, { "id": "link19", - "source": "q_sym", + "source": "symptom_setting", "target": "flow6" }, { @@ -771,12 +771,12 @@ }, { "id": "link21", - "source": "into_iso_sym", + "source": "fr_becoming_serious", "target": "flow7" }, { "id": "link22", - "source": "fr_becoming_serious", + "source": "into_iso_sym", "target": "flow7" }, { @@ -841,22 +841,22 @@ }, { "id": "link35", - "source": "fr_fatality", + "source": "icu_admissions", "target": "flow12" }, { "id": "link36", - "source": "delay_recovery_sym_extreme", + "source": "fr_fatality", "target": "flow12" }, { "id": "link37", - "source": "icu_admissions", + "source": "delay_recovery_sym_extreme", "target": "flow12" }, { "id": "link38", - "source": "hospital_overflow", + "source": "delay_recovery_sym_serious", "target": "flow13" }, { @@ -866,12 +866,12 @@ }, { "id": "link40", - "source": "delay_recovery_sym_serious", + "source": "hospital_overflow", "target": "flow13" }, { "id": "link41", - "source": "hospital_overflow", + "source": "delay_worsening", "target": "flow14" }, { @@ -881,7 +881,7 @@ }, { "id": "link43", - "source": "delay_worsening", + "source": "hospital_overflow", "target": "flow14" }, { @@ -926,23 +926,23 @@ }, { "id": "link52", - "source": "fraction_iso_asym", - "target": "flow19" + "source": "time", + "target": "flow18" }, { "id": "link53", - "source": "infection_rate_sum", + "source": "fraction_iso_asym", "target": "flow19" }, { "id": "link54", - "source": "delay_iso_asym", + "source": "infection_rate_sum", "target": "flow19" }, { "id": "link55", - "source": "delay_q_asym", - "target": "flow20" + "source": "delay_iso_asym", + "target": "flow19" }, { "id": "link56", @@ -951,107 +951,107 @@ }, { "id": "link57", - "source": "fraction_q_asym", + "source": "delay_q_asym", "target": "flow20" }, { "id": "link58", - "source": "delay_recovery_asym", - "target": "flow21" + "source": "fraction_q_asym", + "target": "flow20" }, { "id": "link59", - "source": "q_asym", + "source": "infection_rate_sum", "target": "flow21" }, { "id": "link60", - "source": "fr_iso", + "source": "q_asym", "target": "flow21" }, { "id": "link61", - "source": "fr_developing_symptoms", + "source": "delay_recovery_asym", "target": "flow21" }, { "id": "link62", - "source": "infection_rate_sum", + "source": "fr_developing_symptoms", "target": "flow21" }, { "id": "link63", - "source": "q_asym", - "target": "flow22" + "source": "fr_iso", + "target": "flow21" }, { "id": "link64", - "source": "delay_asymp_infective_to_symp", + "source": "infection_rate_sum", "target": "flow22" }, { "id": "link65", - "source": "fr_iso", + "source": "delay_asymp_infective_to_symp", "target": "flow22" }, { "id": "link66", - "source": "fr_developing_symptoms", + "source": "q_asym", "target": "flow22" }, { "id": "link67", - "source": "infection_rate_sum", + "source": "fr_developing_symptoms", "target": "flow22" }, { "id": "link68", - "source": "fraction_q_sym", - "target": "flow24" + "source": "fr_iso", + "target": "flow22" }, { "id": "link69", - "source": "delay_q_sym", + "source": "fraction_q_sym", "target": "flow24" }, { "id": "link70", - "source": "symptom_setting", + "source": "delay_q_sym", "target": "flow24" }, { "id": "link71", - "source": "fr_becoming_serious", - "target": "flow25" + "source": "symptom_setting", + "target": "flow24" }, { "id": "link72", - "source": "symptom_setting", + "source": "q_sym", "target": "flow25" }, { "id": "link73", - "source": "q_sym", + "source": "fr_becoming_serious", "target": "flow25" }, { "id": "link74", - "source": "delay_recovery_sym_mild", + "source": "symptom_setting", "target": "flow25" }, { "id": "link75", - "source": "fr_iso", + "source": "delay_recovery_sym_mild", "target": "flow25" }, { "id": "link76", - "source": "isolation_rate_asym", - "target": "flow26" + "source": "fr_iso", + "target": "flow25" }, { "id": "link77", - "source": "net_delay_incubation_iso_asym", + "source": "isolation_rate_asym", "target": "flow26" }, { @@ -1061,8 +1061,8 @@ }, { "id": "link79", - "source": "net_delay_recovery_iso_asym", - "target": "flow27" + "source": "net_delay_incubation_iso_asym", + "target": "flow26" }, { "id": "link80", @@ -1076,8 +1076,8 @@ }, { "id": "link82", - "source": "into_iso_sym", - "target": "flow28" + "source": "net_delay_recovery_iso_asym", + "target": "flow27" }, { "id": "link83", @@ -1086,13 +1086,13 @@ }, { "id": "link84", - "source": "net_delay_recovery_iso_sym", + "source": "into_iso_sym", "target": "flow28" }, { "id": "link85", - "source": "net_delay_incubation_q_asym", - "target": "flow29" + "source": "net_delay_recovery_iso_sym", + "target": "flow28" }, { "id": "link86", @@ -1106,8 +1106,8 @@ }, { "id": "link88", - "source": "net_delay_recovery_q_asym", - "target": "flow30" + "source": "net_delay_incubation_q_asym", + "target": "flow29" }, { "id": "link89", @@ -1116,13 +1116,13 @@ }, { "id": "link90", - "source": "fr_developing_symptoms", + "source": "net_delay_recovery_q_asym", "target": "flow30" }, { "id": "link91", - "source": "net_delay_recovery_q_sym", - "target": "flow31" + "source": "fr_developing_symptoms", + "target": "flow30" }, { "id": "link92", @@ -1133,6 +1133,11 @@ "id": "link93", "source": "into_q_sym", "target": "flow31" + }, + { + "id": "link94", + "source": "net_delay_recovery_q_sym", + "target": "flow31" } ] }, From 6af86cbbed399a9fc623520012b81ee08d60658e Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Wed, 28 Feb 2024 15:02:13 +0100 Subject: [PATCH 6/9] Handle issues with parsing ampersand character --- mira/sources/system_dynamics/pysd.py | 2 + .../scenario5/Scenario 5 Notebook.ipynb | 236 +++++++++-------- .../scenario5/scenario_5_stockflow.json | 243 +++++++++--------- 3 files changed, 253 insertions(+), 228 deletions(-) diff --git a/mira/sources/system_dynamics/pysd.py b/mira/sources/system_dynamics/pysd.py index 7c1b99cae..bc86d22b4 100644 --- a/mira/sources/system_dynamics/pysd.py +++ b/mira/sources/system_dynamics/pysd.py @@ -444,12 +444,14 @@ def preprocess_expression_text(expr_text): # strip leading and trailing white spaces # replace space between two words that makeup a variable name with "_"' # replace single and doubel quotation marks + # replace ampersand & with and expr_text = ( expr_text.strip() .replace("^", "**") .replace(" ", "_") .replace("'", "") .replace('"', "") + .replace("&", "_") .lower() ) return expr_text diff --git a/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb b/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb index f5921134c..521b506a4 100644 --- a/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb +++ b/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb @@ -96,7 +96,22 @@ "execution_count": 5, "id": "943251a0-4d0d-4398-9042-37faa48631af", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ERROR\n", + "\t physical_distance_impact\n", + "\t piecewise((log(density_ratio,density_ratio<=1,1),(50)+1,true))\n", + "\t log takes at least 1 argument (3 given)\n", + "ERROR\n", + "\t effect_of_density_on_contacts\n", + "\t piecewise((log(density_ratio,density_ratio<=1,1),(50)+1,true))\n", + "\t log takes at least 1 argument (3 given)\n" + ] + } + ], "source": [ "hackathon_file_path = \"IndiaNonSubscriptedPulsed.mdl\"\n", "hackathon_file_url = (\n", @@ -206,33 +221,33 @@ " ...\n", " \n", " \n", - " 82\n", + " 79\n", " sum_in_icu_overflow\n", " 0.0\n", " \n", " \n", - " 83\n", + " 80\n", " sum_quarantine_asym\n", " 0.0\n", " \n", " \n", - " 84\n", + " 81\n", " sum_recovered_asym\n", " 0.0\n", " \n", " \n", - " 85\n", + " 82\n", " sum_recovered_mild\n", " 0.0\n", " \n", " \n", - " 86\n", + " 83\n", " sumnoninfectives\n", - " 5.0\n", + " 0.0\n", " \n", " \n", "\n", - "

87 rows × 2 columns

\n", + "

84 rows × 2 columns

\n", "" ], "text/plain": [ @@ -243,13 +258,13 @@ "3 policy_high_contact_tracing_and_isolation 0.0\n", "4 base_contacts_other 6.0\n", ".. ... ...\n", - "82 sum_in_icu_overflow 0.0\n", - "83 sum_quarantine_asym 0.0\n", - "84 sum_recovered_asym 0.0\n", - "85 sum_recovered_mild 0.0\n", - "86 sumnoninfectives 5.0\n", + "79 sum_in_icu_overflow 0.0\n", + "80 sum_quarantine_asym 0.0\n", + "81 sum_recovered_asym 0.0\n", + "82 sum_recovered_mild 0.0\n", + "83 sumnoninfectives 0.0\n", "\n", - "[87 rows x 2 columns]" + "[84 rows x 2 columns]" ] }, "execution_count": 7, @@ -318,7 +333,7 @@ " \n", " 2\n", " exposed\n", - " 5.0\n", + " 0.0\n", " \n", " \n", " 3\n", @@ -408,7 +423,7 @@ " name expression\n", "0 cumulative_cases_reported 0.0\n", "1 dead 0.0\n", - "2 exposed 5.0\n", + "2 exposed 0.0\n", "3 incoming_demand_on_hospital 0.0\n", "4 infected_sym_extreme_icu 0.0\n", "5 infected_sym_hospital_overflow 0.0\n", @@ -619,7 +634,7 @@ " 22\n", " 23\n", " isolation_rate_sym\n", - " None\n", + " fr_symptomatic_tested_isolated*delay3i(symptom...\n", " \n", " \n", " 23\n", @@ -730,7 +745,7 @@ "19 fraction_q_asym*delay3i(infection_rate_sum, de... \n", "20 (1 - fr_developing_symptoms)*(-fr_iso + q_asym... \n", "21 fr_developing_symptoms*(-fr_iso + q_asym + 1)*... \n", - "22 None \n", + "22 fr_symptomatic_tested_isolated*delay3i(symptom... \n", "23 fraction_q_sym*delay3i(symptom_setting, delay_... \n", "24 (1 - Min(0.9, fr_becoming_serious))*(-fr_iso +... \n", "25 fr_developing_symptoms*delay3i(isolation_rate_... \n", @@ -912,7 +927,9 @@ " {'id': '23',\n", " 'name': 'isolation_rate_sym',\n", " 'upstream_stock': 'infectious_symptomatics_mild',\n", - " 'downstream_stock': 'isolated_symtomatics_mild'},\n", + " 'downstream_stock': 'isolated_symtomatics_mild',\n", + " 'rate_expression': 'fr_symptomatic_tested_isolated*delay3i(symptom_setting, delay_iso_sym, 0)',\n", + " 'rate_expression_mathml': 'fr_symptomatic_tested_isolatedsymptom_settingdelay_iso_sym0'},\n", " {'id': '24',\n", " 'name': 'quarantine_rate_sym',\n", " 'upstream_stock': 'infectious_symptomatics_mild',\n", @@ -1078,11 +1095,7 @@ " 'grounding': {'identifiers': {'ido': '0000592'},\n", " 'modifiers': {'isolation': 'ncit:C25549',\n", " 'disease_severity': 'ncit:C70666'}}}],\n", - " 'auxiliaries': [{'id': 'isolation_rate_sym',\n", - " 'name': 'isolation_rate_sym',\n", - " 'expression': 'isolation_rate_sym',\n", - " 'expression_mathml': 'isolation_rate_sym'},\n", - " {'id': 'delay_for_death',\n", + " 'auxiliaries': [{'id': 'delay_for_death',\n", " 'name': 'delay_for_death',\n", " 'expression': 'delay_for_death',\n", " 'expression_mathml': 'delay_for_death'},\n", @@ -1138,6 +1151,10 @@ " 'name': 'delay_asymp_infective_to_symp',\n", " 'expression': 'delay_asymp_infective_to_symp',\n", " 'expression_mathml': 'delay_asymp_infective_to_symp'},\n", + " {'id': 'delay_iso_sym',\n", + " 'name': 'delay_iso_sym',\n", + " 'expression': 'delay_iso_sym',\n", + " 'expression_mathml': 'delay_iso_sym'},\n", " {'id': 'delay_q_sym',\n", " 'name': 'delay_q_sym',\n", " 'expression': 'delay_q_sym',\n", @@ -1151,49 +1168,49 @@ " 'expression': 'delay_recovery_sym_mild',\n", " 'expression_mathml': 'delay_recovery_sym_mild'}],\n", " 'links': [{'id': 'link1',\n", - " 'source': 'isolation_rate_asym',\n", + " 'source': 'q_disease_progress_rate',\n", " 'target': 'flow1'},\n", - " {'id': 'link2', 'source': 'disease_progression', 'target': 'flow1'},\n", - " {'id': 'link3', 'source': 'isolation_rate_sym', 'target': 'flow1'},\n", - " {'id': 'link4', 'source': 'q_disease_progress_rate', 'target': 'flow1'},\n", - " {'id': 'link5', 'source': 'delay_for_death', 'target': 'flow2'},\n", - " {'id': 'link6', 'source': 'icu_admissions', 'target': 'flow2'},\n", + " {'id': 'link2', 'source': 'isolation_rate_sym', 'target': 'flow1'},\n", + " {'id': 'link3', 'source': 'disease_progression', 'target': 'flow1'},\n", + " {'id': 'link4', 'source': 'isolation_rate_asym', 'target': 'flow1'},\n", + " {'id': 'link5', 'source': 'icu_admissions', 'target': 'flow2'},\n", + " {'id': 'link6', 'source': 'delay_for_death', 'target': 'flow2'},\n", " {'id': 'link7', 'source': 'fr_fatality', 'target': 'flow2'},\n", - " {'id': 'link8', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow3'},\n", - " {'id': 'link9', 'source': 'delay_for_death', 'target': 'flow3'},\n", + " {'id': 'link8', 'source': 'delay_for_death', 'target': 'flow3'},\n", + " {'id': 'link9', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow3'},\n", " {'id': 'link10', 'source': 'net_fr_fatality', 'target': 'flow3'},\n", " {'id': 'link11', 'source': 'infectivity', 'target': 'flow4'},\n", - " {'id': 'link12',\n", + " {'id': 'link12', 'source': 'susceptibles', 'target': 'flow4'},\n", + " {'id': 'link13',\n", " 'source': 'contacts_total_per_susceptible',\n", " 'target': 'flow4'},\n", - " {'id': 'link13', 'source': 'susceptibles', 'target': 'flow4'},\n", - " {'id': 'link14',\n", + " {'id': 'link14', 'source': 'exposure', 'target': 'flow5'},\n", + " {'id': 'link15',\n", " 'source': 'delay_asymp_noninfective_to_infective',\n", " 'target': 'flow5'},\n", - " {'id': 'link15', 'source': 'exposure', 'target': 'flow5'},\n", " {'id': 'link16', 'source': 'q_sym', 'target': 'flow6'},\n", - " {'id': 'link17', 'source': 'fr_becoming_serious', 'target': 'flow6'},\n", - " {'id': 'link18', 'source': 'delay_disease_diagnosis', 'target': 'flow6'},\n", - " {'id': 'link19', 'source': 'symptom_setting', 'target': 'flow6'},\n", - " {'id': 'link20', 'source': 'fr_iso', 'target': 'flow6'},\n", - " {'id': 'link21', 'source': 'fr_becoming_serious', 'target': 'flow7'},\n", - " {'id': 'link22', 'source': 'into_iso_sym', 'target': 'flow7'},\n", + " {'id': 'link17', 'source': 'symptom_setting', 'target': 'flow6'},\n", + " {'id': 'link18', 'source': 'fr_iso', 'target': 'flow6'},\n", + " {'id': 'link19', 'source': 'fr_becoming_serious', 'target': 'flow6'},\n", + " {'id': 'link20', 'source': 'delay_disease_diagnosis', 'target': 'flow6'},\n", + " {'id': 'link21', 'source': 'into_iso_sym', 'target': 'flow7'},\n", + " {'id': 'link22', 'source': 'fr_becoming_serious', 'target': 'flow7'},\n", " {'id': 'link23',\n", " 'source': 'net_delay_dprogress_iso_sym',\n", " 'target': 'flow7'},\n", - " {'id': 'link24', 'source': 'fr_becoming_serious', 'target': 'flow8'},\n", - " {'id': 'link25', 'source': 'into_q_sym', 'target': 'flow8'},\n", - " {'id': 'link26', 'source': 'net_delay_dprogress_q_sym', 'target': 'flow8'},\n", - " {'id': 'link27', 'source': 'time_step', 'target': 'flow9'},\n", - " {'id': 'link28', 'source': 'managable_hospital_inflow', 'target': 'flow9'},\n", - " {'id': 'link29',\n", + " {'id': 'link24', 'source': 'into_q_sym', 'target': 'flow8'},\n", + " {'id': 'link25', 'source': 'net_delay_dprogress_q_sym', 'target': 'flow8'},\n", + " {'id': 'link26', 'source': 'fr_becoming_serious', 'target': 'flow8'},\n", + " {'id': 'link27',\n", " 'source': 'incoming_demand_on_hospital',\n", " 'target': 'flow9'},\n", - " {'id': 'link30', 'source': 'time_step', 'target': 'flow10'},\n", - " {'id': 'link31', 'source': 'managable_hospital_inflow', 'target': 'flow10'},\n", - " {'id': 'link32',\n", + " {'id': 'link28', 'source': 'managable_hospital_inflow', 'target': 'flow9'},\n", + " {'id': 'link29', 'source': 'time_step', 'target': 'flow9'},\n", + " {'id': 'link30',\n", " 'source': 'incoming_demand_on_hospital',\n", " 'target': 'flow10'},\n", + " {'id': 'link31', 'source': 'managable_hospital_inflow', 'target': 'flow10'},\n", + " {'id': 'link32', 'source': 'time_step', 'target': 'flow10'},\n", " {'id': 'link33', 'source': 'managable_icu_inflow', 'target': 'flow11'},\n", " {'id': 'link34', 'source': 'typical_worsening_rate', 'target': 'flow11'},\n", " {'id': 'link35', 'source': 'icu_admissions', 'target': 'flow12'},\n", @@ -1201,14 +1218,14 @@ " {'id': 'link37',\n", " 'source': 'delay_recovery_sym_extreme',\n", " 'target': 'flow12'},\n", - " {'id': 'link38',\n", + " {'id': 'link38', 'source': 'net_fr_requiring_icu', 'target': 'flow13'},\n", + " {'id': 'link39', 'source': 'hospital_overflow', 'target': 'flow13'},\n", + " {'id': 'link40',\n", " 'source': 'delay_recovery_sym_serious',\n", " 'target': 'flow13'},\n", - " {'id': 'link39', 'source': 'net_fr_requiring_icu', 'target': 'flow13'},\n", - " {'id': 'link40', 'source': 'hospital_overflow', 'target': 'flow13'},\n", - " {'id': 'link41', 'source': 'delay_worsening', 'target': 'flow14'},\n", - " {'id': 'link42', 'source': 'net_fr_requiring_icu', 'target': 'flow14'},\n", - " {'id': 'link43', 'source': 'hospital_overflow', 'target': 'flow14'},\n", + " {'id': 'link41', 'source': 'net_fr_requiring_icu', 'target': 'flow14'},\n", + " {'id': 'link42', 'source': 'hospital_overflow', 'target': 'flow14'},\n", + " {'id': 'link43', 'source': 'delay_worsening', 'target': 'flow14'},\n", " {'id': 'link44', 'source': 'managable_icu_inflow', 'target': 'flow15'},\n", " {'id': 'link45', 'source': 'typical_worsening_rate', 'target': 'flow15'},\n", " {'id': 'link46', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow16'},\n", @@ -1222,65 +1239,66 @@ " 'source': 'delay_recovery_sym_serious',\n", " 'target': 'flow17'},\n", " {'id': 'link52', 'source': 'time', 'target': 'flow18'},\n", - " {'id': 'link53', 'source': 'fraction_iso_asym', 'target': 'flow19'},\n", - " {'id': 'link54', 'source': 'infection_rate_sum', 'target': 'flow19'},\n", - " {'id': 'link55', 'source': 'delay_iso_asym', 'target': 'flow19'},\n", - " {'id': 'link56', 'source': 'infection_rate_sum', 'target': 'flow20'},\n", - " {'id': 'link57', 'source': 'delay_q_asym', 'target': 'flow20'},\n", - " {'id': 'link58', 'source': 'fraction_q_asym', 'target': 'flow20'},\n", - " {'id': 'link59', 'source': 'infection_rate_sum', 'target': 'flow21'},\n", + " {'id': 'link53', 'source': 'infection_rate_sum', 'target': 'flow19'},\n", + " {'id': 'link54', 'source': 'delay_iso_asym', 'target': 'flow19'},\n", + " {'id': 'link55', 'source': 'fraction_iso_asym', 'target': 'flow19'},\n", + " {'id': 'link56', 'source': 'delay_q_asym', 'target': 'flow20'},\n", + " {'id': 'link57', 'source': 'fraction_q_asym', 'target': 'flow20'},\n", + " {'id': 'link58', 'source': 'infection_rate_sum', 'target': 'flow20'},\n", + " {'id': 'link59', 'source': 'delay_recovery_asym', 'target': 'flow21'},\n", " {'id': 'link60', 'source': 'q_asym', 'target': 'flow21'},\n", - " {'id': 'link61', 'source': 'delay_recovery_asym', 'target': 'flow21'},\n", - " {'id': 'link62', 'source': 'fr_developing_symptoms', 'target': 'flow21'},\n", - " {'id': 'link63', 'source': 'fr_iso', 'target': 'flow21'},\n", - " {'id': 'link64', 'source': 'infection_rate_sum', 'target': 'flow22'},\n", - " {'id': 'link65',\n", + " {'id': 'link61', 'source': 'fr_iso', 'target': 'flow21'},\n", + " {'id': 'link62', 'source': 'infection_rate_sum', 'target': 'flow21'},\n", + " {'id': 'link63', 'source': 'fr_developing_symptoms', 'target': 'flow21'},\n", + " {'id': 'link64',\n", " 'source': 'delay_asymp_infective_to_symp',\n", " 'target': 'flow22'},\n", - " {'id': 'link66', 'source': 'q_asym', 'target': 'flow22'},\n", - " {'id': 'link67', 'source': 'fr_developing_symptoms', 'target': 'flow22'},\n", - " {'id': 'link68', 'source': 'fr_iso', 'target': 'flow22'},\n", - " {'id': 'link69', 'source': 'fraction_q_sym', 'target': 'flow24'},\n", - " {'id': 'link70', 'source': 'delay_q_sym', 'target': 'flow24'},\n", - " {'id': 'link71', 'source': 'symptom_setting', 'target': 'flow24'},\n", - " {'id': 'link72', 'source': 'q_sym', 'target': 'flow25'},\n", - " {'id': 'link73', 'source': 'fr_becoming_serious', 'target': 'flow25'},\n", - " {'id': 'link74', 'source': 'symptom_setting', 'target': 'flow25'},\n", - " {'id': 'link75', 'source': 'delay_recovery_sym_mild', 'target': 'flow25'},\n", - " {'id': 'link76', 'source': 'fr_iso', 'target': 'flow25'},\n", - " {'id': 'link77', 'source': 'isolation_rate_asym', 'target': 'flow26'},\n", - " {'id': 'link78', 'source': 'fr_developing_symptoms', 'target': 'flow26'},\n", - " {'id': 'link79',\n", + " {'id': 'link65', 'source': 'q_asym', 'target': 'flow22'},\n", + " {'id': 'link66', 'source': 'fr_iso', 'target': 'flow22'},\n", + " {'id': 'link67', 'source': 'infection_rate_sum', 'target': 'flow22'},\n", + " {'id': 'link68', 'source': 'fr_developing_symptoms', 'target': 'flow22'},\n", + " {'id': 'link69', 'source': 'symptom_setting', 'target': 'flow23'},\n", + " {'id': 'link70',\n", + " 'source': 'fr_symptomatic_tested_isolated',\n", + " 'target': 'flow23'},\n", + " {'id': 'link71', 'source': 'delay_iso_sym', 'target': 'flow23'},\n", + " {'id': 'link72', 'source': 'symptom_setting', 'target': 'flow24'},\n", + " {'id': 'link73', 'source': 'fraction_q_sym', 'target': 'flow24'},\n", + " {'id': 'link74', 'source': 'delay_q_sym', 'target': 'flow24'},\n", + " {'id': 'link75', 'source': 'q_sym', 'target': 'flow25'},\n", + " {'id': 'link76', 'source': 'symptom_setting', 'target': 'flow25'},\n", + " {'id': 'link77', 'source': 'delay_recovery_sym_mild', 'target': 'flow25'},\n", + " {'id': 'link78', 'source': 'fr_iso', 'target': 'flow25'},\n", + " {'id': 'link79', 'source': 'fr_becoming_serious', 'target': 'flow25'},\n", + " {'id': 'link80', 'source': 'fr_developing_symptoms', 'target': 'flow26'},\n", + " {'id': 'link81', 'source': 'isolation_rate_asym', 'target': 'flow26'},\n", + " {'id': 'link82',\n", " 'source': 'net_delay_incubation_iso_asym',\n", " 'target': 'flow26'},\n", - " {'id': 'link80', 'source': 'isolation_rate_asym', 'target': 'flow27'},\n", - " {'id': 'link81', 'source': 'fr_developing_symptoms', 'target': 'flow27'},\n", - " {'id': 'link82',\n", + " {'id': 'link83',\n", " 'source': 'net_delay_recovery_iso_asym',\n", " 'target': 'flow27'},\n", - " {'id': 'link83', 'source': 'fr_becoming_serious', 'target': 'flow28'},\n", - " {'id': 'link84', 'source': 'into_iso_sym', 'target': 'flow28'},\n", - " {'id': 'link85',\n", + " {'id': 'link84', 'source': 'fr_developing_symptoms', 'target': 'flow27'},\n", + " {'id': 'link85', 'source': 'isolation_rate_asym', 'target': 'flow27'},\n", + " {'id': 'link86',\n", " 'source': 'net_delay_recovery_iso_sym',\n", " 'target': 'flow28'},\n", - " {'id': 'link86', 'source': 'quarantining_rate_asym', 'target': 'flow29'},\n", - " {'id': 'link87', 'source': 'fr_developing_symptoms', 'target': 'flow29'},\n", - " {'id': 'link88',\n", + " {'id': 'link87', 'source': 'into_iso_sym', 'target': 'flow28'},\n", + " {'id': 'link88', 'source': 'fr_becoming_serious', 'target': 'flow28'},\n", + " {'id': 'link89',\n", " 'source': 'net_delay_incubation_q_asym',\n", " 'target': 'flow29'},\n", - " {'id': 'link89', 'source': 'quarantining_rate_asym', 'target': 'flow30'},\n", - " {'id': 'link90', 'source': 'net_delay_recovery_q_asym', 'target': 'flow30'},\n", - " {'id': 'link91', 'source': 'fr_developing_symptoms', 'target': 'flow30'},\n", - " {'id': 'link92', 'source': 'fr_becoming_serious', 'target': 'flow31'},\n", - " {'id': 'link93', 'source': 'into_q_sym', 'target': 'flow31'},\n", - " {'id': 'link94',\n", + " {'id': 'link90', 'source': 'fr_developing_symptoms', 'target': 'flow29'},\n", + " {'id': 'link91', 'source': 'quarantining_rate_asym', 'target': 'flow29'},\n", + " {'id': 'link92', 'source': 'net_delay_recovery_q_asym', 'target': 'flow30'},\n", + " {'id': 'link93', 'source': 'fr_developing_symptoms', 'target': 'flow30'},\n", + " {'id': 'link94', 'source': 'quarantining_rate_asym', 'target': 'flow30'},\n", + " {'id': 'link95', 'source': 'into_q_sym', 'target': 'flow31'},\n", + " {'id': 'link96', 'source': 'fr_becoming_serious', 'target': 'flow31'},\n", + " {'id': 'link97',\n", " 'source': 'net_delay_recovery_q_sym',\n", " 'target': 'flow31'}]},\n", - " 'semantics': {'ode': {'parameters': [{'id': 'isolation_rate_sym',\n", - " 'value': 0.0,\n", - " 'units': {'expression': 'person/day',\n", - " 'expression_mathml': 'personday'}},\n", - " {'id': 'delay_for_death',\n", + " 'semantics': {'ode': {'parameters': [{'id': 'delay_for_death',\n", " 'value': 5.0,\n", " 'units': {'expression': 'day', 'expression_mathml': 'day'}},\n", " {'id': 'fr_fatality',\n", @@ -1323,6 +1341,9 @@ " {'id': 'delay_asymp_infective_to_symp',\n", " 'value': 2.0,\n", " 'units': {'expression': 'day', 'expression_mathml': 'day'}},\n", + " {'id': 'delay_iso_sym',\n", + " 'value': 2.5,\n", + " 'units': {'expression': 'day', 'expression_mathml': 'day'}},\n", " {'id': 'delay_q_sym',\n", " 'value': 2.5,\n", " 'units': {'expression': 'day', 'expression_mathml': 'day'}},\n", @@ -1367,9 +1388,6 @@ " {'id': 'area_of_states', 'value': 1.0},\n", " {'id': 'ppe_availability', 'value': 0.8},\n", " {'id': 'fr_q_asym_via_awareness', 'value': 0.0},\n", - " {'id': 'fr_q_asym_via_tracing', 'value': 0.0},\n", - " {'id': 'delay_iso_sym', 'value': 2.5},\n", - " {'id': 'fr_isoq_sym', 'value': 0.0},\n", " {'id': 'fr_multiplier_fatality', 'value': 2.0},\n", " {'id': 'fr_multiplier_icu', 'value': 2.0},\n", " {'id': 'healthcare_capacity_multiplier', 'value': 200.0},\n", @@ -1406,7 +1424,7 @@ " {'id': 'sum_quarantine_asym', 'value': 0.0},\n", " {'id': 'sum_recovered_asym', 'value': 0.0},\n", " {'id': 'sum_recovered_mild', 'value': 0.0},\n", - " {'id': 'sumnoninfectives', 'value': 5.0}],\n", + " {'id': 'sumnoninfectives', 'value': 0.0}],\n", " 'initials': [{'target': 'cumulative_cases_reported',\n", " 'expression': '0.0',\n", " 'expression_mathml': '0.0'},\n", @@ -1423,8 +1441,8 @@ " 'expression': '1339200000.0',\n", " 'expression_mathml': '1339200000.0'},\n", " {'target': 'exposed',\n", - " 'expression': '5.0',\n", - " 'expression_mathml': '5.0'},\n", + " 'expression': '0.0',\n", + " 'expression_mathml': '0.0'},\n", " {'target': 'infectious_asymptomatics',\n", " 'expression': '0.0',\n", " 'expression_mathml': '0.0'},\n", diff --git a/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json b/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json index 5d39d5425..4c31d7cd7 100644 --- a/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json +++ b/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json @@ -189,7 +189,9 @@ "id": "23", "name": "isolation_rate_sym", "upstream_stock": "infectious_symptomatics_mild", - "downstream_stock": "isolated_symtomatics_mild" + "downstream_stock": "isolated_symtomatics_mild", + "rate_expression": "fr_symptomatic_tested_isolated*delay3i(symptom_setting, delay_iso_sym, 0)", + "rate_expression_mathml": "fr_symptomatic_tested_isolatedsymptom_settingdelay_iso_sym0" }, { "id": "24", @@ -559,12 +561,6 @@ } ], "auxiliaries": [ - { - "id": "isolation_rate_sym", - "name": "isolation_rate_sym", - "expression": "isolation_rate_sym", - "expression_mathml": "isolation_rate_sym" - }, { "id": "delay_for_death", "name": "delay_for_death", @@ -649,6 +645,12 @@ "expression": "delay_asymp_infective_to_symp", "expression_mathml": "delay_asymp_infective_to_symp" }, + { + "id": "delay_iso_sym", + "name": "delay_iso_sym", + "expression": "delay_iso_sym", + "expression_mathml": "delay_iso_sym" + }, { "id": "delay_q_sym", "name": "delay_q_sym", @@ -671,32 +673,32 @@ "links": [ { "id": "link1", - "source": "isolation_rate_asym", + "source": "q_disease_progress_rate", "target": "flow1" }, { "id": "link2", - "source": "disease_progression", + "source": "isolation_rate_sym", "target": "flow1" }, { "id": "link3", - "source": "isolation_rate_sym", + "source": "disease_progression", "target": "flow1" }, { "id": "link4", - "source": "q_disease_progress_rate", + "source": "isolation_rate_asym", "target": "flow1" }, { "id": "link5", - "source": "delay_for_death", + "source": "icu_admissions", "target": "flow2" }, { "id": "link6", - "source": "icu_admissions", + "source": "delay_for_death", "target": "flow2" }, { @@ -706,12 +708,12 @@ }, { "id": "link8", - "source": "into_inf_sym_icu_overflow", + "source": "delay_for_death", "target": "flow3" }, { "id": "link9", - "source": "delay_for_death", + "source": "into_inf_sym_icu_overflow", "target": "flow3" }, { @@ -726,22 +728,22 @@ }, { "id": "link12", - "source": "contacts_total_per_susceptible", + "source": "susceptibles", "target": "flow4" }, { "id": "link13", - "source": "susceptibles", + "source": "contacts_total_per_susceptible", "target": "flow4" }, { "id": "link14", - "source": "delay_asymp_noninfective_to_infective", + "source": "exposure", "target": "flow5" }, { "id": "link15", - "source": "exposure", + "source": "delay_asymp_noninfective_to_infective", "target": "flow5" }, { @@ -751,32 +753,32 @@ }, { "id": "link17", - "source": "fr_becoming_serious", + "source": "symptom_setting", "target": "flow6" }, { "id": "link18", - "source": "delay_disease_diagnosis", + "source": "fr_iso", "target": "flow6" }, { "id": "link19", - "source": "symptom_setting", + "source": "fr_becoming_serious", "target": "flow6" }, { "id": "link20", - "source": "fr_iso", + "source": "delay_disease_diagnosis", "target": "flow6" }, { "id": "link21", - "source": "fr_becoming_serious", + "source": "into_iso_sym", "target": "flow7" }, { "id": "link22", - "source": "into_iso_sym", + "source": "fr_becoming_serious", "target": "flow7" }, { @@ -786,22 +788,22 @@ }, { "id": "link24", - "source": "fr_becoming_serious", + "source": "into_q_sym", "target": "flow8" }, { "id": "link25", - "source": "into_q_sym", + "source": "net_delay_dprogress_q_sym", "target": "flow8" }, { "id": "link26", - "source": "net_delay_dprogress_q_sym", + "source": "fr_becoming_serious", "target": "flow8" }, { "id": "link27", - "source": "time_step", + "source": "incoming_demand_on_hospital", "target": "flow9" }, { @@ -811,12 +813,12 @@ }, { "id": "link29", - "source": "incoming_demand_on_hospital", + "source": "time_step", "target": "flow9" }, { "id": "link30", - "source": "time_step", + "source": "incoming_demand_on_hospital", "target": "flow10" }, { @@ -826,7 +828,7 @@ }, { "id": "link32", - "source": "incoming_demand_on_hospital", + "source": "time_step", "target": "flow10" }, { @@ -856,32 +858,32 @@ }, { "id": "link38", - "source": "delay_recovery_sym_serious", + "source": "net_fr_requiring_icu", "target": "flow13" }, { "id": "link39", - "source": "net_fr_requiring_icu", + "source": "hospital_overflow", "target": "flow13" }, { "id": "link40", - "source": "hospital_overflow", + "source": "delay_recovery_sym_serious", "target": "flow13" }, { "id": "link41", - "source": "delay_worsening", + "source": "net_fr_requiring_icu", "target": "flow14" }, { "id": "link42", - "source": "net_fr_requiring_icu", + "source": "hospital_overflow", "target": "flow14" }, { "id": "link43", - "source": "hospital_overflow", + "source": "delay_worsening", "target": "flow14" }, { @@ -931,37 +933,37 @@ }, { "id": "link53", - "source": "fraction_iso_asym", + "source": "infection_rate_sum", "target": "flow19" }, { "id": "link54", - "source": "infection_rate_sum", + "source": "delay_iso_asym", "target": "flow19" }, { "id": "link55", - "source": "delay_iso_asym", + "source": "fraction_iso_asym", "target": "flow19" }, { "id": "link56", - "source": "infection_rate_sum", + "source": "delay_q_asym", "target": "flow20" }, { "id": "link57", - "source": "delay_q_asym", + "source": "fraction_q_asym", "target": "flow20" }, { "id": "link58", - "source": "fraction_q_asym", + "source": "infection_rate_sum", "target": "flow20" }, { "id": "link59", - "source": "infection_rate_sum", + "source": "delay_recovery_asym", "target": "flow21" }, { @@ -971,171 +973,186 @@ }, { "id": "link61", - "source": "delay_recovery_asym", + "source": "fr_iso", "target": "flow21" }, { "id": "link62", - "source": "fr_developing_symptoms", + "source": "infection_rate_sum", "target": "flow21" }, { "id": "link63", - "source": "fr_iso", + "source": "fr_developing_symptoms", "target": "flow21" }, { "id": "link64", - "source": "infection_rate_sum", + "source": "delay_asymp_infective_to_symp", "target": "flow22" }, { "id": "link65", - "source": "delay_asymp_infective_to_symp", + "source": "q_asym", "target": "flow22" }, { "id": "link66", - "source": "q_asym", + "source": "fr_iso", "target": "flow22" }, { "id": "link67", - "source": "fr_developing_symptoms", + "source": "infection_rate_sum", "target": "flow22" }, { "id": "link68", - "source": "fr_iso", + "source": "fr_developing_symptoms", "target": "flow22" }, { "id": "link69", - "source": "fraction_q_sym", - "target": "flow24" + "source": "symptom_setting", + "target": "flow23" }, { "id": "link70", - "source": "delay_q_sym", - "target": "flow24" + "source": "fr_symptomatic_tested_isolated", + "target": "flow23" }, { "id": "link71", - "source": "symptom_setting", - "target": "flow24" + "source": "delay_iso_sym", + "target": "flow23" }, { "id": "link72", - "source": "q_sym", - "target": "flow25" + "source": "symptom_setting", + "target": "flow24" }, { "id": "link73", - "source": "fr_becoming_serious", - "target": "flow25" + "source": "fraction_q_sym", + "target": "flow24" }, { "id": "link74", - "source": "symptom_setting", - "target": "flow25" + "source": "delay_q_sym", + "target": "flow24" }, { "id": "link75", - "source": "delay_recovery_sym_mild", + "source": "q_sym", "target": "flow25" }, { "id": "link76", - "source": "fr_iso", + "source": "symptom_setting", "target": "flow25" }, { "id": "link77", - "source": "isolation_rate_asym", - "target": "flow26" + "source": "delay_recovery_sym_mild", + "target": "flow25" }, { "id": "link78", + "source": "fr_iso", + "target": "flow25" + }, + { + "id": "link79", + "source": "fr_becoming_serious", + "target": "flow25" + }, + { + "id": "link80", "source": "fr_developing_symptoms", "target": "flow26" }, { - "id": "link79", + "id": "link81", + "source": "isolation_rate_asym", + "target": "flow26" + }, + { + "id": "link82", "source": "net_delay_incubation_iso_asym", "target": "flow26" }, { - "id": "link80", - "source": "isolation_rate_asym", + "id": "link83", + "source": "net_delay_recovery_iso_asym", "target": "flow27" }, { - "id": "link81", + "id": "link84", "source": "fr_developing_symptoms", "target": "flow27" }, { - "id": "link82", - "source": "net_delay_recovery_iso_asym", + "id": "link85", + "source": "isolation_rate_asym", "target": "flow27" }, { - "id": "link83", - "source": "fr_becoming_serious", + "id": "link86", + "source": "net_delay_recovery_iso_sym", "target": "flow28" }, { - "id": "link84", + "id": "link87", "source": "into_iso_sym", "target": "flow28" }, { - "id": "link85", - "source": "net_delay_recovery_iso_sym", + "id": "link88", + "source": "fr_becoming_serious", "target": "flow28" }, { - "id": "link86", - "source": "quarantining_rate_asym", + "id": "link89", + "source": "net_delay_incubation_q_asym", "target": "flow29" }, { - "id": "link87", + "id": "link90", "source": "fr_developing_symptoms", "target": "flow29" }, { - "id": "link88", - "source": "net_delay_incubation_q_asym", + "id": "link91", + "source": "quarantining_rate_asym", "target": "flow29" }, { - "id": "link89", - "source": "quarantining_rate_asym", + "id": "link92", + "source": "net_delay_recovery_q_asym", "target": "flow30" }, { - "id": "link90", - "source": "net_delay_recovery_q_asym", + "id": "link93", + "source": "fr_developing_symptoms", "target": "flow30" }, { - "id": "link91", - "source": "fr_developing_symptoms", + "id": "link94", + "source": "quarantining_rate_asym", "target": "flow30" }, { - "id": "link92", - "source": "fr_becoming_serious", + "id": "link95", + "source": "into_q_sym", "target": "flow31" }, { - "id": "link93", - "source": "into_q_sym", + "id": "link96", + "source": "fr_becoming_serious", "target": "flow31" }, { - "id": "link94", + "id": "link97", "source": "net_delay_recovery_q_sym", "target": "flow31" } @@ -1144,14 +1161,6 @@ "semantics": { "ode": { "parameters": [ - { - "id": "isolation_rate_sym", - "value": 0.0, - "units": { - "expression": "person/day", - "expression_mathml": "personday" - } - }, { "id": "delay_for_death", "value": 5.0, @@ -1265,6 +1274,14 @@ "expression_mathml": "day" } }, + { + "id": "delay_iso_sym", + "value": 2.5, + "units": { + "expression": "day", + "expression_mathml": "day" + } + }, { "id": "delay_q_sym", "value": 2.5, @@ -1422,18 +1439,6 @@ "id": "fr_q_asym_via_awareness", "value": 0.0 }, - { - "id": "fr_q_asym_via_tracing", - "value": 0.0 - }, - { - "id": "delay_iso_sym", - "value": 2.5 - }, - { - "id": "fr_isoq_sym", - "value": 0.0 - }, { "id": "fr_multiplier_fatality", "value": 2.0 @@ -1566,7 +1571,7 @@ }, { "id": "sumnoninfectives", - "value": 5.0 + "value": 0.0 } ], "initials": [ @@ -1597,8 +1602,8 @@ }, { "target": "exposed", - "expression": "5.0", - "expression_mathml": "5.0" + "expression": "0.0", + "expression_mathml": "0.0" }, { "target": "infectious_asymptomatics", From d380a09e12d57a19e1f3b890476d24f1e6ae1b4f Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Thu, 29 Feb 2024 15:17:23 +0100 Subject: [PATCH 7/9] Consolidate tests --- tests/test_pysd.py | 15 --------------- tests/test_system_dynamics.py | 8 ++++++++ 2 files changed, 8 insertions(+), 15 deletions(-) delete mode 100644 tests/test_pysd.py diff --git a/tests/test_pysd.py b/tests/test_pysd.py deleted file mode 100644 index 803b3c152..000000000 --- a/tests/test_pysd.py +++ /dev/null @@ -1,15 +0,0 @@ -import unittest - -import sympy -from mira.metamodel import safe_parse_expr -from mira.sources.system_dynamics.pysd import with_lookup_to_piecewise - - -class TestPySDUtils(unittest.TestCase): - """Test pysd utility functions.""" - - def test_with_lookup_to_piecewise(self): - data = "WITH LOOKUP(time,([(0,0)-(500,100)],(0,0),(1,2),(2,1),(3,0),(4,2),(5,1),(1000,0)))" - val = with_lookup_to_piecewise(data) - rv = safe_parse_expr(val) - self.assertIsInstance(rv, sympy.Expr) diff --git a/tests/test_system_dynamics.py b/tests/test_system_dynamics.py index 5c0deba86..e2389fceb 100644 --- a/tests/test_system_dynamics.py +++ b/tests/test_system_dynamics.py @@ -3,6 +3,7 @@ import sympy +from mira.sources.system_dynamics.pysd import with_lookup_to_piecewise from mira.sources.system_dynamics.vensim import * from mira.sources.system_dynamics.stella import * from mira.modeling.amr.stockflow import template_model_to_stockflow_json @@ -342,3 +343,10 @@ def test_stella_covid19_model(): assert isinstance(tm.templates[22], NaturalDegradation) assert tm.templates[22].subject.name == "tested_symptomatic_not_contagious" + + +def test_with_lookup_to_piecewise(self): + data = "WITH LOOKUP(time,([(0,0)-(500,100)],(0,0),(1,2),(2,1),(3,0),(4,2),(5,1),(1000,0)))" + val = with_lookup_to_piecewise(data) + rv = safe_parse_expr(val) + assert isinstance(rv, sympy.Expr) From 513c2d685371eabb572d4a3a01771565d51b1bab Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Thu, 29 Feb 2024 15:21:59 +0100 Subject: [PATCH 8/9] Add flag for turning on initial value parsing from INTEG expressions --- mira/sources/system_dynamics/vensim.py | 44 ++++-- .../scenario5/Scenario 5 Notebook.ipynb | 136 +++++++++--------- .../scenario5/scenario_5_stockflow.json | 120 ++++++++-------- 3 files changed, 161 insertions(+), 139 deletions(-) diff --git a/mira/sources/system_dynamics/vensim.py b/mira/sources/system_dynamics/vensim.py index 49600b267..4150e1a22 100644 --- a/mira/sources/system_dynamics/vensim.py +++ b/mira/sources/system_dynamics/vensim.py @@ -17,7 +17,9 @@ from mira.metamodel import TemplateModel, Initial from mira.sources.system_dynamics.pysd import ( - template_model_from_pysd_model, ifthenelse_to_piecewise, with_lookup_to_piecewise + template_model_from_pysd_model, + ifthenelse_to_piecewise, + with_lookup_to_piecewise, ) __all__ = ["template_model_from_mdl_file", "template_model_from_mdl_url"] @@ -34,7 +36,9 @@ CONTROL_VARIABLES = {"SAVEPER", "FINAL TIME", "INITIAL TIME", "TIME STEP"} -def template_model_from_mdl_file(fname, *, grounding_map=None, initials=None) -> TemplateModel: +def template_model_from_mdl_file( + fname, *, grounding_map=None, initials=None, initials_from_integ: bool = False +) -> TemplateModel: """Return a template model from a local Vensim file Parameters @@ -46,6 +50,9 @@ def template_model_from_mdl_file(fname, *, grounding_map=None, initials=None) -> initials: dict[str, float] Explicit initial values to use for compartments in the model. Will overwrite model-internal definitions. + initials_from_integ : bool + If true, gets initial values from INTEG expressions. If ``initials`` + are given, they override anything from INTEG expressions Returns ------- @@ -54,7 +61,9 @@ def template_model_from_mdl_file(fname, *, grounding_map=None, initials=None) -> """ pysd_model = pysd.read_vensim(fname) vensim_file = VensimFile(fname) - expression_map, initials_map = extract_vensim_variable_expressions(vensim_file.model_text) + expression_map, initials_map = extract_vensim_variable_expressions( + vensim_file.model_text, initials_from_integ=initials_from_integ, + ) if initials: initials_map.update(initials) return template_model_from_pysd_model( @@ -65,7 +74,9 @@ def template_model_from_mdl_file(fname, *, grounding_map=None, initials=None) -> ) -def template_model_from_mdl_url(url, *, grounding_map=None, initials=None) -> TemplateModel: +def template_model_from_mdl_url( + url, *, grounding_map=None, initials=None, initials_from_integ: bool = False +) -> TemplateModel: """Return a template model from a Vensim file provided by an url Parameters @@ -77,6 +88,9 @@ def template_model_from_mdl_url(url, *, grounding_map=None, initials=None) -> Te initials: dict[str, float] Explicit initial values to use for compartments in the model. Will overwrite model-internal definitions. + initials_from_integ : bool + If true, gets initial values from INTEG expressions. If ``initials`` + are given, they override anything from INTEG expressions Returns ------- @@ -91,17 +105,26 @@ def template_model_from_mdl_url(url, *, grounding_map=None, initials=None) -> Te with temp_file as file: file.write(data) - return template_model_from_mdl_file(temp_file.name, grounding_map=grounding_map, initials=initials) + return template_model_from_mdl_file( + temp_file.name, + grounding_map=grounding_map, + initials=initials, + initials_from_integ=initials_from_integ, + ) # look past control section -def extract_vensim_variable_expressions(model_text): +def extract_vensim_variable_expressions( + model_text, *, initials_from_integ: bool = False +): """Method that extracts expressions for each variable in a Vensim file Parameters ---------- model_text : str The plain-text information about the Vensim file + initials_from_integ : bool + If true, gets initial values from INTEG expressions Returns ------- @@ -174,10 +197,11 @@ def extract_vensim_variable_expressions(model_text): expression_map[old_var_name] = text_expression - # CTH: I couldn't find where this normalization happens - # between the vensim and pysd code, so I added it again here. - # also, the initial value might be an expression that needs normalizing - initial_values[_norm(old_var_name)] = initial and _norm(initial) + if initials_from_integ: + # CTH: I couldn't find where this normalization happens + # between the vensim and pysd code, so I added it again here. + # also, the initial value might be an expression that needs normalizing + initial_values[_norm(old_var_name)] = initial and _norm(initial) # remove any control variables listed past the control section that were added to the # expression map diff --git a/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb b/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb index 521b506a4..4878ef974 100644 --- a/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb +++ b/notebooks/hackathon_2024.02/scenario5/Scenario 5 Notebook.ipynb @@ -123,9 +123,9 @@ "if not os.path.exists(hackathon_file_path):\n", " urlretrieve(hackathon_file_url, hackathon_file_path)\n", "if os.path.exists(hackathon_file_path):\n", - " tm = template_model_from_mdl_file(hackathon_file_path, grounding_map=grounding_map, initials=initials)\n", + " tm = template_model_from_mdl_file(hackathon_file_path, grounding_map=grounding_map, initials=initials, initials_from_integ=True)\n", "else:\n", - " tm = template_model_from_mdl_url(hackathon_file_url, grounding_map=grounding_map, initials=initials)" + " tm = template_model_from_mdl_url(hackathon_file_url, grounding_map=grounding_map, initials=initials, initials_from_integ=True)" ] }, { @@ -1170,134 +1170,132 @@ " 'links': [{'id': 'link1',\n", " 'source': 'q_disease_progress_rate',\n", " 'target': 'flow1'},\n", - " {'id': 'link2', 'source': 'isolation_rate_sym', 'target': 'flow1'},\n", + " {'id': 'link2', 'source': 'isolation_rate_asym', 'target': 'flow1'},\n", " {'id': 'link3', 'source': 'disease_progression', 'target': 'flow1'},\n", - " {'id': 'link4', 'source': 'isolation_rate_asym', 'target': 'flow1'},\n", + " {'id': 'link4', 'source': 'isolation_rate_sym', 'target': 'flow1'},\n", " {'id': 'link5', 'source': 'icu_admissions', 'target': 'flow2'},\n", " {'id': 'link6', 'source': 'delay_for_death', 'target': 'flow2'},\n", " {'id': 'link7', 'source': 'fr_fatality', 'target': 'flow2'},\n", - " {'id': 'link8', 'source': 'delay_for_death', 'target': 'flow3'},\n", - " {'id': 'link9', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow3'},\n", + " {'id': 'link8', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow3'},\n", + " {'id': 'link9', 'source': 'delay_for_death', 'target': 'flow3'},\n", " {'id': 'link10', 'source': 'net_fr_fatality', 'target': 'flow3'},\n", - " {'id': 'link11', 'source': 'infectivity', 'target': 'flow4'},\n", - " {'id': 'link12', 'source': 'susceptibles', 'target': 'flow4'},\n", + " {'id': 'link11', 'source': 'susceptibles', 'target': 'flow4'},\n", + " {'id': 'link12', 'source': 'infectivity', 'target': 'flow4'},\n", " {'id': 'link13',\n", " 'source': 'contacts_total_per_susceptible',\n", " 'target': 'flow4'},\n", - " {'id': 'link14', 'source': 'exposure', 'target': 'flow5'},\n", - " {'id': 'link15',\n", + " {'id': 'link14',\n", " 'source': 'delay_asymp_noninfective_to_infective',\n", " 'target': 'flow5'},\n", - " {'id': 'link16', 'source': 'q_sym', 'target': 'flow6'},\n", - " {'id': 'link17', 'source': 'symptom_setting', 'target': 'flow6'},\n", - " {'id': 'link18', 'source': 'fr_iso', 'target': 'flow6'},\n", - " {'id': 'link19', 'source': 'fr_becoming_serious', 'target': 'flow6'},\n", + " {'id': 'link15', 'source': 'exposure', 'target': 'flow5'},\n", + " {'id': 'link16', 'source': 'symptom_setting', 'target': 'flow6'},\n", + " {'id': 'link17', 'source': 'fr_becoming_serious', 'target': 'flow6'},\n", + " {'id': 'link18', 'source': 'q_sym', 'target': 'flow6'},\n", + " {'id': 'link19', 'source': 'fr_iso', 'target': 'flow6'},\n", " {'id': 'link20', 'source': 'delay_disease_diagnosis', 'target': 'flow6'},\n", - " {'id': 'link21', 'source': 'into_iso_sym', 'target': 'flow7'},\n", - " {'id': 'link22', 'source': 'fr_becoming_serious', 'target': 'flow7'},\n", - " {'id': 'link23',\n", + " {'id': 'link21',\n", " 'source': 'net_delay_dprogress_iso_sym',\n", " 'target': 'flow7'},\n", + " {'id': 'link22', 'source': 'into_iso_sym', 'target': 'flow7'},\n", + " {'id': 'link23', 'source': 'fr_becoming_serious', 'target': 'flow7'},\n", " {'id': 'link24', 'source': 'into_q_sym', 'target': 'flow8'},\n", " {'id': 'link25', 'source': 'net_delay_dprogress_q_sym', 'target': 'flow8'},\n", " {'id': 'link26', 'source': 'fr_becoming_serious', 'target': 'flow8'},\n", - " {'id': 'link27',\n", + " {'id': 'link27', 'source': 'time_step', 'target': 'flow9'},\n", + " {'id': 'link28', 'source': 'managable_hospital_inflow', 'target': 'flow9'},\n", + " {'id': 'link29',\n", " 'source': 'incoming_demand_on_hospital',\n", " 'target': 'flow9'},\n", - " {'id': 'link28', 'source': 'managable_hospital_inflow', 'target': 'flow9'},\n", - " {'id': 'link29', 'source': 'time_step', 'target': 'flow9'},\n", - " {'id': 'link30',\n", + " {'id': 'link30', 'source': 'time_step', 'target': 'flow10'},\n", + " {'id': 'link31', 'source': 'managable_hospital_inflow', 'target': 'flow10'},\n", + " {'id': 'link32',\n", " 'source': 'incoming_demand_on_hospital',\n", " 'target': 'flow10'},\n", - " {'id': 'link31', 'source': 'managable_hospital_inflow', 'target': 'flow10'},\n", - " {'id': 'link32', 'source': 'time_step', 'target': 'flow10'},\n", " {'id': 'link33', 'source': 'managable_icu_inflow', 'target': 'flow11'},\n", " {'id': 'link34', 'source': 'typical_worsening_rate', 'target': 'flow11'},\n", " {'id': 'link35', 'source': 'icu_admissions', 'target': 'flow12'},\n", - " {'id': 'link36', 'source': 'fr_fatality', 'target': 'flow12'},\n", - " {'id': 'link37',\n", + " {'id': 'link36',\n", " 'source': 'delay_recovery_sym_extreme',\n", " 'target': 'flow12'},\n", - " {'id': 'link38', 'source': 'net_fr_requiring_icu', 'target': 'flow13'},\n", - " {'id': 'link39', 'source': 'hospital_overflow', 'target': 'flow13'},\n", - " {'id': 'link40',\n", + " {'id': 'link37', 'source': 'fr_fatality', 'target': 'flow12'},\n", + " {'id': 'link38',\n", " 'source': 'delay_recovery_sym_serious',\n", " 'target': 'flow13'},\n", + " {'id': 'link39', 'source': 'net_fr_requiring_icu', 'target': 'flow13'},\n", + " {'id': 'link40', 'source': 'hospital_overflow', 'target': 'flow13'},\n", " {'id': 'link41', 'source': 'net_fr_requiring_icu', 'target': 'flow14'},\n", " {'id': 'link42', 'source': 'hospital_overflow', 'target': 'flow14'},\n", " {'id': 'link43', 'source': 'delay_worsening', 'target': 'flow14'},\n", " {'id': 'link44', 'source': 'managable_icu_inflow', 'target': 'flow15'},\n", " {'id': 'link45', 'source': 'typical_worsening_rate', 'target': 'flow15'},\n", - " {'id': 'link46', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow16'},\n", - " {'id': 'link47', 'source': 'net_fr_fatality', 'target': 'flow16'},\n", - " {'id': 'link48',\n", + " {'id': 'link46',\n", " 'source': 'delay_recovery_sym_extreme',\n", " 'target': 'flow16'},\n", + " {'id': 'link47', 'source': 'into_inf_sym_icu_overflow', 'target': 'flow16'},\n", + " {'id': 'link48', 'source': 'net_fr_fatality', 'target': 'flow16'},\n", " {'id': 'link49', 'source': 'hospital_admissions', 'target': 'flow17'},\n", - " {'id': 'link50', 'source': 'fr_requiring_icu', 'target': 'flow17'},\n", - " {'id': 'link51',\n", + " {'id': 'link50',\n", " 'source': 'delay_recovery_sym_serious',\n", " 'target': 'flow17'},\n", + " {'id': 'link51', 'source': 'fr_requiring_icu', 'target': 'flow17'},\n", " {'id': 'link52', 'source': 'time', 'target': 'flow18'},\n", " {'id': 'link53', 'source': 'infection_rate_sum', 'target': 'flow19'},\n", " {'id': 'link54', 'source': 'delay_iso_asym', 'target': 'flow19'},\n", " {'id': 'link55', 'source': 'fraction_iso_asym', 'target': 'flow19'},\n", - " {'id': 'link56', 'source': 'delay_q_asym', 'target': 'flow20'},\n", - " {'id': 'link57', 'source': 'fraction_q_asym', 'target': 'flow20'},\n", - " {'id': 'link58', 'source': 'infection_rate_sum', 'target': 'flow20'},\n", - " {'id': 'link59', 'source': 'delay_recovery_asym', 'target': 'flow21'},\n", - " {'id': 'link60', 'source': 'q_asym', 'target': 'flow21'},\n", + " {'id': 'link56', 'source': 'infection_rate_sum', 'target': 'flow20'},\n", + " {'id': 'link57', 'source': 'delay_q_asym', 'target': 'flow20'},\n", + " {'id': 'link58', 'source': 'fraction_q_asym', 'target': 'flow20'},\n", + " {'id': 'link59', 'source': 'infection_rate_sum', 'target': 'flow21'},\n", + " {'id': 'link60', 'source': 'fr_developing_symptoms', 'target': 'flow21'},\n", " {'id': 'link61', 'source': 'fr_iso', 'target': 'flow21'},\n", - " {'id': 'link62', 'source': 'infection_rate_sum', 'target': 'flow21'},\n", - " {'id': 'link63', 'source': 'fr_developing_symptoms', 'target': 'flow21'},\n", - " {'id': 'link64',\n", + " {'id': 'link62', 'source': 'q_asym', 'target': 'flow21'},\n", + " {'id': 'link63', 'source': 'delay_recovery_asym', 'target': 'flow21'},\n", + " {'id': 'link64', 'source': 'infection_rate_sum', 'target': 'flow22'},\n", + " {'id': 'link65', 'source': 'fr_developing_symptoms', 'target': 'flow22'},\n", + " {'id': 'link66',\n", " 'source': 'delay_asymp_infective_to_symp',\n", " 'target': 'flow22'},\n", - " {'id': 'link65', 'source': 'q_asym', 'target': 'flow22'},\n", - " {'id': 'link66', 'source': 'fr_iso', 'target': 'flow22'},\n", - " {'id': 'link67', 'source': 'infection_rate_sum', 'target': 'flow22'},\n", - " {'id': 'link68', 'source': 'fr_developing_symptoms', 'target': 'flow22'},\n", - " {'id': 'link69', 'source': 'symptom_setting', 'target': 'flow23'},\n", - " {'id': 'link70',\n", + " {'id': 'link67', 'source': 'fr_iso', 'target': 'flow22'},\n", + " {'id': 'link68', 'source': 'q_asym', 'target': 'flow22'},\n", + " {'id': 'link69',\n", " 'source': 'fr_symptomatic_tested_isolated',\n", " 'target': 'flow23'},\n", - " {'id': 'link71', 'source': 'delay_iso_sym', 'target': 'flow23'},\n", + " {'id': 'link70', 'source': 'delay_iso_sym', 'target': 'flow23'},\n", + " {'id': 'link71', 'source': 'symptom_setting', 'target': 'flow23'},\n", " {'id': 'link72', 'source': 'symptom_setting', 'target': 'flow24'},\n", - " {'id': 'link73', 'source': 'fraction_q_sym', 'target': 'flow24'},\n", - " {'id': 'link74', 'source': 'delay_q_sym', 'target': 'flow24'},\n", - " {'id': 'link75', 'source': 'q_sym', 'target': 'flow25'},\n", - " {'id': 'link76', 'source': 'symptom_setting', 'target': 'flow25'},\n", - " {'id': 'link77', 'source': 'delay_recovery_sym_mild', 'target': 'flow25'},\n", + " {'id': 'link73', 'source': 'delay_q_sym', 'target': 'flow24'},\n", + " {'id': 'link74', 'source': 'fraction_q_sym', 'target': 'flow24'},\n", + " {'id': 'link75', 'source': 'symptom_setting', 'target': 'flow25'},\n", + " {'id': 'link76', 'source': 'fr_becoming_serious', 'target': 'flow25'},\n", + " {'id': 'link77', 'source': 'q_sym', 'target': 'flow25'},\n", " {'id': 'link78', 'source': 'fr_iso', 'target': 'flow25'},\n", - " {'id': 'link79', 'source': 'fr_becoming_serious', 'target': 'flow25'},\n", - " {'id': 'link80', 'source': 'fr_developing_symptoms', 'target': 'flow26'},\n", - " {'id': 'link81', 'source': 'isolation_rate_asym', 'target': 'flow26'},\n", - " {'id': 'link82',\n", + " {'id': 'link79', 'source': 'delay_recovery_sym_mild', 'target': 'flow25'},\n", + " {'id': 'link80',\n", " 'source': 'net_delay_incubation_iso_asym',\n", " 'target': 'flow26'},\n", - " {'id': 'link83',\n", + " {'id': 'link81', 'source': 'isolation_rate_asym', 'target': 'flow26'},\n", + " {'id': 'link82', 'source': 'fr_developing_symptoms', 'target': 'flow26'},\n", + " {'id': 'link83', 'source': 'isolation_rate_asym', 'target': 'flow27'},\n", + " {'id': 'link84', 'source': 'fr_developing_symptoms', 'target': 'flow27'},\n", + " {'id': 'link85',\n", " 'source': 'net_delay_recovery_iso_asym',\n", " 'target': 'flow27'},\n", - " {'id': 'link84', 'source': 'fr_developing_symptoms', 'target': 'flow27'},\n", - " {'id': 'link85', 'source': 'isolation_rate_asym', 'target': 'flow27'},\n", " {'id': 'link86',\n", " 'source': 'net_delay_recovery_iso_sym',\n", " 'target': 'flow28'},\n", " {'id': 'link87', 'source': 'into_iso_sym', 'target': 'flow28'},\n", " {'id': 'link88', 'source': 'fr_becoming_serious', 'target': 'flow28'},\n", - " {'id': 'link89',\n", + " {'id': 'link89', 'source': 'quarantining_rate_asym', 'target': 'flow29'},\n", + " {'id': 'link90', 'source': 'fr_developing_symptoms', 'target': 'flow29'},\n", + " {'id': 'link91',\n", " 'source': 'net_delay_incubation_q_asym',\n", " 'target': 'flow29'},\n", - " {'id': 'link90', 'source': 'fr_developing_symptoms', 'target': 'flow29'},\n", - " {'id': 'link91', 'source': 'quarantining_rate_asym', 'target': 'flow29'},\n", - " {'id': 'link92', 'source': 'net_delay_recovery_q_asym', 'target': 'flow30'},\n", + " {'id': 'link92', 'source': 'quarantining_rate_asym', 'target': 'flow30'},\n", " {'id': 'link93', 'source': 'fr_developing_symptoms', 'target': 'flow30'},\n", - " {'id': 'link94', 'source': 'quarantining_rate_asym', 'target': 'flow30'},\n", + " {'id': 'link94', 'source': 'net_delay_recovery_q_asym', 'target': 'flow30'},\n", " {'id': 'link95', 'source': 'into_q_sym', 'target': 'flow31'},\n", - " {'id': 'link96', 'source': 'fr_becoming_serious', 'target': 'flow31'},\n", - " {'id': 'link97',\n", - " 'source': 'net_delay_recovery_q_sym',\n", - " 'target': 'flow31'}]},\n", + " {'id': 'link96', 'source': 'net_delay_recovery_q_sym', 'target': 'flow31'},\n", + " {'id': 'link97', 'source': 'fr_becoming_serious', 'target': 'flow31'}]},\n", " 'semantics': {'ode': {'parameters': [{'id': 'delay_for_death',\n", " 'value': 5.0,\n", " 'units': {'expression': 'day', 'expression_mathml': 'day'}},\n", diff --git a/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json b/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json index 4c31d7cd7..1b6e86d6b 100644 --- a/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json +++ b/notebooks/hackathon_2024.02/scenario5/scenario_5_stockflow.json @@ -678,7 +678,7 @@ }, { "id": "link2", - "source": "isolation_rate_sym", + "source": "isolation_rate_asym", "target": "flow1" }, { @@ -688,7 +688,7 @@ }, { "id": "link4", - "source": "isolation_rate_asym", + "source": "isolation_rate_sym", "target": "flow1" }, { @@ -708,12 +708,12 @@ }, { "id": "link8", - "source": "delay_for_death", + "source": "into_inf_sym_icu_overflow", "target": "flow3" }, { "id": "link9", - "source": "into_inf_sym_icu_overflow", + "source": "delay_for_death", "target": "flow3" }, { @@ -723,12 +723,12 @@ }, { "id": "link11", - "source": "infectivity", + "source": "susceptibles", "target": "flow4" }, { "id": "link12", - "source": "susceptibles", + "source": "infectivity", "target": "flow4" }, { @@ -738,32 +738,32 @@ }, { "id": "link14", - "source": "exposure", + "source": "delay_asymp_noninfective_to_infective", "target": "flow5" }, { "id": "link15", - "source": "delay_asymp_noninfective_to_infective", + "source": "exposure", "target": "flow5" }, { "id": "link16", - "source": "q_sym", + "source": "symptom_setting", "target": "flow6" }, { "id": "link17", - "source": "symptom_setting", + "source": "fr_becoming_serious", "target": "flow6" }, { "id": "link18", - "source": "fr_iso", + "source": "q_sym", "target": "flow6" }, { "id": "link19", - "source": "fr_becoming_serious", + "source": "fr_iso", "target": "flow6" }, { @@ -773,17 +773,17 @@ }, { "id": "link21", - "source": "into_iso_sym", + "source": "net_delay_dprogress_iso_sym", "target": "flow7" }, { "id": "link22", - "source": "fr_becoming_serious", + "source": "into_iso_sym", "target": "flow7" }, { "id": "link23", - "source": "net_delay_dprogress_iso_sym", + "source": "fr_becoming_serious", "target": "flow7" }, { @@ -803,7 +803,7 @@ }, { "id": "link27", - "source": "incoming_demand_on_hospital", + "source": "time_step", "target": "flow9" }, { @@ -813,12 +813,12 @@ }, { "id": "link29", - "source": "time_step", + "source": "incoming_demand_on_hospital", "target": "flow9" }, { "id": "link30", - "source": "incoming_demand_on_hospital", + "source": "time_step", "target": "flow10" }, { @@ -828,7 +828,7 @@ }, { "id": "link32", - "source": "time_step", + "source": "incoming_demand_on_hospital", "target": "flow10" }, { @@ -848,27 +848,27 @@ }, { "id": "link36", - "source": "fr_fatality", + "source": "delay_recovery_sym_extreme", "target": "flow12" }, { "id": "link37", - "source": "delay_recovery_sym_extreme", + "source": "fr_fatality", "target": "flow12" }, { "id": "link38", - "source": "net_fr_requiring_icu", + "source": "delay_recovery_sym_serious", "target": "flow13" }, { "id": "link39", - "source": "hospital_overflow", + "source": "net_fr_requiring_icu", "target": "flow13" }, { "id": "link40", - "source": "delay_recovery_sym_serious", + "source": "hospital_overflow", "target": "flow13" }, { @@ -898,17 +898,17 @@ }, { "id": "link46", - "source": "into_inf_sym_icu_overflow", + "source": "delay_recovery_sym_extreme", "target": "flow16" }, { "id": "link47", - "source": "net_fr_fatality", + "source": "into_inf_sym_icu_overflow", "target": "flow16" }, { "id": "link48", - "source": "delay_recovery_sym_extreme", + "source": "net_fr_fatality", "target": "flow16" }, { @@ -918,12 +918,12 @@ }, { "id": "link50", - "source": "fr_requiring_icu", + "source": "delay_recovery_sym_serious", "target": "flow17" }, { "id": "link51", - "source": "delay_recovery_sym_serious", + "source": "fr_requiring_icu", "target": "flow17" }, { @@ -948,27 +948,27 @@ }, { "id": "link56", - "source": "delay_q_asym", + "source": "infection_rate_sum", "target": "flow20" }, { "id": "link57", - "source": "fraction_q_asym", + "source": "delay_q_asym", "target": "flow20" }, { "id": "link58", - "source": "infection_rate_sum", + "source": "fraction_q_asym", "target": "flow20" }, { "id": "link59", - "source": "delay_recovery_asym", + "source": "infection_rate_sum", "target": "flow21" }, { "id": "link60", - "source": "q_asym", + "source": "fr_developing_symptoms", "target": "flow21" }, { @@ -978,52 +978,52 @@ }, { "id": "link62", - "source": "infection_rate_sum", + "source": "q_asym", "target": "flow21" }, { "id": "link63", - "source": "fr_developing_symptoms", + "source": "delay_recovery_asym", "target": "flow21" }, { "id": "link64", - "source": "delay_asymp_infective_to_symp", + "source": "infection_rate_sum", "target": "flow22" }, { "id": "link65", - "source": "q_asym", + "source": "fr_developing_symptoms", "target": "flow22" }, { "id": "link66", - "source": "fr_iso", + "source": "delay_asymp_infective_to_symp", "target": "flow22" }, { "id": "link67", - "source": "infection_rate_sum", + "source": "fr_iso", "target": "flow22" }, { "id": "link68", - "source": "fr_developing_symptoms", + "source": "q_asym", "target": "flow22" }, { "id": "link69", - "source": "symptom_setting", + "source": "fr_symptomatic_tested_isolated", "target": "flow23" }, { "id": "link70", - "source": "fr_symptomatic_tested_isolated", + "source": "delay_iso_sym", "target": "flow23" }, { "id": "link71", - "source": "delay_iso_sym", + "source": "symptom_setting", "target": "flow23" }, { @@ -1033,27 +1033,27 @@ }, { "id": "link73", - "source": "fraction_q_sym", + "source": "delay_q_sym", "target": "flow24" }, { "id": "link74", - "source": "delay_q_sym", + "source": "fraction_q_sym", "target": "flow24" }, { "id": "link75", - "source": "q_sym", + "source": "symptom_setting", "target": "flow25" }, { "id": "link76", - "source": "symptom_setting", + "source": "fr_becoming_serious", "target": "flow25" }, { "id": "link77", - "source": "delay_recovery_sym_mild", + "source": "q_sym", "target": "flow25" }, { @@ -1063,12 +1063,12 @@ }, { "id": "link79", - "source": "fr_becoming_serious", + "source": "delay_recovery_sym_mild", "target": "flow25" }, { "id": "link80", - "source": "fr_developing_symptoms", + "source": "net_delay_incubation_iso_asym", "target": "flow26" }, { @@ -1078,12 +1078,12 @@ }, { "id": "link82", - "source": "net_delay_incubation_iso_asym", + "source": "fr_developing_symptoms", "target": "flow26" }, { "id": "link83", - "source": "net_delay_recovery_iso_asym", + "source": "isolation_rate_asym", "target": "flow27" }, { @@ -1093,7 +1093,7 @@ }, { "id": "link85", - "source": "isolation_rate_asym", + "source": "net_delay_recovery_iso_asym", "target": "flow27" }, { @@ -1113,7 +1113,7 @@ }, { "id": "link89", - "source": "net_delay_incubation_q_asym", + "source": "quarantining_rate_asym", "target": "flow29" }, { @@ -1123,12 +1123,12 @@ }, { "id": "link91", - "source": "quarantining_rate_asym", + "source": "net_delay_incubation_q_asym", "target": "flow29" }, { "id": "link92", - "source": "net_delay_recovery_q_asym", + "source": "quarantining_rate_asym", "target": "flow30" }, { @@ -1138,7 +1138,7 @@ }, { "id": "link94", - "source": "quarantining_rate_asym", + "source": "net_delay_recovery_q_asym", "target": "flow30" }, { @@ -1148,12 +1148,12 @@ }, { "id": "link96", - "source": "fr_becoming_serious", + "source": "net_delay_recovery_q_sym", "target": "flow31" }, { "id": "link97", - "source": "net_delay_recovery_q_sym", + "source": "fr_becoming_serious", "target": "flow31" } ] From 43804b08c8bf68b65f1a064b9b7f266207e7be43 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Thu, 29 Feb 2024 15:26:30 +0100 Subject: [PATCH 9/9] Update test_system_dynamics.py --- tests/test_system_dynamics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_system_dynamics.py b/tests/test_system_dynamics.py index e2389fceb..0b8d9cbf7 100644 --- a/tests/test_system_dynamics.py +++ b/tests/test_system_dynamics.py @@ -345,7 +345,7 @@ def test_stella_covid19_model(): assert tm.templates[22].subject.name == "tested_symptomatic_not_contagious" -def test_with_lookup_to_piecewise(self): +def test_with_lookup_to_piecewise(): data = "WITH LOOKUP(time,([(0,0)-(500,100)],(0,0),(1,2),(2,1),(3,0),(4,2),(5,1),(1000,0)))" val = with_lookup_to_piecewise(data) rv = safe_parse_expr(val)