Exemplo n.º 1
0
def main():
    parser = argparse.ArgumentParser(prog='pade-fmi')
    parser.add_argument('-v',
                        '--version',
                        action='version',
                        version=__version__)

    parser.add_argument('project_files',
                        metavar='filename',
                        type=str,
                        nargs='+',
                        help='File containing FMU settings')
    parser.add_argument('-d',
                        '--dest',
                        dest='dest',
                        help='Where to save the FMU.',
                        default='.')

    args = parser.parse_args()

    FmuBuilder.build_FMU(
        script_file=pade.fmi.fmi_wrapper.__file__,
        dest=args.dest,
        project_files=args.project_files +
        [os.path.join(os.path.dirname(__file__), 'requirements.txt')],
        needsExecutionTool=False)
Exemplo n.º 2
0
def test_deploy(tmp_path, test_manager, requirements, test_requirements,
                expected):

    dummy_requirements = """numpy=1.16
scipy
"""
    if requirements is not None:
        with TemporaryDirectory() as tempd:
            requirements_file = Path(tempd) / requirements
            requirements_file.write_text(dummy_requirements)
            fmu = FmuBuilder.build_FMU(PYTHON_SLAVE,
                                       dest=tmp_path,
                                       project_files=[
                                           requirements_file,
                                       ])
    else:
        fmu = FmuBuilder.build_FMU(PYTHON_SLAVE, dest=tmp_path)
    assert fmu.exists()

    with patch("subprocess.run") as run:
        if isinstance(expected, type) and issubclass(expected, Exception):
            with pytest.raises(expected):
                deploy(fmu,
                       environment=test_requirements,
                       package_manager=test_manager)
            run.assert_not_called()
        else:
            deploy(fmu,
                   environment=test_requirements,
                   package_manager=test_manager)

            run.assert_called_once()
            assert (test_manager or expected) in " ".join(run.call_args[0][0])
Exemplo n.º 3
0
def test_integration_throw_py_error(tmp_path):
    fmpy = pytest.importorskip(
        "fmpy", reason="fmpy is not available for testing the produced FMU")

    slave_code = """from pythonfmu.fmi2slave import Fmi2Slave, Fmi2Causality, Integer, Real, Boolean, String


class PythonSlaveWithException(Fmi2Slave):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.realIn = 22.0
        self.realOut = 0.0
        self.register_variable(Real("realIn", causality=Fmi2Causality.input))
        self.register_variable(Real("realOut", causality=Fmi2Causality.output))

    def do_step(self, current_time, step_size):
        raise RuntimeError()
        return True
"""

    script_file = tmp_path / "orig" / "slavewithexception.py"
    script_file.parent.mkdir(parents=True, exist_ok=True)
    script_file.write_text(slave_code)

    fmu = FmuBuilder.build_FMU(script_file, dest=tmp_path)
    assert fmu.exists()

    with pytest.raises(Exception):
        fmpy.simulate_fmu(str(fmu), stop_time=1.0)
Exemplo n.º 4
0
def test_integration_get_state(tmp_path):
    script_file = Path(__file__).parent / DEMO

    fmu = FmuBuilder.build_FMU(script_file,
                               dest=tmp_path,
                               needsExecutionTool="false",
                               canGetAndSetFMUstate="true")
    assert fmu.exists()

    model = pyfmi.load_fmu(str(fmu))

    vr = model.get_model_variables()["realOut"].value_reference
    dt = 0.1
    t = 0.0

    def step_model():
        nonlocal t
        model.do_step(t, dt, True)
        t += dt

    model.initialize()
    step_model()
    state = model.get_fmu_state()
    assert model.get_real([vr])[0] == pytest.approx(dt, rel=1e-7)
    step_model()
    assert model.get_real([vr])[0] == pytest.approx(dt * 2, rel=1e-7)
    model.set_fmu_state(state)
    assert model.get_real([vr])[0] == pytest.approx(dt, rel=1e-7)
    step_model()
    assert model.get_real([vr])[0] == pytest.approx(dt * 3, rel=1e-7)
    model.free_fmu_state(state)
