diff --git a/python/src/convert_mdl.py b/python/src/convert_mdl.py new file mode 100644 index 0000000..b1d2523 --- /dev/null +++ b/python/src/convert_mdl.py @@ -0,0 +1,3 @@ +import pysd + +model = pysd.read_vensim('./vensim/bes.mdl') diff --git a/vensim/__pycache__/bes.cpython-312.pyc b/vensim/__pycache__/bes.cpython-312.pyc new file mode 100644 index 0000000..5307576 Binary files /dev/null and b/vensim/__pycache__/bes.cpython-312.pyc differ diff --git a/vensim/bes.py b/vensim/bes.py new file mode 100644 index 0000000..5b0ec92 --- /dev/null +++ b/vensim/bes.py @@ -0,0 +1,692 @@ +""" +Python model 'bes.py' +Translated using PySD +""" + +from pathlib import Path +import numpy as np + +from pysd.py_backend.statefuls import Integ +from pysd import Component + +__pysd_version__ = "3.14.2" + +__data = {"scope": None, "time": lambda: 0} + +_root = Path(__file__).parent + + +component = Component() + +####################################################################### +# CONTROL VARIABLES # +####################################################################### + +_control_vars = { + "initial_time": lambda: 0, + "final_time": lambda: 120, + "time_step": lambda: 1, + "saveper": lambda: time_step(), +} + + +def _init_outer_references(data): + for key in data: + __data[key] = data[key] + + +@component.add(name="Time") +def time(): + """ + Current time of the model. + """ + return __data["time"]() + + +@component.add( + name="FINAL TIME", units="Month", comp_type="Constant", comp_subtype="Normal" +) +def final_time(): + """ + The final time for the simulation. + """ + return __data["time"].final_time() + + +@component.add( + name="INITIAL TIME", units="Month", comp_type="Constant", comp_subtype="Normal" +) +def initial_time(): + """ + The initial time for the simulation. + """ + return __data["time"].initial_time() + + +@component.add( + name="SAVEPER", + units="Month", + limits=(0.0, np.nan), + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"time_step": 1}, +) +def saveper(): + """ + The frequency with which output is stored. + """ + return __data["time"].saveper() + + +@component.add( + name="TIME STEP", + units="Month", + limits=(0.0, np.nan), + comp_type="Constant", + comp_subtype="Normal", +) +def time_step(): + """ + The time step for the simulation. + """ + return __data["time"].time_step() + + +####################################################################### +# MODEL VARIABLES # +####################################################################### + + +@component.add( + name="Noise", comp_type="Auxiliary", comp_subtype="Normal", depends_on={"time": 1} +) +def noise(): + return float(np.random.uniform(0, 5, size=())) + + +@component.add( + name="Demand", + units="loaves/Day", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"base_demand": 1, "noise": 1}, +) +def demand(): + return base_demand() + noise() + + +@component.add( + name="Amplitude of Seasonal Variations", + units="loaves", + comp_type="Constant", + comp_subtype="Normal", +) +def amplitude_of_seasonal_variations(): + return 30 + + +@component.add(name="phi", units="radians", comp_type="Constant", comp_subtype="Normal") +def phi(): + return 0 + + +@component.add( + name="Base Demand", units="loaves/Day", comp_type="Constant", comp_subtype="Normal" +) +def base_demand(): + return 200 + + +@component.add( + name="Sigma", units="loaves/Day", comp_type="Constant", comp_subtype="Normal" +) +def sigma(): + return 5 + + +@component.add(name="Period", units="days", comp_type="Constant", comp_subtype="Normal") +def period(): + return 7 + + +@component.add( + name="Available Workers", + units="workers", + comp_type="Constant", + comp_subtype="Normal", +) +def available_workers(): + return 2 + + +@component.add( + name="Base Rate", units="loaves/Day", comp_type="Constant", comp_subtype="Normal" +) +def base_rate(): + return 200 + + +@component.add( + name="Lead Time Sugar", units="days", comp_type="Constant", comp_subtype="Normal" +) +def lead_time_sugar(): + return 1 + + +@component.add( + name="Bread Production", + units="loaves/Day", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"base_rate": 1, "minresource": 1, "labour_efficiency": 1}, +) +def bread_production(): + return float(np.minimum(base_rate(), minresource())) * labour_efficiency() + + +@component.add( + name="Water Safety", units="litres", comp_type="Constant", comp_subtype="Normal" +) +def water_safety(): + return 300 + + +@component.add( + name="Sugar Purchase", + units="kg", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={ + "bread_production": 1, + "suger_per_loaf": 1, + "sugar_safety": 1, + "sugar": 1, + "lead_time_sugar": 1, + }, +) +def sugar_purchase(): + return float( + np.maximum( + 0, + (bread_production() * suger_per_loaf() + sugar_safety() - sugar()) + / lead_time_sugar(), + ) + ) + + +@component.add( + name="Flour per Loaf", units="kg/loaf", comp_type="Constant", comp_subtype="Normal" +) +def flour_per_loaf(): + return 0.3 + + +@component.add( + name="Suger per Loaf", units="kg/loaf", comp_type="Constant", comp_subtype="Normal" +) +def suger_per_loaf(): + return 0.02 + + +@component.add( + name="MinSugarWater", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"sugar": 1, "suger_per_loaf": 1, "water_per_loaf": 1, "water": 1}, +) +def minsugarwater(): + return float(np.minimum(sugar() / suger_per_loaf(), water() / water_per_loaf())) + + +@component.add( + name="Yeast Purchase", + units="kg", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={ + "bread_production": 1, + "yeast_per_loaf": 1, + "yeast_safety": 1, + "yeast": 1, + "lead_time_yeast": 1, + }, +) +def yeast_purchase(): + return float( + np.maximum( + 0, + (bread_production() * yeast_per_loaf() + yeast_safety() - yeast()) + / lead_time_yeast(), + ) + ) + + +@component.add( + name="Labour Efficiency", + units="Dmnl", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"available_workers": 1, "workers_required": 1}, +) +def labour_efficiency(): + return float(np.minimum(1, available_workers() / workers_required())) + + +@component.add( + name="Water", + units="litres", + comp_type="Stateful", + comp_subtype="Integ", + depends_on={"_integ_water": 1}, + other_deps={ + "_integ_water": { + "initial": {}, + "step": {"water_purchase": 1, "water_per_loaf": 1, "bread_production": 1}, + } + }, +) +def water(): + return _integ_water() + + +_integ_water = Integ( + lambda: water_purchase() - bread_production() * water_per_loaf(), + lambda: 2000, + "_integ_water", +) + + +@component.add( + name="Water Cost", units="$/litre", comp_type="Constant", comp_subtype="Normal" +) +def water_cost(): + return 0.001 + + +@component.add( + name="Lead Time Water", units="days", comp_type="Constant", comp_subtype="Normal" +) +def lead_time_water(): + return 1 + + +@component.add( + name="Lead Time Yeast", units="days", comp_type="Constant", comp_subtype="Normal" +) +def lead_time_yeast(): + return 1 + + +@component.add( + name="Water Purchase", + units="litres", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={ + "bread_production": 1, + "water_per_loaf": 1, + "water_safety": 1, + "water": 1, + "lead_time_water": 1, + }, +) +def water_purchase(): + return float( + np.maximum( + 0, + (bread_production() * water_per_loaf() + water_safety() - water()) + / lead_time_water(), + ) + ) + + +@component.add( + name="Workers Required", + units="workers", + comp_type="Constant", + comp_subtype="Normal", +) +def workers_required(): + return 2 + + +@component.add( + name="MinFlourYeast", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"flour": 1, "flour_per_loaf": 1, "yeast_per_loaf": 1, "yeast": 1}, +) +def minflouryeast(): + return float(np.minimum(flour() / flour_per_loaf(), yeast() / yeast_per_loaf())) + + +@component.add( + name="MinResource", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"minflouryeast": 1, "minsugarwater": 1}, +) +def minresource(): + return float(np.minimum(minflouryeast(), minsugarwater())) + + +@component.add( + name="Water per Loaf", + units="litres/loaf", + comp_type="Constant", + comp_subtype="Normal", +) +def water_per_loaf(): + return 0.2 + + +@component.add( + name="Waste Bread", units="loaves/Day", comp_type="Constant", comp_subtype="Normal" +) +def waste_bread(): + return 2 + + +@component.add( + name="Raw Material Cost", + units="$", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={ + "flour_cost": 1, + "flour_purchase": 1, + "sugar_purchase": 1, + "sugar_cost": 1, + "yeast_purchase": 1, + "yeast_cost": 1, + "water_purchase": 1, + "water_cost": 1, + }, +) +def raw_material_cost(): + return ( + flour_cost() * flour_purchase() + + sugar_cost() * sugar_purchase() + + yeast_cost() * yeast_purchase() + + water_cost() * water_purchase() + ) + + +@component.add( + name="Yeast Safety", units="kg", comp_type="Constant", comp_subtype="Normal" +) +def yeast_safety(): + return 10 + + +@component.add( + name="Sugar Safety", units="kg", comp_type="Constant", comp_subtype="Normal" +) +def sugar_safety(): + return 20 + + +@component.add( + name="Bread", + units="loaves", + comp_type="Stateful", + comp_subtype="Integ", + depends_on={"_integ_bread": 1}, + other_deps={ + "_integ_bread": { + "initial": {}, + "step": {"bread_production": 1, "waste_bread": 1, "sales": 1}, + } + }, +) +def bread(): + return _integ_bread() + + +_integ_bread = Integ( + lambda: bread_production() - waste_bread() - sales(), lambda: 0, "_integ_bread" +) + + +@component.add( + name="Flour", + units="kg", + comp_type="Stateful", + comp_subtype="Integ", + depends_on={"_integ_flour": 1}, + other_deps={ + "_integ_flour": { + "initial": {}, + "step": {"flour_purchase": 1, "flour_per_loaf": 1, "bread_production": 1}, + } + }, +) +def flour(): + return _integ_flour() + + +_integ_flour = Integ( + lambda: flour_purchase() - flour_per_loaf() * bread_production(), + lambda: 1000, + "_integ_flour", +) + + +@component.add( + name="Flour Cost", units="$/kg", comp_type="Constant", comp_subtype="Normal" +) +def flour_cost(): + return 0.5 + + +@component.add( + name="Flour Purchase", + units="kg", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={ + "bread_production": 1, + "flour_per_loaf": 1, + "flour_safety": 1, + "flour": 1, + "lead_time_flour": 1, + }, +) +def flour_purchase(): + return float( + np.maximum( + 0, + (bread_production() * flour_per_loaf() + flour_safety() - flour()) + / lead_time_flour(), + ) + ) + + +@component.add( + name="Flour Safety", units="kg", comp_type="Constant", comp_subtype="Normal" +) +def flour_safety(): + return 200 + + +@component.add( + name="Labour Cost", + units="$", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"wage_rate": 1, "total_labour_hours": 1}, +) +def labour_cost(): + return wage_rate() * total_labour_hours() + + +@component.add( + name="Lead Time Flour", units="days", comp_type="Constant", comp_subtype="Normal" +) +def lead_time_flour(): + return 1 + + +@component.add( + name="Maintainance Cost", + units="$", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"maintainance_rate": 1, "money": 1}, +) +def maintainance_cost(): + return maintainance_rate() * money() + + +@component.add( + name="Maintainance Rate", + units="fraction/Day", + comp_type="Constant", + comp_subtype="Normal", +) +def maintainance_rate(): + return 0.002 + + +@component.add( + name="Money", + units="$", + comp_type="Stateful", + comp_subtype="Integ", + depends_on={"_integ_money": 1}, + other_deps={ + "_integ_money": { + "initial": {}, + "step": { + "revenue": 1, + "labour_cost": 1, + "maintainance_cost": 1, + "raw_material_cost": 1, + }, + } + }, +) +def money(): + return _integ_money() + + +_integ_money = Integ( + lambda: revenue() - labour_cost() - maintainance_cost() - raw_material_cost(), + lambda: 100000, + "_integ_money", +) + + +@component.add( + name="Revenue", + units="$", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"sales_price": 1, "sales": 1}, +) +def revenue(): + return sales_price() * sales() + + +@component.add( + name="Sales", + units="loaves/Day", + comp_type="Auxiliary", + comp_subtype="Normal", + depends_on={"bread": 1, "demand": 1}, +) +def sales(): + return float(np.minimum(bread(), demand())) + + +@component.add( + name="Sales Price", units="$/loaf", comp_type="Constant", comp_subtype="Normal" +) +def sales_price(): + return 4 + + +@component.add( + name="Sugar", + units="kg", + comp_type="Stateful", + comp_subtype="Integ", + depends_on={"_integ_sugar": 1}, + other_deps={ + "_integ_sugar": { + "initial": {}, + "step": {"sugar_purchase": 1, "suger_per_loaf": 1, "bread_production": 1}, + } + }, +) +def sugar(): + return _integ_sugar() + + +_integ_sugar = Integ( + lambda: sugar_purchase() - bread_production() * suger_per_loaf(), + lambda: 100, + "_integ_sugar", +) + + +@component.add( + name="Sugar Cost", units="$/kg", comp_type="Constant", comp_subtype="Normal" +) +def sugar_cost(): + return 0.8 + + +@component.add( + name="Total Labour Hours", + units="hours/Day", + comp_type="Constant", + comp_subtype="Normal", +) +def total_labour_hours(): + return 16 + + +@component.add( + name="Wage Rate", units="$/Hour", comp_type="Constant", comp_subtype="Normal" +) +def wage_rate(): + return 12 + + +@component.add( + name="Yeast", + units="kg", + comp_type="Stateful", + comp_subtype="Integ", + depends_on={"_integ_yeast": 1}, + other_deps={ + "_integ_yeast": { + "initial": {}, + "step": {"yeast_purchase": 1, "yeast_per_loaf": 1, "bread_production": 1}, + } + }, +) +def yeast(): + return _integ_yeast() + + +_integ_yeast = Integ( + lambda: yeast_purchase() - bread_production() * yeast_per_loaf(), + lambda: 50, + "_integ_yeast", +) + + +@component.add( + name="Yeast Cost", units="$/kg", comp_type="Constant", comp_subtype="Normal" +) +def yeast_cost(): + return 10 + + +@component.add( + name="Yeast per Loaf", units="kg/loaf", comp_type="Constant", comp_subtype="Normal" +) +def yeast_per_loaf(): + return 0.01