高级用法
PySD的强大之处及其存在的动机在于它能够与Python环境中的其他模型和分析包相关联。在本节中,我们讨论这些联系是如何发生的。
一次运行一步(或多步)运行模型
耦合不同的模型需要它们之间交换某些变量。只有当步进机制可用时,这才可能实现,该机制允许在某些随时间演变的边界条件下运行任意数量的步骤。在本节中,我们展示如何使用 pysd.py_backend.model.Model.step()
函数(而不是 pysd.py_backend.model.Model.run()
函数)并在每个步骤之前更新模型变量的值::
from pysd.py_backend.output import ModelOutput
# instantiate ModelOutput object
output = ModelOutput()
# configure the stepper behavior, passing a list of the variables that
# will be updated before running each model step in the step_vars argument
model.set_stepper(output,
step_vars=["room_temperature"],
final_time=5)
# run 40 steps increasing the room_temperature by one degree at each step
for _ in range(40):
model.step(1, {"room_temperature": model["room_temperature"] + 1})
# store model results in a pandas DataFrame (default unless a file name
# is passed when instantiating the ModelOutput object)
result_df = output.collect(model)
用更复杂的对象替换模型组件
在最后一节中,我们看到参数可以随时间推移采用单个值或一系列值,PySD在提供的时间序列值之间线性内插。在幕后,PySD正在将该常数或时间序列转换成一个函数,然后继续替换模型中的原始组件。例如,在茶杯示例中,室温最初是通过将模型文件解析为类似于::
def room_temperature():
return 75
然而,当我们使室温随时间变化时,PySD将此函数替换为:
def room_temperature():
return np.interp(t, series.index, series.values)
这利用了系统的内部状态,即时间t,以及我们希望变量表示的时间序列数据序列。用户可以使用这个替换过程,如果我们小心的话,我们可以自己替换功能。
由于PySD假设模型中的所有组件都表示为不带参数的函数,因此我们想要修改的任何组件都必须用不带参数的函数替换。由于系统的状态和所有辅助或流方法都是公开的,因此我们的替换函数可以将这些方法作为其内部结构的一部分来调用。
在我们的茶杯例子中,假设我们不知道计算房间热量损失的函数形式,而是有很多茶杯温度和热流率的数据。我们可以使用回归模型(这里是Scikit-Learn的支持向量回归)来代替分析函数:
from sklearn.svm import SVR
regression = SVR()
regression.fit(X_training, Y_training)
回归模型适合后,我们为其预测方法编写一个包装函数,该函数访问模型的输入组件并格式化PySD的预测::
def new_heatflow_function():
""" Replaces the original flowrate equation
with a regression model"""
tea_temp = model.components.teacup_temperature()
room_temp = model.components.room_temperature()
return regression.predict([room_temp, tea_temp])[0]
要使用直接替换此函数来替换heat_loss_to_room模型组件 set_components()
方法:
model.set_components({'heat_loss_to_room': new_heatflow_function})
如果您想替换带有后缀的变量,则需要确保新函数的输出与前一个函数的输出相同。您可以使用检查零部件的当前坐标和尺寸 get_coords()
正如中所解释的那样 Getting started .
备注
或者,您还可以直接设置模型组件::
model.components.heat_loss_to_room = new_heatflow_function
然而,这将仅接受模型组件的pony名称。而对于 set_components()
方法,也可以使用原来的名称。
将Vensim视图拆分为单独的Python文件(模块)
为了在转换后的模型中复制Vensim视图,用户可以设置 split_views 中对True的争论 pysd.read_vensim()
功能::
read_vensim("many_views_model.mdl", split_views=True)
将模型拆分为视图的选项对于具有数十个视图的大型模型来说尤其有趣。将这些模型转换为单个文件可能会使生成的Python模型难以阅读和维护。
在具有三个独立视图的Vensim模型中(例如 view_1 , view_2 和 view_3 )、设置 split_views 到True将在 .mdl 模型位于:
每个文件中的变量将使用其Python名称按字母顺序排序。
备注
Often, modelers wish to organise views further. To that end, a common practice is to include a particular character in the View name to indicate that what comes after it is the name of the subview. For instance, we could name one view as ENERGY.Supply and another one as ENERGY.Demand. In that particular case, setting the subview_sep kwarg equal to ["."], as in the code below, would name the translated views as demand.py and supply.py and place them inside the ENERGY folder:
read_vensim("many_views_model.mdl", split_views=True, subview_sep=["."])
备注
如果变量显示为 workbench variable 在多个视图中,它将仅添加到与第一个视图对应的模块中,并且将打印警告消息。如果变量在任何视图中没有作为工作台变量出现,则它将被添加到主模型文件中并打印警告消息。
如果存在宏,它们将自包含在以宏本身命名的文件中。宏内部变量将被放置在与定义它们的视图相对应的模块中。
从另一个模拟的结束状态开始模拟
可以使用 export()
方法:
import pysd
model1 = pysd.read_vensim("my_model.mdl")
model1.run(final_time=50)
model1.export("final_state.pic")
则导出的数据可以在另一个会话中使用::
import pysd
model2 = pysd.load("my_model.py")
model2 = run(initial_condition="final_state.pic", return_timestamps=[55, 60])
新模拟的初始时间将等于50,并保存前一个模拟的值。
备注
您可以使用设置模拟的确切最终时间 final_time 论点如果您想避免退回可以使用的股票的打印机 return_timestamps=[]
model1.run(final_time=50, return_timestamps=[])
备注
所做的更改 params 参数不被移植到新模型( model2 )您初始化的对象 final_state.pic .如果您想保留它们,您需要用相同的方式调用运行 params 与原始模型中的值( model1 ).
警告
导出的数据将使用 pickle .泡菜中存储的数据可能与未来版本不兼容 PySD 或 xarray .为了防止数据丢失,请始终保存源代码。
选择和运行子模型
已转换模型的子模型可以作为独立模型运行。这可以通过 select_submodel()
方法:
- Model.select_submodel(vars=[], modules=[], exogenous_components={}, inplace=True)[源代码]
从原始模型中选择子模型。选择子模型后,仅计算集成该子模型所需的有状态对象。使用示例可在 Advanced Usage .
- 参数:
vars (set or list of strings (optional)) -- 要包含在新子模型中的变量。如果仅通过模块名称选择子模型,则可以是空列表。默认为空列表。
modules (set or list of strings (optional)) -- 新子模型中包含的模块。如果仅通过变量名称选择子模型,则可以是空列表。默认为空列表。可以通过传递不带.py的路径来选择完整模块或子模块,例如:“view_1/submodule1”。
exogenous_components (dictionary of parameters (optional)) -- 用于修复运行所选子模型所需的模型变量的外部值。externative_components应该作为字典传递,与set_components方法相同。默认情况下,它是空的dict,并且所需的外部组件将被设置为numpy.nan值。
inplace (bool (optional)) -- 如果为True,它将修改当前对象并返回无。如果为假,它将创建模型的副本并返回它,保持原始模型不变。默认为True。
- 返回:
如果inplace=False,它将返回原始模型的修改副本。
- 返回类型:
None or pysd.py_backend.model.Model
备注
只有当模型在转换过程中被拆分为不同的文件时,才能传递模块。
示例
>>> model.select_submodel( ... vars=["Room Temperature", "Teacup temperature"]) UserWarning: Selecting submodel, to run the full model again use model.reload()
>>> model.select_submodel( ... modules=["view_1", "view_2/subview_1"]) UserWarning: Selecting submodel, to run the full model again use model.reload() UserWarning: Exogenous components for the following variables are necessary but not given: initial_value_stock1, stock3
>>> model.select_submodel( ... vars=["stock3"], ... modules=["view_1", "view_2/subview_1"]) UserWarning: Selecting submodel, to run the full model again use model.reload() UserWarning: Exogenous components for the following variables are necessary but not given: initial_value_stock1, initial_value_stock3 Please, set them before running the model using set_components method...
>>> model.select_submodel( ... vars=["stock3"], ... modules=["view_1", "view_2/subview_1"], ... exogenous_components={ ... "initial_value_stock1": 3, ... "initial_value_stock3": 5}) UserWarning: Selecting submodel, to run the full model again use model.reload()
备注
此方法将变异原始模型。如果您想要拥有包含所选变量/模块的模型副本,可以使用 inplace=False
论点
为了预览所需的外生变量, get_dependencies()
可以使用的方法:
- Model.get_dependencies(vars=[], modules=[])[源代码]
获取一组变量或模块的依赖项。
- 参数:
- 返回:
dependencies --附属数据对象。
- 返回类型:
pysd.py_backend.utils.Dependencies
备注
只有当模型在转换过程中被拆分为不同的文件时,才能传递模块。
示例
>>> print(model.get_dependencies( ... vars=["Room Temperature", "Teacup temperature"])) Selected variables (total 1): room_temperature, teacup_temperature Stateful objects integrated with the selected variables (total 1): _integ_teacup_temperature
>>> print(model.get_dependencies( ... modules=["view_1", "view_2/subview_1"])) Selected variables (total 4): var1, var2, stock1, delay1 Dependencies for initialization only (total 1): initial_value_stock1 Dependencies that may change over time (total 2): stock3 Stateful objects integrated with the selected variables (total 1): _integ_stock1, _delay_fixed_delay1
>>> print(model.get_dependencies( ... vars=["stock3"], ... modules=["view_1", "view_2/subview_1"])) Selected variables (total 4): var1, var2, stock1, stock3, delay1 Dependencies for initialization only (total 1): initial_value_stock1, initial_value_stock3 Stateful objects integrated with the selected variables (total 1): _integ_stock1, _integ_stock3, _delay_fixed_delay1
从netCDF文件初始化外部数据
IO操作成本高昂,尤其是在读取非二进制文件时。当需要从电子表格文件中读取大量数据集时,这使得模型初始化过程变慢。
在PySD 3.8中,用户可以将子集或所有模型外部数据输出到netEDF文件,并使用该文件进行后续模型初始化。
假设我们有一个模型( my_model.mdl )那个负载 param_1 从 parameters_1.xls , param_2 从 parameters_2.xls ,而且 policy_1 和 policy_2 从 scenario.xls .想象一下,我们想要通过改变的值来测试不同的策略配置 policy_1 和 policy_2 ,同时保持所有其他参数不变。在这种情况下,我们可能想要输出不打算修改的外部对象( param_1 和 param_2 )转换为netCDF文件,以便它们立即初始化:
下面的代码展示了如何实现这一点::
import pysd
model = pysd.read_vensim("my_model.mdl", initialize=False)
model.serialize_externals(export_path="parameters.nc",
include_externals="all",
exclude_externals=["scenario.xls"])
这将仅输出中定义的外部数据 parameters_1.xls 和 parameters_2.xls 到 parameters.xls 文件.这样做的一个好处是,生成的netEDF文件将包括模型中为此类变量定义的所有元数据(描述、单位等)
请注意,通过以下方式可以获得完全相同的结果:
import pysd
model = pysd.read_vensim("my_model.mdl", initialize=False)
model.serialize_externals(export_path="parameters.nc",
include_externals="all",
exclude_externals=["policy_1", "policy_2"])
或者甚至::
import pysd
model = pysd.read_vensim("my_model.mdl", initialize=False)
model.serialize_externals(export_path="parameters.nc",
include_externals=["parameters_1.xls",
"parameters_2.xls"],
exclude_externals=None)
Or we could have also combined variable names and spreadsheet files in the include_externals argument, the exclude_externals argument or both:
import pysd
model = pysd.read_vensim("my_model.mdl", initialize=False)
model.serialize_externals(export_path="parameters.nc",
include_externals=["param_1", "parameters_2.xls"],
exclude_externals=None)
然后,运行模拟加载存储在 parameters.nc ,我们写::
import pysd
model = pysd.read_vensim("my_model.mdl", initialize=False)
model.initialize_external_data(externals="parameters.nc")
从这里开始,我们可以正常运行模型, pysd.py_backend.Model.run()
.