Exemplo n.º 5
0
    def build():
        with tempfile.TemporaryDirectory() as project_dir:
            project_dir = Path(project_dir)

            project_files = set()
            for f in pfiles:
                full_name = project_dir / f
                if full_name.suffix:
                    full_name.parent.mkdir(parents=True, exist_ok=True)
                    full_name.write_text("dummy content")
                else:
                    full_name.mkdir(parents=True, exist_ok=True)

                # Add subfolder and not file if common parent exits
                to_remove = None
                for p in project_files:
                    if p.parent == full_name.parent or p == full_name.parent:
                        to_remove = p
                        break
                if to_remove is None:
                    project_files.add(full_name)
                else:
                    project_files.remove(to_remove)
                    project_files.add(full_name.parent)

            return FmuBuilder.build_FMU(script_file, dest=tmp_path, project_files=project_files)
Exemplo n.º 6
0
 def build():
     with tempfile.TemporaryDirectory() as documentation_dir:
         doc_dir = Path(documentation_dir)
         license_file = doc_dir / "licenses" / "license.txt"
         license_file.parent.mkdir()
         license_file.write_text("Dummy license")
         index_file = doc_dir / "index.html"
         index_file.write_text("dummy index")
         return FmuBuilder.build_FMU(script_file, dest=tmp_path, documentation_folder=doc_dir)
Exemplo n.º 7
0
def test_integration_throw_py_error(tmp_path):
    fmpy = pytest.importorskip(
        "fmpy", reason="fmpy is not available for testing the produced FMU")

    script_file = Path(__file__).parent / "slaves/PythonSlaveWithException.py"
    fmu = FmuBuilder.build_FMU(script_file, dest=tmp_path)
    assert fmu.exists()

    with pytest.raises(Exception):
        fmpy.simulate_fmu(str(fmu), stop_time=1.0)
Exemplo n.º 8
0
def test_simple_integration_fmpy(tmp_path):
    fmpy = pytest.importorskip(
        "fmpy", reason="fmpy is not available for testing the produced FMU")

    script_file = Path(__file__).parent / "slaves/pythonslave.py"
    fmu = FmuBuilder.build_FMU(script_file, dest=tmp_path)
    assert fmu.exists()
    res = fmpy.simulate_fmu(str(fmu), stop_time=2.0)

    assert res["realOut"][-1] == pytest.approx(res["time"][-1], rel=1e-7)
Exemplo n.º 9
0
def test_integration_demo(tmp_path):
    script_file = Path(__file__).parent / "slaves/pythonslave.py"
    fmu = FmuBuilder.build_FMU(script_file,
                               dest=tmp_path,
                               needsExecutionTool="false")
    assert fmu.exists()
    model = pyfmi.load_fmu(str(fmu))
    res = model.simulate(final_time=0.5)

    assert res["realOut"][-1] == pytest.approx(res["time"][-1], rel=1e-7)
Exemplo n.º 10
0
def test_integration_get_serialize_state(tmp_path):
    fmpy = pytest.importorskip(
        "fmpy", reason="fmpy is not available for testing the produced FMU")

    script_file = Path(__file__).parent / DEMO

    fmu = FmuBuilder.build_FMU(script_file,
                               dest=tmp_path,
                               canGetAndSetFMUstate="true",
                               canSerializeFMUstate="true")
    assert fmu.exists()

    model_description = fmpy.read_model_description(fmu)
    unzip_dir = fmpy.extract(fmu)

    model = fmpy.fmi2.FMU2Slave(
        guid=model_description.guid,
        unzipDirectory=unzip_dir,
        modelIdentifier=model_description.coSimulation.modelIdentifier,
        instanceName='instance1')

    realOut = filter(lambda var: var.name == "realOut",
                     model_description.modelVariables)
    vrs = list(map(lambda var: var.valueReference, realOut))
    t = 0.0
    dt = 0.1

    def step_model():
        nonlocal t
        model.doStep(t, dt)
        t += dt

    model.instantiate()
    model.setupExperiment()
    model.enterInitializationMode()
    model.exitInitializationMode()

    step_model()
    state = model.getFMUstate()
    assert model.getReal(vrs)[0] == pytest.approx(dt, rel=1e-7)
    step_model()
    assert model.getReal(vrs)[0] == pytest.approx(dt * 2, rel=1e-7)
    model.setFMUstate(state)
    assert model.getReal(vrs)[0] == pytest.approx(dt, rel=1e-7)
    step_model()
    assert model.getReal(vrs)[0] == pytest.approx(dt * 3, rel=1e-7)

    serialize_fmu_state = model.serializeFMUstate(state)
    model.freeFMUstate(state)
    de_serialize_fmu_state = model.deSerializeFMUstate(serialize_fmu_state)
    model.setFMUstate(de_serialize_fmu_state)
    assert model.getReal(vrs)[0] == pytest.approx(dt, rel=1e-7)

    model.freeFMUstate(de_serialize_fmu_state)
    model.terminate()
