Ejemplo n.º 1
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
Ejemplo n.º 2
0
def main():

    import argparse
    import fmpy
    import sys
    import os

    description = f"""\
Validate and simulate Functional Mock-up Units (FMUs)

Get information about an FMU:
   
    fmpy info Rectifier.fmu
 
Simulate an FMU:
 
    fmpy simulate Rectifier.fmu --show-plot
    
Compile a source code FMU:

    fmpy compile Rectifier.fmu
    
Create a Jupyter Notebook

    fmpy create-jupyter-notebook Rectifier.fmu
    

About FMPy

FMPy version:       {fmpy.__version__}
FMI platform:       {fmpy.platform}
Installation path:  {os.path.dirname(__file__)}  
Python interpreter: {sys.executable}
Python version:     {sys.version}
"""

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=description)

    parser.add_argument('command',
                        choices=[
                            'info', 'validate', 'simulate', 'compile',
                            'add-cswrapper', 'add-remoting',
                            'create-cmake-project', 'create-jupyter-notebook'
                        ],
                        help="Command to execute")
    parser.add_argument('fmu_filename', help="filename of the FMU")

    parser.add_argument('--validate',
                        action='store_true',
                        help="validate the FMU")
    parser.add_argument('--start-time',
                        type=float,
                        help="start time for the simulation")
    parser.add_argument('--stop-time',
                        type=float,
                        help="stop time for the simulation")
    parser.add_argument('--solver',
                        choices=['Euler', 'CVode'],
                        default='CVode',
                        help="solver to use for Model Exchange")
    parser.add_argument('--step-size',
                        type=float,
                        help="step size for fixed-step solvers")
    parser.add_argument(
        '--relative-tolerance',
        type=float,
        help=
        "relative tolerance for the 'CVode' solver and FMI 2.0 co-simulation FMUs"
    )
    parser.add_argument(
        '--dont-record-events',
        action='store_true',
        help="dont't record outputs at events (model exchange only)")
    parser.add_argument('--start-values',
                        nargs='+',
                        help="name-value pairs of start values")
    parser.add_argument(
        '--apply-default-start-values',
        action='store_true',
        help="apply the start values from the model description")
    parser.add_argument('--output-interval',
                        type=float,
                        help="interval for sampling the output")
    parser.add_argument('--input-file', help="CSV file to use as input")
    parser.add_argument('--output-variables',
                        nargs='+',
                        help="Variables to record")
    parser.add_argument('--output-file', help="CSV to store the results")
    parser.add_argument('--timeout',
                        type=float,
                        help="max. time to wait for the simulation to finish")
    parser.add_argument('--debug-logging',
                        action='store_true',
                        help="enable the FMU's debug logging")
    parser.add_argument('--visible',
                        action='store_true',
                        help="enable interactive mode")
    parser.add_argument('--fmi-logging',
                        action='store_true',
                        help="enable FMI logging")
    parser.add_argument('--show-plot',
                        action='store_true',
                        help="plot the results")
    parser.add_argument('--cmake-project-dir',
                        help="Directory for the CMake project")
    parser.add_argument('--target-platform',
                        help="The target platform to compile the binary for")
    parser.add_argument('--compiler-options',
                        help="Options used when compiling the platform binary")

    args = parser.parse_args()

    if args.command == 'info':

        from fmpy import dump
        dump(args.fmu_filename)

    elif args.command == 'validate':

        import sys
        from fmpy.validation import validate_fmu

        problems = validate_fmu(args.fmu_filename)

        if len(problems) == 0:
            print('No problems found.')
        else:
            print('%d problems were found:' % len(problems))
            for message in problems:
                print()
                print(message)

        sys.exit(len(problems))

    elif args.command == 'compile':

        from fmpy.util import compile_platform_binary
        compile_platform_binary(filename=args.fmu_filename,
                                target_platform=args.target_platform,
                                compiler_options=args.compiler_options)

    elif args.command == 'add-cswrapper':

        from fmpy.cswrapper import add_cswrapper
        add_cswrapper(args.fmu_filename)

    elif args.command == 'add-remoting':

        from fmpy.util import add_remoting
        from fmpy import supported_platforms

        platforms = supported_platforms(args.fmu_filename)

        if 'win32' in platforms and 'win64' not in platforms:
            add_remoting(args.fmu_filename, 'win64', 'win32')
        elif 'win64' in platforms and 'linux64' not in platforms:
            add_remoting(args.fmu_filename, 'linux64', 'win64')
        else:
            print("Failed to add remoting binaries.")

    elif args.command == 'create-cmake-project':

        import os
        from fmpy.util import create_cmake_project

        project_dir = args.cmake_project_dir

        if project_dir is None:
            project_dir = os.path.basename(args.fmu_filename)
            project_dir, _ = os.path.splitext(project_dir)
            print("Creating CMake project in %s" %
                  os.path.abspath(project_dir))

        create_cmake_project(args.fmu_filename, project_dir)

    elif args.command == 'create-jupyter-notebook':

        from fmpy.util import create_jupyter_notebook

        create_jupyter_notebook(args.fmu_filename)

    elif args.command == 'simulate':

        from fmpy import simulate_fmu
        from fmpy.util import read_csv, write_csv, plot_result

        if args.start_values:
            if len(args.start_values) % 2 != 0:
                raise Exception("Start values must be name-value pairs.")
            start_values = {
                k: v
                for k, v in zip(args.start_values[::2],
                                args.start_values[1::2])
            }
        else:
            start_values = {}

        input = read_csv(args.input_file) if args.input_file else None

        if args.fmi_logging:
            fmi_call_logger = lambda s: print('[FMI] ' + s)
        else:
            fmi_call_logger = None

        result = simulate_fmu(
            args.fmu_filename,
            validate=args.validate,
            start_time=args.start_time,
            stop_time=args.stop_time,
            solver=args.solver,
            step_size=args.step_size,
            relative_tolerance=args.relative_tolerance,
            output_interval=args.output_interval,
            record_events=not args.dont_record_events,
            fmi_type=None,
            start_values=start_values,
            apply_default_start_values=args.apply_default_start_values,
            input=input,
            output=args.output_variables,
            timeout=args.timeout,
            debug_logging=args.debug_logging,
            visible=args.visible,
            fmi_call_logger=fmi_call_logger)

        if args.output_file:
            write_csv(filename=args.output_file, result=result)

        if args.show_plot:
            plot_result(result=result, window_title=args.fmu_filename)
