diff --git a/pydynamo/__init__.py b/pydynamo/__init__.py index c14fc70afd5e6d0a65dcf67cc0ec7d0882c9fe3a..320c1168f20eb504f36db978f345b9d2c1473fd7 100644 --- a/pydynamo/__init__.py +++ b/pydynamo/__init__.py @@ -7,14 +7,10 @@ from pydynamo.core.parse_system import new_cst_politic, new_table_politic, new_v from pydynamo.core.plot_system import * # import pydynamo.core.dynamo_converter from pydynamo.core import psdsystem -from .world3 import w3_code, w3_defs, plot_world_03, plot_world_with_scales, var_color, scenarios -from .world2 import w2_code, w2_defs, scales_w2 +from .world3_utils import World3 + -def get_w3(): - w3 = system_from_lines(w3_code) - w3.add_comments(w3_defs) - return w3 def get_w2(): w2 = system_from_lines(w2_code) @@ -23,12 +19,3 @@ def get_w2(): def plot_w2(w2, title=''): plot_system(w2, scales_w2, scales=scales_w2, title=title) - -def get_scenario(number): - w3s = get_w3() - changes = scenarios[number - 1]['changes'] - for c, v in changes.items(): - setattr(w3s, c, v) - return w3s, scenarios[number - 1]['title'] - - diff --git a/pydynamo/core/parse_system.py b/pydynamo/core/parse_system.py index 74eefd9c2e1bcc31b498dfdd450f8d73a3ea5df5..f5d72b8c44d3756f8e175623320a856844e2f706 100644 --- a/pydynamo/core/parse_system.py +++ b/pydynamo/core/parse_system.py @@ -112,7 +112,8 @@ def new_cst_politic(s, cst, year, val2): del s.eqs['cst'][cst] # New equations - eq_clip = f"{cst}.k = clip({cst}2, {cst}1, time.k, {cst}_year) # {s.get_comment({cst})} " + + eq_clip = f"{cst}.k = clip({cst}2, {cst}1, time.k, {cst}_year) # {s.get_comment(cst)} " eq_year = f"{cst}_year = {year} # Date of {cst} change" eq_cst1 = f"{cst}1 = {initval} # {s.get_comment({cst})} before {cst}_year" eq_cst2 = f"{cst}2 = {val2} # {s.get_comment({cst})} after {cst}_year" @@ -130,11 +131,11 @@ def new_table_politic(s, var, year, val2): table_init_val = s.eqs['cst'][table_name]['line'] var_line = s.eqs['update'][var]['raw_line'] - eq_table_1 = f"{table_name}1 = {table_init_val} # {s.get_comment({table_name})} before {year}" - eq_table_2 = f"{table_name}2 = {list(val2)} # {s.get_comment({table_name})} after {year}" + eq_table_1 = f"{table_name}1 = {table_init_val} # {s.get_comment(table_name)} before {year}" + eq_table_2 = f"{table_name}2 = {list(val2)} # {s.get_comment(table_name)} after {year}" eq_var_1 = var_line.replace(f'{var}.k', f'{var}1.k').replace(table_name, table_name + '1') eq_var_2 = var_line.replace(f'{var}.k', f'{var}2.k').replace(table_name, table_name + '2') - eq_var = f"{var}.k = clip({var}2.k, {var}1.k, time.k, {year}) {s.get_comment({var})}" + eq_var = f"{var}.k = clip({var}2.k, {var}1.k, time.k, {year}) # {s.get_comment(var)}" system_from_lines([eq_table_1, eq_table_2, eq_var_1, eq_var_2, eq_var], s=s) diff --git a/pydynamo/world3/plot_utils.py b/pydynamo/world3/plot_utils.py index e51ca5c1fa04080be1795b4803a62928c69e3cc8..df1544a9f0564b91ed79d3b44747b86149e84c72 100644 --- a/pydynamo/world3/plot_utils.py +++ b/pydynamo/world3/plot_utils.py @@ -184,12 +184,13 @@ def plot_world_03(s, title=None, with_legend=False): time = s.time except KeyError: raise Exception("System must be ran before plot !") + if with_legend: figsize = (8,12) else: figsize = (7, 12) - + dist_spines=0.09 grid=1 img_background=None diff --git a/pydynamo/world3_utils.py b/pydynamo/world3_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..2a34daebe7812a4d1fd567a6b15643e45f3278ba --- /dev/null +++ b/pydynamo/world3_utils.py @@ -0,0 +1,175 @@ +from pydynamo.core.system import System +from pydynamo.core.plot_system import * +from pydynamo.core.parse_system import new_cst_politic, new_table_politic, new_var_politic,new_system, system_from_lines, system_from_fun, system_from_file +from .world3 import w3_code, w3_defs, plot_world_03, plot_world_with_scales, var_color, scenarios + + +def get_w3(): + w3 = system_from_lines(w3_code) + w3.add_comments(w3_defs) + return w3 + +def get_scenario(number): + w3s = get_w3() + changes = scenarios[number - 1]['changes'] + for c, v in changes.items(): + setattr(w3s, c, v) + return w3s, scenarios[number - 1]['title'] + + +class World3(System): + """ + A World3 object is a System object with more convenient functions and defaults, adapted for the manipulation of the World3 model 2003's version equations. + """ + def __init__(self, scenario_number=2, additional_equations=None, sys=None): + """Initialise a World3 object. By default, the scenario number is the second one, because it's the most "realistic" when we compare to the current situation.""" + s, title = get_scenario(scenario_number) + if sys: + s = sys + self.eqs = s.eqs + self.nodes = s.nodes + self.comments = s.comments + self.prepare() + for cst in self.nodes['cst']: + setattr(self, cst, getattr(s, cst)) + if additional_equations: + new_system(additional_equations, s=self) + + def new_politic(self, name, date, new_val): + """Implements a new politic for some constant, table or variable from a certain date. + + Parameters + ---------- + name: str + Name of a constant, table or variable we want to change. + + date: float + date from which the new value will be activated. + + new_val: float, array or string + If name refers to a constant, a float with the new value. If name srefers to a table, an array of the sime size as the older one. If name refers to a variable, a string with the new value. + + """ + if name in self.nodes['cst']: + n = getattr(self, name) + if '__iter__' in dir(n): + new_table_politic(self, name[:-1], date, new_val) + else: + new_cst_politic(self, name, date, new_val) + elif name in self.nodes['var']: + new_var_politic(self, name, date, new_val) + + def copy(self): + """Returns a copy of the system, with same equations. + + Returns + ------- + World3: + Copy of the system. + """ + + return World3(sys=super().copy()) + + def run(self, N=400, dt=0.5): + """Run the system with 400 steps of 1/2 year""" + super().run(N, dt) + + def plot_world(self, **kwargs): + """Plot the main variables of the world, in the "Limits To Growth: the 30th years update" way.""" + + plot_world_03(self, with_legend=True, **kwargs) + + def plot(self, *args, **kwargs): + """Plot the given variables and constants of a system. + + Parameters + ---------- + v_names: iterable(str) + Name of variables + + rescale: bool + If yes, variables are normalized between 0 and 1 + + com: bool + If yes, comments are shown in the legend + + filter_no: iterable(str) + Names of variables that won't appear in the plot + + scales: dict(str, float) + Scales of variables. Variables are divided by their respective scales on the plot. + + colors: dict(str, str) + Colors of each variable + + title: str + Title of the plot + + linestyle: str + Linestyle of the plot + + outside_legend_number: int + Number of lines from which legend is plotted outside the graph + + legend: bool + If yes, the legend is drawn + """ + + plot_system(self, *args, **kwargs) + + def definition(self, name): + """Returns the definition of a variable or constant.""" + return self.get_comment(name) + + def equation(self, name): + """Returns the pydynamo equation of a variable or constant.""" + return self.get_eq(name) + + def plot_non_linearity(self, var_name): + """Plot the non linear function used by the variable, if exists.""" + plot_non_linearity(self, var_name) + + def plot_compare(self, s, *args, **kwargs): + """Show the variables of 2 different systems. + + Parameters + ---------- + s: System + Other system to compare whith. + + v_names: iterable(str) + Names of variables or constant to plot + + scales: dict(str, float) + Scales of variables. Variables are divided by their respective scales on the plot. + + rescale: bool + If yes, If yes, variables are normalized between 0 and 1 + + *args + Argument list for the pydynamo.core.plot_utils.plot_system function + + **kwargs + Argument dictionnary for the pydynamo.core.plot_utils.plot_system function + """ + + compare_systems(self, s, *args, **kwargs) + + def __getitem__(self, arg): + try: + name, year = arg + return self.get_at(name, year) + + except TypeError: + return getattr(self, arg) + + def add_equations(self, equations): + """Add new equations to the system. Each variable uses the newest equation. + + Parameters + ---------- + equations: list(str) + New equations to add. + + """ + new_system(equations, self)