添加新功能

在本节中,您可能会找到一些有用的示例,用于向PySD Python构建器添加新函数。在开始添加任何新功能或功能之前,请确保没有人正在处理它。搜索您要处理的功能是否存在任何未决问题,如果不存在,则打开新功能。然后,声称您正在研究它。

添加硬编码函数

最简单的情况是当现有的抽象结构 pysd.translators.structures.abstract_expressions.CallStructure 可以使用此结构保存对函数名和传递的参数的引用, tuple .有时,可以直接将该函数添加到模型文件中,而无需定义任何具体的函数。当该函数已经在Python或Python库中实现并且行为相同时,就可以完成这一操作。例如, Vensim's ABSXMILE's ABS 功能可以替换为 numpy.abs() .

在这个简单的情况下,我们只需要将转换包括在 functionspace 词典 pysd.builders.python.python_functions.py

"abs": ("np.abs(%(0)s)", ("numpy",)),

他们的关键 ("abs" )是Vensim/XMILE函数的名称。值中的第一个参数 ("np.abs(%(0)s)" )是Python表示, %(0)s 代表原始函数的第一个参数。最后一个参数代表该函数的依赖项;在这种情况下,使用的函数包含在 numpy module.因此,我们需要进口 numpy 在我们的模型文件中,这是通过添加依赖项来完成的 ("numpy",) ,请注意,依赖关系是 tuple .

The next step is to test the new function. In order to do that, we need to include integration tests in the test-models repo. Please, follow the instructions to add a new test in the README of that repo. For this example, we would need to add test models for a Vensim's mdl file and a XMILE file, as we are adding support for both. In addition, the tests should cover all the possible cases. For that reason, we should test the absolute of positive and negative floats and positive, negative and mixed arrays. We included the tests test-models/tests/abs/test_abs.mdl and test-models/tests/abs/test_abs.xmile, with their corresponding outputs file. Now we include the test in the testing script. We need to add the following entry in the vensim_test dictionary of tests/pytest_integration/pytest_integration_test_vensim_pathway.py:

"abs": {
    "folder": "abs",
    "file": "test_abs.mdl"
},

以及下面的一个 xmile_test 词典 tests/pytest_integration/pytest_integration_test_xmile_pathway.py

"abs": {
    "folder": "abs",
    "file": "test_abs.xmile"
},

此时,我们应该能够运行测试,如果实现正确,那么它们应该通过。我们还需要确保运行所有测试不会破坏任何其他功能。

为了完成贡献,我们应该更新文档。的桌子 supported Vensim functions , supported Xmile functions ,而且 supported Python functions 是自动生成的 docs/tables/*.tab ,它们是制表符分隔的文件。在这种情况下,我们应该将以下行添加到 docs/tables/functions.tab :

ABS

Vensim

文西姆例子

X米尔

Xmile示例

抽象语法

Python转换

ABS

ABS(A)

ABS

腹肌(A)

CallStructure('abs', (A,))

numpy.abs(A)

最后,我们在顶部创建一个新的发行注释块 docs/whats_new.rst 文件并更新软件版本。提交所有更改,包括测试模型存储库,并打开一个新的PR。

添加简单功能

有时,最好定义自己的Python函数。这可以帮助保持与源代码相似的语法,使最终的模型文件内容更简单。这个示例重点关注我们仍然可以使用抽象结构的函数 pysd.translators.structures.abstract_expressions.CallStructure ,但我们将包括中定义的函数 pysd.py_backend.functions .

假设我们想要添加对 Vensim's VECTOR SORT ORDER function .首先,我们可能需要检查Vensim的文档来了解这个函数的工作原理,并尝试思考解决这个问题的最快方法。VECTOR SORT Order函数接受两个参数, vectordirection .该函数返回 vector 基于 direction .因此,我们不需要保存之前的状态信息或将其他信息作为参数传递,我们应该有足够的信息使用接受相同参数的基本Python函数。

然后,我们根据Vensim的文档定义Python函数。我们还包括doc字符串(与其他函数具有相同的风格)并将此函数添加到文件中 pysd.py_backend.functions

def vector_sort_order(vector, direction):
    """
    Implements Vensim's VECTOR SORT ORDER function. Sorting is done on
    the complete vector relative to the last subscript.
    https://www.vensim.com/documentation/fn_vector_sort_order.html

    Parameters
    -----------
    vector: xarray.DataArray
        The vector to sort.
    direction: float
        The direction to sort the vector. If direction > 1 it will sort
        the vector entries from smallest to biggest, otherwise from
        biggest to smallest.

    Returns
    -------
    vector_sorted: xarray.DataArray
        The sorted vector.

    """
    if direction <= 0:
        flip = np.flip(vector.argsort(), axis=-1)
        return xr.DataArray(flip.values, vector.coords, vector.dims)
    return vector.argsort()

现在,我们需要将定义的函数与其相应的抽象表示相关联。因此,我们将以下条目包括在 functionspace 词典 pysd.builders.python.python_functions.py

"vector_sort_order": (
    "vector_sort_order(%(0)s, %(1)s)",
    ("functions", "vector_sort_order"))

他们的关键 ("vector_sort_order" )是收件箱中Vensim函数的名称,并将空白替换为强调线。值中的第一个参数 ("vector_sort_order(%(0)s, %(1)s)" )是Python表示, %(0)s%(1)s 分别代表原始函数的第一个和第二个参数。在这个例子中,该表示与Vensim中的表示非常相似,我们将从 VECTOR SORT ORDER(vec, direction)vector_sort_order(vec, direction) .最后一个参数代表该函数的依赖项;在这种情况下,该函数已包含在函数子模块中。因此,我们需要进口 vector_sort_orderfunctions ,这是通过添加依赖项来完成的 ("functions", "vector_sort_order") .

下一步是为Vensim的 mdl 文件.由于测试应该涵盖所有可能的情况,因此我们应该测试沿着维度具有不同值的一维和多个维度数组的结果,以生成不同的顺序组合。此外,我们还包括了两个可能方向的案例。我们包括了测试 test-models/tests/vector_order/test_vector_order.mdl ,及其相应的输出文件。现在我们将测试包括在测试脚本中。我们需要在 vensim_test 词典 tests/pytest_integration/pytest_integration_test_vensim_pathway.py

"vector_order": {
    "folder": "vector_order",
    "file": "test_vector_order.mdl"
},

此时,我们应该能够运行测试,如果实现正确,那么它们应该通过。我们还需要确保运行所有测试不会破坏任何其他功能。

为了完成贡献,我们应该通过添加以下行来更新文档, docs/tables/functions.tab :

载体排序

Vensim

文西姆例子

X米尔

Xmile示例

抽象语法

Python转换

载体排序

VECTOR SORT ORDER(vec, direction)

呼叫结构(' vector_sort_order ',(vec,Direction))

vector_sort_order(vec,direction)

最后,我们在顶部创建一个新的发行注释块 docs/whats_new.rst 文件并更新软件版本。提交所有更改,包括测试模型存储库,并打开一个新的PR。