示例#1
0
def atest_identicalNamesSameTypes_throws():

    with pytest.raises(Exception):
        with ExampleArchive('Adder') as a, ExampleArchive('Adder') as b:

            md_a = read_model_description(str(b.root))
            md_b = read_model_description(str(a.root))

            instance_a = 'a'
            instance_b = 'a'

            fmu_a = FMU2Slave(
                guid=md_a.guid,
                unzipDirectory=a.root,
                modelIdentifier=md_a.coSimulation.modelIdentifier,
                instanceName=instance_a,
                fmiCallLogger=None)

            fmu_b = FMU2Slave(
                guid=md_b.guid,
                unzipDirectory=b.root,
                modelIdentifier=md_b.coSimulation.modelIdentifier,
                instanceName=instance_b,
                fmiCallLogger=None)

            fmu_a.instantiate()
            fmu_b.instantiate()
示例#2
0
def test_generate_model_description_generates_valid_model_description(tmp_path):
    filename = str(tmp_path / "modelDescription.xml")
    model_description = generate_model_description(
        model_name="Test Model",
        model_identifier="test-model",
        guid=str(uuid4()),
        inputs=["x", "y"],
        outputs=["z"],
    )
    try:
        model_description.write(filename, encoding="utf-8", xml_declaration=True)
        read_model_description(filename, validate_model_structure=True)
    except Exception as e:
        pytest.fail(str(e))
示例#3
0
def test_multipleInstantiationsAllDifferentInstanceNames_canSimulate():

    for idx, pname in enumerate(get_correct_examples()):

        with ExampleArchive(pname) as archive:

            print(archive.model_description)
            # seems like fmpy does not accept Path objects
            path = str(archive.root)

            md = read_model_description(str(archive.root))

            instance_a = str(idx) + 'a'
            instance_b = str(idx) + 'b'

            fmu_a = FMU2Slave(guid=md.guid,
                              unzipDirectory=archive.root,
                              modelIdentifier=md.coSimulation.modelIdentifier,
                              instanceName=instance_a,
                              fmiCallLogger=None)

            fmu_b = FMU2Slave(guid=md.guid,
                              unzipDirectory=archive.root,
                              modelIdentifier=md.coSimulation.modelIdentifier,
                              instanceName=instance_b,
                              fmiCallLogger=None)

            fmu_a.instantiate()
            fmu_b.instantiate()
示例#4
0
文件: model.py 项目: UdSAES/modest-py
    def __init__(self, fmu_path, opts=None):
        self.logger = logging.getLogger(type(self).__name__)

        try:
            self.logger.debug("Loading FMU")
            self.fmu_path = fmu_path
            self.model_description = read_model_description(self.fmu_path)

            self.fmu_args = {
                'guid': self.model_description.guid,
                'modelIdentifier':
                self.model_description.coSimulation.modelIdentifier,
                'instanceName': None,
                'fmiCallLogger': None
            }

        except Exception as e:
            self.logger.error(e)

        self.opts = opts
        self.start = None
        self.end = None
        self.timeline = None

        self.input_names = list()
        self.input_values = list()
        self.output_names = list()
        self.parameter_names = list()
        self.input = None
        self.parameter_df = pd.DataFrame()
        self.res = None
示例#5
0
    def add_fmu(self, fmu_name, fmu_loc, step_size, inputs = [], outputs=[], exist=False, solver_name = 'Cvode', variable=False, **kwargs):
        if 'validate' in kwargs.keys():
            validate=kwargs['validate']
        else:
            validate=True
        m_desc = read_model_description(fmu_loc, validate=validate)
        fmi_type = 'CoSimulation' if m_desc.coSimulation is not None else 'ModelExchange'
        if fmi_type == 'CoSimulation':
            fmu_temp = FmuCsAdapter(fmu_loc,
                             instanceName=fmu_name,
                             step_size = step_size,
                             inputs = inputs,
                             outputs=outputs,
                             exist=exist,
                             validate=validate)
        elif fmi_type == 'ModelExchange':
            fmu_temp = FmuMeAdapter(fmu_loc,
                             instanceName=fmu_name,
                             step_size = step_size,
                             inputs = inputs,
                             outputs=outputs,
                             solver_name = solver_name,
                             validate=validate)

        if fmu_name not in self.simulator_dict.keys():
            self.simulator_dict[fmu_name] = ['fmu', fmu_temp, step_size, outputs, variable]
            if self.logging:
                print("Added FMU simulator '%s' to the world" %(fmu_name))
        else:
            print(f"Please specify a unique name for simulator. {fmu_name} already exists.")