Exemplo n.º 11
0
def test_zip_content(tmp_path):

    script_name = "pythonslave.py"
    script_file = Path(__file__).parent / "slaves" / script_name
    fmu = FmuBuilder.build_FMU(script_file, dest=tmp_path)
    assert fmu.exists()
    assert zipfile.is_zipfile(fmu)

    with zipfile.ZipFile(fmu) as files:
        names = files.namelist()

        assert "modelDescription.xml" in names
        assert "/".join(("resources", script_name)) in names
        module_file = "/".join(("resources", "slavemodule.txt"))
        assert module_file in names

        nfiles = 20
        if FmuBuilder.has_binary():
            assert ("/".join(
                ("binaries", get_platform(), f"PythonSlave.{lib_extension}"))
                    in names)
        else:
            nfiles -= 1

        # Check sources
        src_folder = Path(pythonfmu.__path__[0]) / "pythonfmu-export"
        for f in itertools.chain(
                src_folder.rglob("*.hpp"),
                src_folder.rglob("*.cpp"),
                src_folder.rglob("CMakeLists.txt"),
        ):
            assert "/".join(
                ("sources", f.relative_to(src_folder).as_posix())) in names

        # Check pythonfmu is embedded
        pkg_folder = Path(pythonfmu.__path__[0])
        for f in pkg_folder.rglob("*.py"):
            relative_f = f.relative_to(pkg_folder).as_posix()
            if "test" not in relative_f:
                assert "/".join(
                    ("resources", "pythonfmu", relative_f)) in names

        assert len(
            names
        ) >= nfiles  # Library + python script + XML + module name + sources

        with files.open(module_file) as myfile:
            assert myfile.read() == b"pythonslave"
Exemplo n.º 12
0
def test_integration_has_local_dep(tmp_path):
    slave_code = """import math
from pythonfmu.fmi2slave import Fmi2Slave, Fmi2Causality, Integer, Real, Boolean, String
from localmodule import get_amplitude, get_time_constant


class PythonSlaveWithDep(Fmi2Slave):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.realIn = 22.0
        self.realOut = 0.0
        self.register_variable(Real("realIn", causality=Fmi2Causality.input))
        self.register_variable(Real("realOut", causality=Fmi2Causality.output))

    def do_step(self, current_time, step_size):
        self.realOut = self.realIn * get_amplitude() * math.exp((current_time + step_size) / get_time_constant())
        return True
"""

    local_module = """def get_amplitude():
    return 5.

def get_time_constant():
    return 0.1
"""

    script_file = tmp_path / "orig" / "slavewithdep.py"
    script_file.parent.mkdir(parents=True, exist_ok=True)
    script_file.write_text(slave_code)

    local_file = script_file.parent / "localmodule.py"
    local_file.write_text(local_module)

    fmu = FmuBuilder.build_FMU(
        script_file,
        dest=tmp_path,
        project_files=[local_file],
        needsExecutionTool="false",
    )
    assert fmu.exists()
    model = pyfmi.load_fmu(str(fmu))
    res = model.simulate(final_time=0.5)

    assert res["realOut"][-1] == pytest.approx(22.0 * 5.0 *
                                               math.exp(res["time"][-1] / 0.1),
                                               rel=1e-7)
Exemplo n.º 13
0
def test_integration_read_from_file(tmp_path):
    script_file = Path(__file__).parent / "slaves/pythonslave_read_file.py"
    project_file = Path(__file__).parent / "data/hello.txt"
    fmu = FmuBuilder.build_FMU(script_file,
                               project_files=[project_file],
                               dest=tmp_path,
                               needsExecutionTool="false")
    assert fmu.exists()
    model = pyfmi.load_fmu(str(fmu))

    variables = model.get_model_variables()
    var = variables["file_content"]
    model_value = model.get_string([var.value_reference])[0]

    with (open(project_file, 'r')) as file:
        data = file.read()

    assert model_value == data
Exemplo n.º 14
0
def test_integration_has_local_dep(tmp_path):

    script_file = Path(__file__).parent / "slaves/slavewithdep.py"
    local_file = Path(__file__).parent / "slaves/localmodule.py"

    fmu = FmuBuilder.build_FMU(
        script_file,
        dest=tmp_path,
        project_files=[local_file],
        needsExecutionTool="false",
    )
    assert fmu.exists()
    model = pyfmi.load_fmu(str(fmu))
    res = model.simulate(final_time=0.5)

    assert res["realOut"][-1] == pytest.approx(22.0 * 5.0 *
                                               math.exp(res["time"][-1] / 0.1),
                                               rel=1e-7)