Ejemplo n.º 3
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
Ejemplo n.º 4
0
    def load(self, filename):

        if not self.isVisible():
            self.show()

        try:
            self.modelDescription = md = read_model_description(filename)
        except Exception as e:
            QMessageBox.warning(self, "Failed to load FMU",
                                "Failed to load %s. %s" % (filename, e))
            return

        self.filename = filename
        platforms = supported_platforms(self.filename)

        self.variables.clear()
        self.selectedVariables.clear()
        self.startValues.clear()

        for v in md.modelVariables:
            self.variables[v.name] = v
            if v.causality == 'output':
                self.selectedVariables.add(v)

        fmi_types = []
        if md.coSimulation:
            fmi_types.append('Co-Simulation')
        if md.modelExchange:
            fmi_types.append('Model Exchange')

        # toolbar
        if md.defaultExperiment is not None:
            if md.defaultExperiment.stopTime is not None:
                self.stopTimeLineEdit.setText(
                    str(md.defaultExperiment.stopTime))
            if md.defaultExperiment.startTime is not None:
                self.startTimeLineEdit.setText(
                    str(md.defaultExperiment.startTime))
        # actions
        can_compile = md.fmiVersion == '2.0' and 'c-code' in platforms
        self.ui.actionCompilePlatformBinary.setEnabled(can_compile)
        self.ui.actionCreateCMakeProject.setEnabled(can_compile)

        # variables view
        self.treeModel.setModelDescription(md)
        self.tableModel.setModelDescription(md)
        self.treeFilterModel.invalidate()
        self.tableFilterModel.invalidate()
        self.ui.treeView.reset()
        self.ui.tableView.reset()

        # settings page
        self.ui.fmiVersionLabel.setText(md.fmiVersion)
        self.ui.fmiTypeLabel.setText(', '.join(fmi_types))
        self.ui.platformsLabel.setText(', '.join(platforms))
        self.ui.modelNameLabel.setText(md.modelName)
        self.ui.descriptionLabel.setText(md.description)
        self.ui.numberOfContinuousStatesLabel.setText(
            str(md.numberOfContinuousStates))
        self.ui.numberOfEventIndicatorsLabel.setText(
            str(md.numberOfEventIndicators))
        self.ui.numberOfVariablesLabel.setText(str(len(md.modelVariables)))
        self.ui.generationToolLabel.setText(md.generationTool)
        self.ui.generationDateAndTimeLabel.setText(md.generationDateAndTime)

        if md.defaultExperiment is not None and md.defaultExperiment.stepSize is not None:
            output_interval = float(md.defaultExperiment.stepSize)
            while output_interval > 1000:
                output_interval *= 0.5
        else:
            output_interval = float(self.stopTimeLineEdit.text()) / 500
        self.ui.outputIntervalLineEdit.setText(str(output_interval))

        self.fmiTypeComboBox.clear()
        self.fmiTypeComboBox.addItems(fmi_types)

        self.updateSimulationSettings()

        self.setCurrentPage(self.ui.settingsPage)

        self.ui.dockWidget.show()

        self.ui.actionSettings.setEnabled(True)
        self.ui.actionShowLog.setEnabled(True)
        self.ui.actionShowResults.setEnabled(False)

        can_simulate = platform in platforms

        self.ui.actionSimulate.setEnabled(can_simulate)
        self.startTimeLineEdit.setEnabled(can_simulate)
        self.stopTimeLineEdit.setEnabled(can_simulate)
        self.fmiTypeComboBox.setEnabled(can_simulate and len(fmi_types) > 1)
        self.ui.settingsGroupBox.setEnabled(can_simulate)

        settings = QSettings()
        recent_files = settings.value("recentFiles", defaultValue=[])
        recent_files = self.removeDuplicates([filename] + recent_files)

        # save the 10 most recent files
        settings.setValue('recentFiles', recent_files[:10])

        self.setWindowTitle("%s - FMPy" % os.path.normpath(filename))

        self.createGraphics()