示例#6
0
def simulate_fmu(filename,
                 validate=True,
                 start_time=None,
                 stop_time=None,
                 step_size=None,
                 sample_interval=None,
                 fmi_type=None,
                 start_values={},
                 input=None,
                 output=None,
                 timeout=None,
                 fmi_logging=False):

    modelDescription = read_model_description(filename, validate=validate)

    if fmi_type is None:
        # determine the FMI type automatically
        fmi_type = 'CoSimulation' if modelDescription.coSimulation is not None else 'ModelExchange'

    defaultExperiment = modelDescription.defaultExperiment

    if start_time is None:
        if defaultExperiment is not None and defaultExperiment.startTime is not None:
            start_time = defaultExperiment.startTime
        else:
            start_time = 0.0

    if stop_time is None:
        if defaultExperiment is not None:
            stop_time = defaultExperiment.stopTime
        else:
            stop_time = 1.0

    if step_size is None:
        total_time = stop_time - start_time
        step_size = 10**(np.round(np.log10(total_time)) - 3)

    unzipdir = extract(filename)

    if fmi_type == 'ModelExchange':
        simfun = simulateME1 if modelDescription.fmiVersion == '1.0' else simulateME2
    elif fmi_type == 'CoSimulation':
        simfun = simulateCS
    else:
        raise Exception(
            'fmi_tpye must be either "ModelExchange" or "CoSimulation"')

    if sample_interval is None:
        sample_interval = (stop_time - start_time) / 500

    # simulate_fmu the FMU
    result = simfun(modelDescription, unzipdir, start_time, stop_time,
                    step_size, start_values, input, output, sample_interval,
                    timeout, fmi_logging)

    # clean up
    shutil.rmtree(unzipdir)

    return result
示例#7
0
def test_bicycle():
    with ExampleArchive('BicycleKinematic') as a:

        md_a = read_model_description(str(a.root))

        fmu_a = FMU2Slave(guid=md_a.guid,
                          unzipDirectory=a.root,
                          modelIdentifier=md_a.coSimulation.modelIdentifier,
                          instanceName='a',
                          fmiCallLogger=None)

        fmu_a.instantiate()
        fmu_a.doStep(0, 1)
示例#8
0
    def setUpClass(cls):
        # store various useful FMU arguments from the model description
        cls._model_description = read_model_description(fmu_path)
        cls._fmi_types = []
        if cls._model_description.coSimulation is not None:
            cls._fmi_types.append('CoSimulation')
        if cls._model_description.modelExchange is not None:
            cls._fmi_types.append('ModelExchange')

        if not cls._fmi_types:
            raise Exception('fmi_type must contain at least "ModelExchange" or "CoSimulation"')

        cls._experiment = cls._model_description.defaultExperiment

        if cls._experiment is not None and cls._experiment.startTime is not None:
            cls._start_time = cls._experiment.startTime
        else:
            cls._start_time = 0.0

        start_time = float(cls._start_time)

        if cls._experiment is not None and cls._experiment.stopTime is not None:
            cls._stop_time = cls._experiment.stopTime
        else:
            cls._stop_time = start_time + 1.0

        stop_time = float(cls._stop_time)

        if cls._experiment is not None:
            cls._relative_tolerance = cls._experiment.tolerance

        total_time = stop_time - start_time
        cls._step_size = 10 ** (np.round(np.log10(total_time)) - 3)

        if 'CoSimulation' in cls._fmi_types and cls._experiment is not None and cls._experiment.stepSize is not None:
            cls._output_interval = cls._experiment.stepSize
            while (stop_time - start_time) / cls._output_interval > 1000:
                cls._output_interval *= 2

        if path.isfile(path.join(fmu_path, 'modelDescription.xml')):
            cls._unzipdir = fmu_path
            cls._tempdir = None
        else:
            cls._tempdir = extract(fmu_path)
            cls._unzipdir = cls._tempdir