Exemplo n.º 15
0
def test_integration_reset(tmp_path):
    script_file = Path(__file__).parent / "slaves/pythonslave.py"
    fmu = FmuBuilder.build_FMU(script_file,
                               dest=tmp_path,
                               needsExecutionTool="false")
    assert fmu.exists()

    vr = 5  # realOut
    dt = 0.1
    model = pyfmi.load_fmu(str(fmu))
    initial_value = model.get_real([vr])[0]
    assert initial_value == pytest.approx(3.0, rel=1e-7)
    model.do_step(0.0, dt, True)
    read = model.get_real([vr])[0]
    assert read == pytest.approx(dt, rel=1e-7)
    model.reset()
    read = model.get_real([vr])[0]
    assert read == pytest.approx(initial_value, rel=1e-7)
Exemplo n.º 16
0
    def build():
        with tempfile.TemporaryDirectory() as project_dir:
            project_dir = Path(project_dir)
            script_file = project_dir / DEMO
            with open(orig_script_file) as script_f:
                script_file.write_text(script_f.read())

            for f in pfiles:
                full_name = project_dir / f
                if full_name.suffix:
                    full_name.parent.mkdir(parents=True, exist_ok=True)
                    full_name.write_text("dummy content")
                else:
                    full_name.mkdir(parents=True, exist_ok=True)

            return FmuBuilder.build_FMU(
                script_file, dest=tmp_path, project_files=[script_file.parent]
            )
Exemplo n.º 17
0
  def buildFMU(self, dest: FilePath = ".") -> Path:
    """
      Build the FMU
      @ In, dest, FilePath, destination of the built fmu
      @ Out, built, Path, the path of the built fmu
    """
    if not os.path.exists(self.serializedModel):
      self.raiseAnError(ValueError, "No such file {}".format(self.serializedModel))

    self._options["dest"] = os.path.dirname(dest)
    self._options["file_name"] = os.path.basename(dest)
    #Get files needed for running model
    self._options["project_files"] = {self.serializedModel}.union(self.model.getSerializationFiles())

    scriptFile = Path(self._temp) / (self.model.name + "_RAVENfmu.py")
    with open(scriptFile, "+w") as f:
      f.write(self.createModelHandler())
    self._options["script_file"] = scriptFile

    built = FmuBuilder.build_FMU(**self._options)
    if not self.keepModule:
      self._temp.cleanup()
    return  built
Exemplo n.º 18
0
def test_integration_set(tmp_path):
    script_file = Path(__file__).parent / DEMO

    fmu = FmuBuilder.build_FMU(script_file,
                               dest=tmp_path,
                               needsExecutionTool="false")
    assert fmu.exists()
    model = pyfmi.load_fmu(str(fmu))

    to_test = {
        "intParam": 20,
        "realIn": 1.0 / 3.0,
        "booleanParameter": True,
        "stringParameter": "cat",
        "container.someReal": 42.0,
        "container.subContainer.someInteger": 421
    }

    variables = model.get_model_variables()
    for key, value in to_test.items():
        var = variables[key]
        if var.type == pyfmi.fmi.FMI2_INTEGER:
            model.set_integer([var.value_reference], [value])
            model_value = model.get_integer([var.value_reference])[0]
        elif var.type == pyfmi.fmi.FMI2_REAL:
            model.set_real([var.value_reference], [value])
            model_value = model.get_real([var.value_reference])[0]
        elif var.type == pyfmi.fmi.FMI2_BOOLEAN:
            model.set_boolean([var.value_reference], [value])
            model_value = model.get_boolean([var.value_reference])[0]
        elif var.type == pyfmi.fmi.FMI2_STRING:
            model.set_string([var.value_reference], [value])
            model_value = model.get_string([var.value_reference])[0]
        else:
            pytest.xfail("Unsupported type")

        assert model_value == value