Ejemplo n.º 5
0
def process_fmu(fmu_filename):

    basename, _ = os.path.splitext(fmu_filename)
    pickle_filename = basename + '.p'
    fmu_hash = os.path.basename(basename)

    try:
        model_description = read_model_description(fmu_filename, validate=False)
    except Exception as e:
        alert = dbc.Alert(
            [html.I(className='fas fa-times mr-3'), f"Failed to read model description. {e}"],
            id='alert', color='danger', className='mt-3')
        with open(pickle_filename, 'wb') as f:
            pickle.dump([alert], f)
        return

    platforms = supported_platforms(fmu_filename)

    with zipfile.ZipFile(fmu_filename, 'r') as zf:
        nl = filter(lambda n: not n.endswith('/'), zf.namelist())

    fmi_types = []

    if model_description.modelExchange:
        fmi_types.append('Model Exchange')

    if model_description.coSimulation:
        fmi_types.append('Co-Simulation')

    def na(attr):
        value = getattr(model_description, attr)
        if value:
            return value
        else:
            return html.Span('n/a', className='text-muted')

    rows = [
        dbc.Row([
            dbc.Col(html.Span("FMI Version"), width=4),
            dbc.Col(html.Span(model_description.fmiVersion), width=8),
        ], className='py-1'),
        dbc.Row([
            dbc.Col("FMI Type", width=4),
            dbc.Col(', '.join(fmi_types), width=8),
        ], className='py-1'),
        dbc.Row([
            dbc.Col("Model Name", width=4),
            dbc.Col(model_description.modelName, width=8),
        ], className='py-1'),
        dbc.Row([
            dbc.Col("Platforms", width=4),
            dbc.Col(', '.join(platforms), width=8),
        ], className='py-1'),
        dbc.Row([
            dbc.Col(html.Span("Continuous States"), width=4),
            dbc.Col(html.Span(model_description.numberOfContinuousStates), width=8),
        ], className='py-1'),
        dbc.Row([
            dbc.Col(html.Span("Event Indicators"), width=4),
            dbc.Col(html.Span(model_description.numberOfEventIndicators), width=8),
        ], className='py-1'),
        dbc.Row([
            dbc.Col(html.Span("Model Variables"), width=4),
            dbc.Col(html.Span(len(model_description.modelVariables)), width=8),
        ], className='py-1'),
        dbc.Row([
            dbc.Col(html.Span("Generation Date"), width=4),
            dbc.Col(na('generationDateAndTime'), width=8)
        ], className='py-1'),
        dbc.Row([
            dbc.Col(html.Span("Generation Tool"), width=4),
            dbc.Col(na('generationTool'), width=8)
        ], className='py-1'),
        dbc.Row([
            dbc.Col(html.Span("Description"), width=4),
            dbc.Col(na('description'), width=8)
        ], className='py-1'),
        dbc.Row([
            dbc.Col(html.Span("SHA256"), width=4),
            dbc.Col(html.Span(fmu_hash), width=8),
        ], className='py-1'),
        dbc.Row([
            dbc.Col(html.Span("File Size"), width=4),
            dbc.Col(html.Span(f'{os.path.getsize(fmu_filename)} bytes'), width=8),
        ], className='py-1'),
        # dbc.Row([
        #     dbc.Col(html.Span("Contained Files"), width=4),
        #     dbc.Col(html.Pre('\n'.join(nl)), width=8),
        # ], className='py-1'),
    ]

    try:
        problems = validate_fmu(fmu_filename)
    except Exception as e:
        problems = [str(e)]

    if problems:
        alert = dbc.Alert(
            [
                html.P(
                    [
                        html.I(className='fas fa-exclamation-circle mr-3'),
                        f"Validation failed. {len(problems)} {'problem was' if len(problems) == 1 else 'problems were'} found:"
                    ]
                ),
                html.Ul(
                    [html.Li(problem) for problem in problems]
                )
            ],
            id='alert', color='danger', className='mt-3')
    else:
        alert = dbc.Alert([html.I(className='fas fa-check mr-3'), "Validation passed. No problems found."],
                          id='alert', color='success', className='mt-3')

    variables = []

    table_header = [
        html.Thead(html.Tr([
            html.Th("Type"),
            html.Th("Name"),
            # html.Th("Variability"),
            html.Th("Causality"),
            html.Th("Start", className='text-right'),
            html.Th("Unit"),
            html.Th("Description")
        ]))
    ]

    for variable in model_description.modelVariables:

        unit = variable.unit

        if unit is None and variable.declaredType is not None:
            unit = variable.declaredType.unit

        if variable.type == 'Boolean':
            color = '#c900c9'
        elif variable.type == 'Binary':
            color = '#ab0000'
        elif variable.type.startswith(('Int', 'Enum')):
            color = '#c78f00'
        elif variable.type.startswith(('Real', 'Float')):
            color = '#0000bf'
        else:  # String
            color = '#00a608'

        variables.append(
            html.Tr(
                [
                    html.Td(html.Small(variable.type, style={
                        'color': color, 'border': '1px solid ' + color, 'border-radius': '1em', 'padding': '0 0.5em 0 0.5em',
                        # 'font-family': 'Georgia, "Times New Roman", Times, serif'
                    })),
                    html.Td(variable.name),
                    # html.Td(variable.variability),
                    html.Td(variable.causality),
                    html.Td(variable.start, className='text-right'),
                    html.Td(unit),
                    html.Td(variable.description, className='text-muted')
                ]
            )
        )

    table = dbc.Table(table_header + [html.Tbody(variables)], borderless=True, size='sm')

    tabs = dbc.Tabs(
        [
            dbc.Tab(rows, label="Model Info", className='p-4'),
            # dbc.Tab(variables, label="Variables", className='p-4'),
            dbc.Tab(table, label="Variables", className='p-4'),
            dbc.Tab(html.Pre('\n'.join(nl)), label="Files", className='p-4'),
        ],
        id='tabs'
    )

    with open(pickle_filename, 'wb') as f:
        pickle.dump([alert, tabs], f)