示例#9
0
def test_Adder():
    with ExampleArchive('Adder') as a:

        md_a = read_model_description(str(a.root))

        instance_a = 'a'

        fmu_a = FMU2Slave(guid=md_a.guid,
                          unzipDirectory=a.root,
                          modelIdentifier=md_a.coSimulation.modelIdentifier,
                          instanceName=instance_a,
                          fmiCallLogger=None)

        fmu_a.instantiate()
        # set input a
        fmu_a.setReal([1], [1])
        # set input b
        fmu_a.setReal([2], [2])

        fmu_a.doStep(0, 1)

        # read output
        s = fmu_a.getReal([0])[0]
        assert s == 3
示例#10
0
def simulate_fmu(filename,
                 validate=True,
                 start_time=None,
                 stop_time=None,
                 solver='CVode',
                 step_size=None,
                 relative_tolerance=None,
                 output_interval=None,
                 record_events=True,
                 fmi_type=None,
                 use_source_code=False,
                 start_values={},
                 apply_default_start_values=False,
                 input=None,
                 output=None,
                 timeout=None,
                 debug_logging=False,
                 logger=None,
                 fmi_call_logger=None,
                 step_finished=None,
                 model_description=None):
    """ Simulate an FMU

    Parameters:
        filename            filename of the FMU or directory with extracted FMU
        validate            validate the FMU
        start_time          simulation start time (None: use default experiment or 0 if not defined)
        stop_time           simulation stop time (None: use default experiment or start_time + 1 if not defined)
        solver              solver to use for model exchange ('Euler' or 'CVode')
        step_size           step size for the 'Euler' solver
        relative_tolerance  relative tolerance for the 'CVode' solver
        output_interval     interval for sampling the output
        record_events       record outputs at events (model exchange only)
        fmi_type            FMI type for the simulation (None: determine from FMU)
        use_source_code     compile the shared library (requires C sources)
        start_values        dictionary of variable name -> value pairs
        apply_default_start_values  apply the start values from the model description
        input               a structured numpy array that contains the input (see :class:`Input`)
        output              list of variables to record (None: record outputs)
        timeout             timeout for the simulation
        debug_logging       enable the FMU's debug logging
        fmi_call_logger     callback function to log FMI calls
        logger              callback function passed to the FMU (experimental)
        step_finished       callback to interact with the simulation (experimental)
        model_description   the previously loaded model description (experimental)

    Returns:
        result              a structured numpy array that contains the result
    """

    from fmpy import supported_platforms
    from fmpy.model_description import read_model_description

    if not use_source_code and platform not in supported_platforms(filename):
        raise Exception(
            "The current platform (%s) is not supported by the FMU." %
            platform)

    if model_description is None:
        model_description = read_model_description(filename, validate=validate)
    else:
        model_description = model_description

    if fmi_type is None:
        # determine the FMI type automatically
        fmi_type = 'CoSimulation' if model_description.coSimulation is not None else 'ModelExchange'

    if fmi_type not in ['ModelExchange', 'CoSimulation']:
        raise Exception(
            'fmi_type must be one of "ModelExchange" or "CoSimulation"')

    experiment = model_description.defaultExperiment

    if start_time is None:
        if experiment is not None and experiment.startTime is not None:
            start_time = experiment.startTime
        else:
            start_time = 0.0

    if stop_time is None:
        if experiment is not None and experiment.stopTime is not None:
            stop_time = experiment.stopTime
        else:
            stop_time = start_time + 1.0

    if relative_tolerance is None:
        if experiment is not None and experiment.tolerance is not None:
            relative_tolerance = experiment.tolerance
        else:
            relative_tolerance = 1e-5

    if step_size is None:
        total_time = stop_time - start_time
        step_size = 10**(np.round(np.log10(total_time)) - 3)

    if os.path.isfile(os.path.join(filename, 'modelDescription.xml')):
        unzipdir = filename
        tempdir = None
    else:
        tempdir = extract(filename)
        unzipdir = tempdir

    # common FMU constructor arguments
    fmu_args = {
        'guid': model_description.guid,
        'unzipDirectory': unzipdir,
        'instanceName': None,
        'fmiCallLogger': fmi_call_logger
    }

    if use_source_code:

        from .util import compile_dll

        # compile the shared library from the C sources
        fmu_args['libraryPath'] = compile_dll(
            model_description=model_description,
            sources_dir=os.path.join(unzipdir, 'sources'))

    if logger is None:
        logger = printLogMessage

    if model_description.fmiVersion == '1.0':
        callbacks = fmi1CallbackFunctions()
        callbacks.logger = fmi1CallbackLoggerTYPE(logger)
        callbacks.allocateMemory = fmi1CallbackAllocateMemoryTYPE(
            allocateMemory)
        callbacks.freeMemory = fmi1CallbackFreeMemoryTYPE(freeMemory)
        callbacks.stepFinished = None
    else:
        callbacks = fmi2CallbackFunctions()
        callbacks.logger = fmi2CallbackLoggerTYPE(logger)
        callbacks.allocateMemory = fmi2CallbackAllocateMemoryTYPE(
            allocateMemory)
        callbacks.freeMemory = fmi2CallbackFreeMemoryTYPE(freeMemory)

    # simulate_fmu the FMU
    if fmi_type == 'ModelExchange' and model_description.modelExchange is not None:
        fmu_args[
            'modelIdentifier'] = model_description.modelExchange.modelIdentifier
        result = simulateME(model_description, fmu_args, start_time, stop_time,
                            solver, step_size, relative_tolerance,
                            start_values, apply_default_start_values, input,
                            output, output_interval, record_events, timeout,
                            callbacks, debug_logging, step_finished)
    elif fmi_type == 'CoSimulation' and model_description.coSimulation is not None:
        fmu_args[
            'modelIdentifier'] = model_description.coSimulation.modelIdentifier
        result = simulateCS(model_description, fmu_args, start_time, stop_time,
                            start_values, apply_default_start_values, input,
                            output, output_interval, timeout, callbacks,
                            debug_logging, step_finished)
    else:
        raise Exception('FMI type "%s" is not supported by the FMU' % fmi_type)

    # clean up
    if tempdir is not None:
        shutil.rmtree(tempdir)

    return result