Exemplo n.º 19
0
def test_integration_get(tmp_path):
    script_file = Path(__file__).parent / "slaves/pythonslave.py"
    fmu = FmuBuilder.build_FMU(script_file,
                               dest=tmp_path,
                               needsExecutionTool="false")
    assert fmu.exists()
    model = pyfmi.load_fmu(str(fmu))

    to_test = {
        "intParam": 42,
        "intOut": 23,
        "realOut": 3.0,
        "booleanVariable": True,
        "stringVariable": "Hello World!",
        "realIn": 2.0 / 3.0,
        "booleanParameter": False,
        "stringParameter": "dog",
        "container.someReal": 99.0,
        "container.subContainer.someInteger": -15
    }

    model_value = None
    variables = model.get_model_variables()
    for key, value in to_test.items():
        var = variables[key]
        if var.type == pyfmi.fmi.FMI2_INTEGER:
            model_value = model.get_integer([var.value_reference])[0]
        elif var.type == pyfmi.fmi.FMI2_REAL:
            model_value = model.get_real([var.value_reference])[0]
        elif var.type == pyfmi.fmi.FMI2_BOOLEAN:
            model_value = model.get_boolean([var.value_reference])[0]
        elif var.type == pyfmi.fmi.FMI2_STRING:
            model_value = model.get_string([var.value_reference])[0]
        else:
            pytest.xfail("Unsupported type")

        assert model_value == value
Exemplo n.º 20
0
def test_integration_multiple_fmus_pyfmi(tmp_path):
    slave1_code = """import math
from pythonfmu.fmi2slave import Fmi2Slave, Fmi2Causality, Integer, Real, Boolean, String


class Slave1(Fmi2Slave):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.realIn = 22.0
        self.realOut = 0.0
        self.register_variable(Real("realIn", causality=Fmi2Causality.input))
        self.register_variable(Real("realOut", causality=Fmi2Causality.output))

    def do_step(self, current_time, step_size):
        self.log("Do step on Slave1.")
        self.realOut = self.realIn * 5.0 * (1.0 - math.exp(-1.0 * (current_time + step_size) / 0.1))
        return True
"""

    slave2_code = """from pythonfmu.fmi2slave import Fmi2Slave, Fmi2Causality, Integer, Real, Boolean, String


class Slave2(Fmi2Slave):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.realIn = 22.0
        self.realOut = 0.0
        self.register_variable(Real("realIn", causality=Fmi2Causality.input))
        self.register_variable(Real("realOut", causality=Fmi2Causality.output))

    def do_step(self, current_time, step_size):
        self.log("Do step on Slave2.")
        self.realOut = -2.0 * self.realIn
        return True
"""

    script1_file = tmp_path / "orig" / "slave1.py"
    script1_file.parent.mkdir(parents=True, exist_ok=True)
    script1_file.write_text(slave1_code)

    fmu1 = FmuBuilder.build_FMU(
        script1_file,
        dest=tmp_path,
        needsExecutionTool="false"
    )
    assert fmu1.exists()

    script2_file = tmp_path / "orig" / "slave2.py"
    script2_file.write_text(slave2_code)

    fmu2 = FmuBuilder.build_FMU(
        script2_file,
        dest=tmp_path,
        needsExecutionTool="false"
    )
    assert fmu2.exists()

    model1 = pyfmi.load_fmu(str(fmu1), log_level=7)
    model2 = pyfmi.load_fmu(str(fmu2), log_level=7)

    connections = [(model1, "realOut", model2, "realIn")]
    sim = pyfmi.Master([model1, model2], connections)

    res = sim.simulate(final_time=0.1, options={'step_size': 0.025})

    res1 = res[model1]
    assert res1["realOut"][-1] == pytest.approx(
        22.0 * 5.0 * (1.0 - math.exp(-1.0 * res1["time"][-1] / 0.1)), rel=1e-7
    )
    res2 = res[model2]
    assert res2["realIn"][-1] == pytest.approx(res1["realOut"][-1])
    # pyfmi master algorithm seems all fmus at once with the output from the previous time step
    assert res2["realOut"][-1] == pytest.approx(-2.0 * res2["realIn"][-2])