Ejemplo n.º 6
0
    def load(self, filename):

        import zipfile

        if not self.isVisible():
            self.show()

        try:
            self.modelDescription = md = read_model_description(filename)
        except Exception as e:
            QMessageBox.warning(self, "Failed to load FMU",
                                "Failed to load %s. %s" % (filename, e))
            return

        # show model.png
        try:
            pixmap = QPixmap()

            # load the model.png
            with zipfile.ZipFile(filename, 'r') as zf:
                pixmap.loadFromData(zf.read('model.png'), format='PNG')

            # show the unscaled version in tooltip
            buffer = QBuffer()
            buffer.open(QIODevice.WriteOnly)
            pixmap.save(buffer, "PNG", quality=100)
            image = bytes(buffer.data().toBase64()).decode()
            html = '<img src="data:image/png;base64,{}">'.format(image)
            self.ui.modelImageLabel.setToolTip(html)

            # show a scaled preview in "Model Info"
            pixmap = pixmap.scaled(200, 200, Qt.KeepAspectRatio,
                                   Qt.SmoothTransformation)
            self.ui.modelImageLabel.setPixmap(pixmap)
        except:
            self.ui.modelImageLabel.setPixmap(QPixmap())
            self.ui.modelImageLabel.setToolTip(None)

        self.filename = filename
        platforms = supported_platforms(self.filename)

        self.variables.clear()
        self.selectedVariables.clear()
        self.startValues.clear()

        for v in md.modelVariables:
            self.variables[v.name] = v
            if v.causality == 'output':
                self.selectedVariables.add(v)

        fmi_types = []
        if md.coSimulation:
            fmi_types.append('Co-Simulation')
        if md.modelExchange:
            fmi_types.append('Model Exchange')

        # toolbar
        if md.defaultExperiment is not None:
            if md.defaultExperiment.stopTime is not None:
                self.stopTimeLineEdit.setText(
                    str(md.defaultExperiment.stopTime))

        # actions
        can_compile = md.fmiVersion == '2.0' and 'c-code' in platforms
        self.ui.actionCompilePlatformBinary.setEnabled(can_compile)
        self.ui.actionCreateCMakeProject.setEnabled(can_compile)

        can_add_remoting = md.fmiVersion == '2.0' and 'win32' in platforms and 'win64' not in platforms
        self.ui.actionAddRemoting.setEnabled(can_add_remoting)

        # variables view
        self.treeModel.setModelDescription(md)
        self.tableModel.setModelDescription(md)
        self.treeFilterModel.invalidate()
        self.tableFilterModel.invalidate()
        self.ui.treeView.reset()
        self.ui.tableView.reset()

        # settings page
        self.ui.fmiVersionLabel.setText(md.fmiVersion)
        self.ui.fmiTypeLabel.setText(', '.join(fmi_types))
        self.ui.platformsLabel.setText(', '.join(platforms))
        self.ui.modelNameLabel.setText(md.modelName)
        self.ui.descriptionLabel.setText(md.description)
        self.ui.numberOfContinuousStatesLabel.setText(
            str(md.numberOfContinuousStates))
        self.ui.numberOfEventIndicatorsLabel.setText(
            str(md.numberOfEventIndicators))
        self.ui.numberOfVariablesLabel.setText(str(len(md.modelVariables)))
        self.ui.generationToolLabel.setText(md.generationTool)
        self.ui.generationDateAndTimeLabel.setText(md.generationDateAndTime)

        if md.defaultExperiment is not None and md.defaultExperiment.stepSize is not None:
            output_interval = float(md.defaultExperiment.stepSize)
            while output_interval > 1000:
                output_interval *= 0.5
        else:
            output_interval = float(self.stopTimeLineEdit.text()) / 500
        self.ui.outputIntervalLineEdit.setText(str(output_interval))

        self.fmiTypeComboBox.clear()
        self.fmiTypeComboBox.addItems(fmi_types)

        self.updateSimulationSettings()

        self.setCurrentPage(self.ui.settingsPage)

        self.ui.dockWidget.show()

        self.ui.actionReload.setEnabled(True)
        self.ui.actionSettings.setEnabled(True)
        self.ui.actionShowLog.setEnabled(True)
        self.ui.actionShowResults.setEnabled(False)

        can_simulate = platform in platforms or platform == 'win64' and 'win32' in platforms

        self.ui.actionLoadStartValues.setEnabled(can_simulate)
        self.ui.actionSimulate.setEnabled(can_simulate)
        self.stopTimeLineEdit.setEnabled(can_simulate)
        self.fmiTypeComboBox.setEnabled(can_simulate and len(fmi_types) > 1)
        self.ui.settingsGroupBox.setEnabled(can_simulate)

        settings = QSettings()
        recent_files = settings.value("recentFiles", defaultValue=[])
        recent_files = self.removeDuplicates([filename] + recent_files)

        # save the 10 most recent files
        settings.setValue('recentFiles', recent_files[:10])

        self.setWindowTitle("%s - FMPy" % os.path.normpath(filename))

        self.createGraphics()
Ejemplo n.º 7
0
        # check the input file
        input = None

        if input_variables:
            in_path = os.path.join(root, fmu_name + '_in.csv')
            input, in_csv_cell = check_csv_file(filename=in_path,
                                                variables=input_variables)
        else:
            in_csv_cell = '<td class="status"><span class="label label-default">n/a</span></td>'

        # check the reference file
        ref_path = os.path.join(root, fmu_name + '_ref.csv')
        reference, ref_csv_cell = check_csv_file(filename=ref_path,
                                                 variables=output_variables)

        supported_platforms = fmpy.supported_platforms(fmu_filename)

        # this will remove any trailing (back)slashes
        fmus_dir = os.path.normpath(args.fmus_dir)
        model_path = fmu_filename[len(fmus_dir) + 1:]
        model_path = os.path.dirname(model_path)
        fmu_simple_filename = os.path.basename(fmu_filename)
        model_name, _ = os.path.splitext(fmu_simple_filename)

        # build the filenames
        result = None

        ##############
        # SIMULATION #
        ##############
Ejemplo n.º 8
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