示例#11
0
def simulate_fmu(filename,
                 validate=True,
                 start_time=None,
                 stop_time=None,
                 solver='CVode',
                 step_size=None,
                 relative_tolerance=None,
                 output_interval=None,
                 record_events=True,
                 fmi_type=None,
                 start_values={},
                 apply_default_start_values=False,
                 input=None,
                 output=None,
                 timeout=None,
                 debug_logging=False,
                 visible=False,
                 logger=None,
                 fmi_call_logger=None,
                 step_finished=None,
                 model_description=None,
                 fmu_instance=None):
    """ Simulate an FMU

    Parameters:
        filename            filename of the FMU or directory with extracted FMU
        validate            validate the FMU
        start_time          simulation start time (None: use default experiment or 0 if not defined)
        stop_time           simulation stop time (None: use default experiment or start_time + 1 if not defined)
        solver              solver to use for model exchange ('Euler' or 'CVode')
        step_size           step size for the 'Euler' solver
        relative_tolerance  relative tolerance for the 'CVode' solver and FMI 2.0 co-simulation FMUs
        output_interval     interval for sampling the output
        record_events       record outputs at events (model exchange only)
        fmi_type            FMI type for the simulation (None: determine from FMU)
        start_values        dictionary of variable name -> value pairs
        apply_default_start_values  apply the start values from the model description
        input               a structured numpy array that contains the input (see :class:`Input`)
        output              list of variables to record (None: record outputs)
        timeout             timeout for the simulation
        debug_logging       enable the FMU's debug logging
        visible             interactive mode (True) or batch mode (False)
        fmi_call_logger     callback function to log FMI calls
        logger              callback function passed to the FMU (experimental)
        step_finished       callback to interact with the simulation (experimental)
        model_description   the previously loaded model description (experimental)
        fmu_instance        the previously instantiated FMU (experimental)

    Returns:
        result              a structured numpy array that contains the result
    """

    from fmpy import supported_platforms
    from fmpy.model_description import read_model_description

    platforms = supported_platforms(filename)

    # use 32-bit DLL remoting
    use_remoting = platform == 'win64' and 'win64' not in platforms and 'win32' in platforms

    if fmu_instance is None and platform not in platforms and not use_remoting:
        raise Exception("The current platform (%s) is not supported by the FMU." % platform)

    if model_description is None:
        model_description = read_model_description(filename, validate=validate)
    else:
        model_description = model_description

    if fmi_type is None:
        if fmu_instance is not None:
            # determine FMI type from the FMU instance
            fmi_type = 'CoSimulation' if type(fmu_instance) in [FMU1Slave, FMU2Slave, fmi3.FMU3Slave] else 'ModelExchange'
        else:
            # determine the FMI type automatically
            fmi_type = 'CoSimulation' if model_description.coSimulation is not None else 'ModelExchange'

    if fmi_type not in ['ModelExchange', 'CoSimulation']:
        raise Exception('fmi_type must be one of "ModelExchange" or "CoSimulation"')

    experiment = model_description.defaultExperiment

    if start_time is None:
        if experiment is not None and experiment.startTime is not None:
            start_time = experiment.startTime
        else:
            start_time = 0.0

    if stop_time is None:
        if experiment is not None and experiment.stopTime is not None:
            stop_time = experiment.stopTime
        else:
            stop_time = start_time + 1.0

    if relative_tolerance is None and experiment is not None:
        relative_tolerance = experiment.tolerance

    if step_size is None:
        total_time = stop_time - start_time
        step_size = 10 ** (np.round(np.log10(total_time)) - 3)

    if output_interval is None and fmi_type == 'CoSimulation' and experiment is not None and experiment.stepSize is not None:
        output_interval = experiment.stepSize
        while (stop_time - start_time) / output_interval > 1000:
            output_interval *= 2

    if os.path.isfile(os.path.join(filename, 'modelDescription.xml')):
        unzipdir = filename
        tempdir = None
    else:
        tempdir = extract(filename)
        unzipdir = tempdir

    if use_remoting:
        # start 32-bit server
        from subprocess import Popen
        server_path = os.path.dirname(__file__)
        server_path = os.path.join(server_path, 'remoting', 'server.exe')
        if fmi_type == 'ModelExchange':
            model_identifier = model_description.modelExchange.modelIdentifier
        else:
            model_identifier = model_description.coSimulation.modelIdentifier
        dll_path = os.path.join(unzipdir, 'binaries', 'win32', model_identifier + '.dll')
        server = Popen([server_path, dll_path])
    else:
        server = None

    if fmu_instance is None:
        fmu = instantiate_fmu(unzipdir, model_description, fmi_type, visible, debug_logging, logger, fmi_call_logger, use_remoting)
    else:
        fmu = fmu_instance

    # simulate_fmu the FMU
    if fmi_type == 'ModelExchange':
        result = simulateME(model_description, fmu, start_time, stop_time, solver, step_size, relative_tolerance, start_values, apply_default_start_values, input, output, output_interval, record_events, timeout, step_finished)
    elif fmi_type == 'CoSimulation':
        result = simulateCS(model_description, fmu, start_time, stop_time, relative_tolerance, start_values, apply_default_start_values, input, output, output_interval, timeout, step_finished)

    if fmu_instance is None:
        fmu.freeInstance()

    if server is not None:
        server.kill()

    # clean up
    if tempdir is not None:
        shutil.rmtree(tempdir, ignore_errors=True)

    return result