Exemplo n.º 21
0
def test_log_categories(tmp_path, debug_logging, categories):
    name = "PythonSlaveDebugCategories" if debug_logging else "PythonSlaveCategories"
    message = "log message"

    log_calls = [
        (f"{status.name.upper()} - {debug} - {message}", status, debug)
        for debug, status in itertools.product([True, False], Fmi2Status)
    ]

    fmu_calls = "\n".join([
        '        self.log("{}", Fmi2Status.{}, None, {})'.format(
            c[0], c[1].name, c[2]) for c in log_calls
    ])

    slave_code = f"""from pythonfmu.fmi2slave import Fmi2Slave, Fmi2Status, Fmi2Causality, Integer, Real, Boolean, String


class {name}(Fmi2Slave):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.realIn = 22.0
        self.realOut = 0.0
        self.register_variable(Real("realIn", causality=Fmi2Causality.input))
        self.register_variable(Real("realOut", causality=Fmi2Causality.output))


    def do_step(self, current_time, step_size):
{fmu_calls}
        return True
"""

    script_file = tmp_path / "orig" / f"{name.lower()}.py"
    script_file.parent.mkdir(parents=True, exist_ok=True)
    script_file.write_text(slave_code)

    fmu = FmuBuilder.build_FMU(script_file, dest=tmp_path)
    assert fmu.exists()

    logger = MagicMock()

    # Load the model
    callbacks = fmpy.fmi2.fmi2CallbackFunctions()
    callbacks.logger = fmpy.fmi2.fmi2CallbackLoggerTYPE(logger)
    callbacks.allocateMemory = fmpy.fmi2.fmi2CallbackAllocateMemoryTYPE(
        fmpy.fmi2.allocateMemory)
    callbacks.freeMemory = fmpy.fmi2.fmi2CallbackFreeMemoryTYPE(
        fmpy.fmi2.freeMemory)

    model_description = fmpy.read_model_description(fmu)
    unzip_dir = fmpy.extract(fmu)

    model = fmpy.fmi2.FMU2Slave(
        guid=model_description.guid,
        unzipDirectory=unzip_dir,
        modelIdentifier=model_description.coSimulation.modelIdentifier,
        instanceName='instance1')
    # Instantiate the model
    model.instantiate(callbacks=callbacks)
    model.setDebugLogging(debug_logging, categories)
    model.setupExperiment()
    model.enterInitializationMode()
    model.exitInitializationMode()
    # Execute the model
    model.doStep(0., 0.1)
    # Clean the model
    model.terminate()

    expected_calls = []
    for c in filter(lambda c: debug_logging or not c[2], log_calls):
        category = f"logStatus{c[1].name.capitalize()}"
        if category not in Fmi2Slave.log_categories:
            category = "logAll"
        if len(categories) == 0 or category in categories:
            expected_calls.append(
                call(
                    logger.call_args[0][0],  # Don't test the first argument
                    b'instance1',
                    int(c[1]),
                    bytes(category, encoding="utf-8"),
                    bytes(c[0], encoding="utf-8")))

    n_calls = len(Fmi2Status) if len(categories) == 0 else len(categories)

    assert logger.call_count == n_calls * (1 + int(debug_logging))
    logger.assert_has_calls(expected_calls)
Exemplo n.º 22
0
def test_logger(tmp_path, debug_logging):
    name = "PythonSlaveWithDebugLogger" if debug_logging else "PythonSlaveWithLogger"
    category = "category"
    message = "log message"

    log_calls = [
        (f"{status.name.upper()} - {debug} - {message}", status, category,
         debug)
        for debug, status in itertools.product([True, False], Fmi2Status)
    ]

    fmu_calls = "\n".join([
        '        self.log("{}", Fmi2Status.{}, "{}", {})'.format(
            c[0], c[1].name, c[2], c[3]) for c in log_calls
    ])

    slave_code = f"""from pythonfmu.fmi2slave import Fmi2Slave, Fmi2Status, Fmi2Causality, Integer, Real, Boolean, String


class {name}(Fmi2Slave):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.realIn = 22.0
        self.realOut = 0.0
        self.register_variable(Real("realIn", causality=Fmi2Causality.input))
        self.register_variable(Real("realOut", causality=Fmi2Causality.output))


    def do_step(self, current_time, step_size):
{fmu_calls}
        return True
"""

    script_file = tmp_path / "orig" / f"{name.lower()}.py"
    script_file.parent.mkdir(parents=True, exist_ok=True)
    script_file.write_text(slave_code)

    fmu = FmuBuilder.build_FMU(script_file, dest=tmp_path)
    assert fmu.exists()

    logger = MagicMock()

    fmpy.simulate_fmu(str(fmu),
                      stop_time=1e-3,
                      output_interval=1e-3,
                      logger=logger,
                      debug_logging=debug_logging)

    expected_calls = [
        call(
            logger.call_args[0][0],  # Don't test the first argument
            bytes(name, encoding="utf-8"),
            int(c[1]),
            bytes(c[2], encoding="utf-8"),
            bytes(c[0], encoding="utf-8"))
        for c in filter(lambda c: debug_logging or not c[3], log_calls)
    ]

    assert logger.call_count == len(Fmi2Status) * (1 + int(debug_logging))
    logger.assert_has_calls(expected_calls)