示例#12
0
def simulate_fmu(filename,
                 validate=True,
                 start_time=None,
                 stop_time=None,
                 solver='CVode',
                 step_size=None,
                 output_interval=None,
                 fmi_type=None,
                 start_values={},
                 input=None,
                 output=None,
                 timeout=None,
                 fmi_logging=False):

    modelDescription = read_model_description(filename, validate=validate)

    if fmi_type is None:
        # determine the FMI type automatically
        fmi_type = 'CoSimulation' if modelDescription.coSimulation is not None else 'ModelExchange'

    if fmi_type not in ['ModelExchange', 'CoSimulation']:
        raise Exception(
            'fmi_tpye must be one of "ModelExchange" or "CoSimulation"')

    defaultExperiment = modelDescription.defaultExperiment

    if start_time is None:
        if defaultExperiment is not None and defaultExperiment.startTime is not None:
            start_time = defaultExperiment.startTime
        else:
            start_time = 0.0

    if stop_time is None:
        if defaultExperiment is not None:
            stop_time = defaultExperiment.stopTime
        else:
            stop_time = 1.0

    if step_size is None:
        total_time = stop_time - start_time
        step_size = 10**(np.round(np.log10(total_time)) - 3)

    unzipdir = extract(filename)

    if output_interval is None:
        output_interval = (stop_time - start_time) / 500

    # common FMU constructor arguments
    fmu_args = {
        'guid': modelDescription.guid,
        'unzipDirectory': unzipdir,
        'instanceName': None,
        'logFMICalls': fmi_logging
    }

    # simulate_fmu the FMU
    if fmi_type == 'ModelExchange':
        fmu_args[
            'modelIdentifier'] = modelDescription.modelExchange.modelIdentifier
        result = simulateME(modelDescription, fmu_args, start_time, stop_time,
                            solver, step_size, start_values, input, output,
                            output_interval, timeout, fmi_logging)
    elif fmi_type == 'CoSimulation':
        fmu_args[
            'modelIdentifier'] = modelDescription.coSimulation.modelIdentifier
        result = simulateCS(modelDescription, fmu_args, start_time, stop_time,
                            start_values, input, output, output_interval,
                            timeout, fmi_logging)

    # clean up
    shutil.rmtree(unzipdir)

    return result
示例#13
0
def simulate_fmu(filename,
                 validate: bool = True,
                 start_time: Union[float, str] = None,
                 stop_time: Union[float, str] = None,
                 solver: str = 'CVode',
                 step_size: Union[float, str] = None,
                 relative_tolerance: Union[float, str] = None,
                 output_interval: Union[float, str] = None,
                 record_events: bool = True,
                 fmi_type: str = None,
                 start_values: Dict[str, Any] = {},
                 apply_default_start_values: bool = False,
                 input: np.ndarray = None,
                 output: Sequence[str] = None,
                 timeout: Union[float, str] = None,
                 debug_logging: bool = False,
                 visible: bool = False,
                 logger: Callable = None,
                 fmi_call_logger: Callable[[str], None] = None,
                 step_finished: Callable[[float, Recorder], bool] = None,
                 model_description: ModelDescription = None,
                 fmu_instance: _FMU = None,
                 set_input_derivatives: bool = False,
                 remote_platform: str = 'auto',
                 early_return_allowed: bool = False,
                 use_event_mode: bool = False,
                 initialize: bool = True,
                 terminate: bool = True,
                 fmu_state: Union[bytes, c_void_p] = None) -> SimulationResult:
    """ Simulate an FMU

    Parameters:
        filename               filename of the FMU or directory with extracted FMU
        validate               validate the FMU and start values
        start_time             simulation start time (None: use default experiment or 0 if not defined)
        stop_time              simulation stop time (None: use default experiment or start_time + 1 if not defined)
        solver                 solver to use for model exchange ('Euler' or 'CVode')
        step_size              step size for the 'Euler' solver
        relative_tolerance     relative tolerance for the 'CVode' solver and FMI 2.0 co-simulation FMUs
        output_interval        interval for sampling the output
        record_events          record outputs at events (model exchange only)
        fmi_type               FMI type for the simulation (None: determine from FMU)
        start_values           dictionary of variable name -> value pairs
        apply_default_start_values  apply the start values from the model description
        input                  a structured numpy array that contains the input (see :class:`Input`)
        output                 list of variables to record (None: record outputs)
        timeout                timeout for the simulation
        debug_logging          enable the FMU's debug logging
        visible                interactive mode (True) or batch mode (False)
        fmi_call_logger        callback function to log FMI calls
        logger                 callback function passed to the FMU (experimental)
        step_finished          callback to interact with the simulation (experimental)
        model_description      the previously loaded model description (experimental)
        fmu_instance           the previously instantiated FMU (experimental)
        set_input_derivatives  set the input derivatives (FMI 2.0 Co-Simulation only)
        remote_platform        platform to use for remoting server ('auto': determine automatically if current platform
                               is not supported, None: no remoting; experimental)
        early_return_allowed   allow early return in FMI 3.0 Co-Simulation
        use_event_mode         use event mode in FMI 3.0 Co-Simulation if the FMU supports it
        initialize             initialize the FMU
        terminate              terminate the FMU
        fmu_state              the FMU state or serialized FMU state to initialize the FMU
    Returns:
        result                 a structured numpy array that contains the result
    """

    from fmpy import supported_platforms
    from fmpy.model_description import read_model_description
    from fmpy.util import can_simulate

    platforms = supported_platforms(filename)

    if fmu_instance is None and platform not in platforms and remote_platform is None:
        raise Exception(f"The current platform ({platform}) is not supported by the FMU.")

    can_sim, remote_platform = can_simulate(platforms, remote_platform)

    if not can_sim:
        raise Exception(f"The FMU cannot be simulated on the current platform ({platform}).")

    if model_description is None:
        model_description = read_model_description(filename, validate=validate)

    if fmi_type is None:
        if fmu_instance is not None:
            # determine FMI type from the FMU instance
            fmi_type = 'CoSimulation' if type(fmu_instance) in [FMU1Slave, FMU2Slave, fmi3.FMU3Slave] else 'ModelExchange'
        else:
            # determine the FMI type automatically
            fmi_type = 'CoSimulation' if model_description.coSimulation is not None else 'ModelExchange'

    if fmi_type not in ['ModelExchange', 'CoSimulation']:
        raise Exception('fmi_type must be one of "ModelExchange" or "CoSimulation"')

    if initialize is False:
        if fmi_type != 'CoSimulation':
            raise Exception("If initialize is False, the interface type must be 'CoSimulation'.")
        if fmu_instance is None and fmu_state is None:
            raise Exception("If initialize is False, fmu_instance or fmu_state must be provided.")

    experiment = model_description.defaultExperiment

    if start_time is None:
        if experiment is not None and experiment.startTime is not None:
            start_time = experiment.startTime
        else:
            start_time = 0.0

    start_time = float(start_time)

    if stop_time is None:
        if experiment is not None and experiment.stopTime is not None:
            stop_time = experiment.stopTime
        else:
            stop_time = start_time + 1.0

    stop_time = float(stop_time)

    if relative_tolerance is None and experiment is not None:
        relative_tolerance = experiment.tolerance

    if step_size is None:
        total_time = stop_time - start_time
        step_size = 10 ** (np.round(np.log10(total_time)) - 3)

    if output_interval is None and fmi_type == 'CoSimulation':

        co_simulation = model_description.coSimulation

        if co_simulation is not None and co_simulation.fixedInternalStepSize is not None:
            output_interval = float(model_description.coSimulation.fixedInternalStepSize)
        elif experiment is not None and experiment.stepSize is not None:
            output_interval = float(experiment.stepSize)

        if output_interval is not None:
            while (stop_time - start_time) / output_interval > 1000:
                output_interval *= 2

    if os.path.isfile(os.path.join(filename, 'modelDescription.xml')):
        unzipdir = filename
        tempdir = None
    else:
        required_paths = ['resources', 'binaries/']
        if remote_platform:
            required_paths.append(os.path.join('binaries', remote_platform))
        tempdir = extract(filename, include=None if remote_platform else lambda n: n.startswith(tuple(required_paths)))
        unzipdir = tempdir

    if remote_platform:
        add_remoting(unzipdir, host_platform=platform, remote_platform=remote_platform)

    if fmu_instance is None:
        fmu = instantiate_fmu(unzipdir, model_description, fmi_type, visible, debug_logging, logger, fmi_call_logger, None, early_return_allowed, use_event_mode)
    else:
        fmu = fmu_instance

    if fmu_state is not None:
        if model_description.fmiVersion == '2.0' or model_description.fmiVersion.startswith('3.0'):
            if isinstance(fmu_state, bytes):
                fmu_state = fmu.deserializeFMUState(fmu_state)
                fmu.setFMUState(fmu_state)
                fmu.freeFMUState(fmu_state)
            else:
                fmu.setFMUState(fmu_state)
        else:
            raise Exception(f"Setting the FMU state is not supported for FMI version {model_description.fmiVersion}.")
        initialize = False

    # simulate_fmu the FMU
    if fmi_type == 'ModelExchange':
        result = simulateME(model_description, fmu, start_time, stop_time, solver, step_size, relative_tolerance, start_values, apply_default_start_values, input, output, output_interval, record_events, timeout, step_finished, validate)
    elif fmi_type == 'CoSimulation':
        result = simulateCS(model_description, fmu, start_time, stop_time, relative_tolerance, start_values, apply_default_start_values, input, output, output_interval, timeout, step_finished, set_input_derivatives, use_event_mode, early_return_allowed, validate, initialize, terminate)

    if fmu_instance is None:
        fmu.freeInstance()

    # clean up
    if tempdir is not None:
        shutil.rmtree(tempdir, ignore_errors=True)

    return result