Exemplo n.º 1
0
    def test_fmi3(self):

        print('FMI 3.0')

        build_dir = os.path.join(test_fmus_dir, 'fmi3')

        if not os.path.exists(build_dir):
            os.makedirs(build_dir)

        subprocess.call(['cmake', '-G', generator, '-DFMI_VERSION=3', '..'],
                        cwd=build_dir)
        subprocess.call(['cmake', '--build', '.', '--config', 'Release'],
                        cwd=build_dir)

        # run examples
        examples = [
            'cs_early_return', 'cs_event_mode', 'cs_intermediate_update',
            'BouncingBall_cs', 'BouncingBall_me', 'import_shared_library',
            'import_static_library', 'jacobian', 'scs_synchronous', 'Stair_cs',
            'Stair_me'
        ]

        is_windows = os.name == 'nt'

        if is_windows:
            examples.append('scs_threaded')  # runs only on Windows

        for example in examples:
            print("Running %s example..." % example)
            filename = os.path.join(build_dir, 'temp', example)
            subprocess.check_call(filename,
                                  cwd=os.path.join(build_dir, 'temp'))

        models = [
            'BouncingBall', 'Dahlquist', 'Feedthrough', 'Resource', 'Stair',
            'VanDerPol'
        ]
        self.validate(build_dir, models=models)
        self.validate(build_dir, models=models, compile=True)

        copy_to_cross_check(build_dir=build_dir,
                            model_names=models,
                            fmi_version='3.0',
                            fmi_types=['cs', 'me'])
        copy_to_cross_check(build_dir=build_dir,
                            model_names=['Clocks'],
                            fmi_version='3.0',
                            fmi_types=['se'])

        for model in ['Clocks', 'LinearTransform']:
            problems = validate_fmu(
                filename=os.path.join(build_dir, 'dist', model + '.fmu'))
            self.assertEqual([], problems)
Exemplo n.º 2
0
def test_create_fmu_container_me(resources_dir):

    configuration = Configuration(
        parallelDoStep=False,
        variables=[
            Variable(type='Real',
                     variability='continuous',
                     causality='output',
                     initial='calculated',
                     name='h',
                     description='Height',
                     mapping=[('ball', 'h')]),
            Variable(type='Boolean',
                     variability='discrete',
                     causality='output',
                     initial='calculated',
                     name='reset',
                     description="Reset",
                     mapping=[('bounce', 'reset')]),
            Variable(type='Real',
                     variability='discrete',
                     causality='output',
                     initial='calculated',
                     name='ticks',
                     description='Ticks',
                     mapping=[('ticker', 'ticks')]),
        ],
        components=[
            Component(filename=resources_dir / 'Bounce.fmu',
                      interfaceType='ModelExchange',
                      name='bounce'),
            Component(filename=resources_dir / 'Ball.fmu',
                      interfaceType='ModelExchange',
                      name='ball'),
            Component(filename=resources_dir / 'Ticker.fmu',
                      interfaceType='ModelExchange',
                      name='ticker')
        ],
        connections=[
            Connection('ball', 'h', 'bounce', 'h'),
            Connection('bounce', 'reset', 'ball', 'reset'),
        ])

    filename = 'BouncingAndBall.fmu'

    create_fmu_container(configuration, filename)

    problems = validate_fmu(filename)

    assert not problems

    result = simulate_fmu(filename, stop_time=3.5, fmi_call_logger=None)
Exemplo n.º 3
0
def test_generate_fmu_generates_valid_fmu(tmp_path, csvfile):
    fmu = tmp_path / "model.fmu"
    dataframe = pandas.read_csv(csvfile)
    generate_fmu(
        dataframe=dataframe,  # type: ignore
        model_name="Test Model",
        inputs=["x", "y"],
        outputs=["z"],
        outfile=fmu,
        strategy="linear",
    )
    errors = validate_fmu(fmu)
    assert not errors
Exemplo n.º 4
0
    def validate(self,
                 build_dir,
                 fmi_types=['ModelExchange', 'CoSimulation'],
                 models=models,
                 compile=False):

        from fmpy.util import read_csv, validate_result

        for model in models:

            print(model)

            fmu_filename = os.path.join(build_dir, 'dist', model + '.fmu')

            problems = validate_fmu(fmu_filename)

            self.assertEqual([], problems)

            if model == 'Feedthrough':
                start_values = {
                    'real_fixed_param': 1,
                    'string_param': "FMI is awesome!"
                }
                in_csv = os.path.join(test_fmus_dir, model, model + '_in.csv')
                input = read_csv(in_csv)
            else:
                start_values = {}
                input = None

            ref_csv = os.path.join(test_fmus_dir, model, model + '_ref.csv')

            for fmi_type in fmi_types:

                ref = read_csv(ref_csv)

                if compile:
                    compile_platform_binary(fmu_filename)

                result = simulate_fmu(fmu_filename,
                                      fmi_type=fmi_type,
                                      start_values=start_values,
                                      input=input,
                                      solver='Euler')

                dev = validate_result(result, ref)

                self.assertLess(dev, 0.2, "Failed to validate " + model)
Exemplo n.º 5
0
def validate(build_dir, fmi_types, models, compile=False):

    from fmpy.util import read_csv, validate_result

    test_fmus_dir = Path(__file__).parent

    for model in models:

        print(model)

        fmu_filename = os.path.join(build_dir, 'dist', model + '.fmu')

        problems = validate_fmu(fmu_filename)

        assert not problems

        if model == 'Feedthrough':
            start_values = {'Float64_fixed_parameter': 1, 'String_parameter': "FMI is awesome!"}
            in_csv = os.path.join(test_fmus_dir, model, model + '_in.csv')
            input = read_csv(in_csv)
        else:
            start_values = {}
            input = None

        ref_csv = os.path.join(test_fmus_dir, model, model + '_ref.csv')

        for fmi_type in fmi_types:

            ref = read_csv(ref_csv)

            if compile:
                if model == 'Resource' and os.name == 'nt':
                    continue
                compile_platform_binary(fmu_filename)

            result = simulate_fmu(fmu_filename,
                                  fmi_type=fmi_type,
                                  start_values=start_values,
                                  input=input,
                                  solver='Euler')

            dev = validate_result(result, ref)

            assert dev < 0.2, "Failed to validate " + model
Exemplo n.º 6
0
def test_fmi3():

    build_dir = Path(__file__).parent / 'fmi3'

    build_fmus(build_dir, fmi_version=3)

    # run examples
    examples = [
        'cs_early_return',
        'cs_event_mode',
        'cs_intermediate_update',
        'BouncingBall_cs',
        'BouncingBall_me',
        'import_shared_library',
        'import_static_library',
        'jacobian',
        'scs_synchronous',
        'Stair_cs',
        'Stair_me'
    ]

    if os.name == 'nt':
        examples.append('scs_threaded')  # runs only on Windows

    for example in examples:
        print("Running %s example..." % example)
        subprocess.check_call(build_dir / 'temp' / example, cwd=build_dir / 'temp')

    models = ['BouncingBall', 'Dahlquist', 'Feedthrough', 'Resource', 'Stair', 'VanDerPol']

    validate(build_dir, fmi_types=['CoSimulation', 'ModelExchange'], models=models)
    validate(build_dir, fmi_types=['CoSimulation', 'ModelExchange'], models=models, compile=True)

    for model in ['Clocks', 'LinearTransform']:
        problems = validate_fmu(filename=build_dir / 'dist' / f'{model}.fmu')
        assert len(problems) == 0

    copy_to_cross_check(dist_dir=build_dir / 'dist', model_names=models, fmi_version='3.0', fmi_types=['cs', 'me'])
    copy_to_cross_check(dist_dir=build_dir / 'dist', model_names=['Clocks'], fmi_version='3.0', fmi_types=['se'])
Exemplo n.º 7
0
def main():

    import argparse
    import textwrap

    description = """\
    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
    """

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=textwrap.dedent(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")

    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(args.fmu_filename,
                                target_platform=args.target_platform)

    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)
Exemplo n.º 8
0
    def test_create_fmu_container(self):

        resources = os.path.join(os.path.dirname(__file__), 'resources')

        configuration = Configuration(
            parallelDoStep=True,
            description="A controlled drivetrain",
            variableNamingConvention='structured',
            unitDefinitions=[
                Unit(name='rad/s',
                     baseUnit=BaseUnit(rad=1, s=-1),
                     displayUnits=[
                         DisplayUnit(name='rpm', factor=0.1047197551196598)
                     ])
            ],
            typeDefinitions=[
                SimpleType(name='AngularVelocity', type='Real', unit='rad/s')
            ],
            variables=[
                Variable(type='Real',
                         variability='tunable',
                         causality='parameter',
                         name='k',
                         start='100',
                         description='Gain of controller',
                         mapping=[('controller', 'PI.k')]),
                Variable(type='Real',
                         variability='continuous',
                         causality='input',
                         name='w_ref',
                         start='0',
                         description='Reference speed',
                         mapping=[('controller', 'u_s')],
                         declaredType='AngularVelocity'),
                Variable(type='Real',
                         variability='continuous',
                         causality='output',
                         name='w',
                         description="Gain of controller",
                         mapping=[('drivetrain', 'w')],
                         unit='rad/s',
                         displayUnit='rpm'),
            ],
            components=[
                Component(filename=os.path.join(resources, 'Controller.fmu'),
                          name='controller'),
                Component(
                    filename=os.path.join(resources, 'Drivetrain.fmu'),
                    name='drivetrain',
                )
            ],
            connections=[
                Connection('drivetrain', 'w', 'controller', 'u_m'),
                Connection('controller', 'y', 'drivetrain', 'tau'),
            ])

        filename = 'ControlledDrivetrain.fmu'

        create_fmu_container(configuration, filename)

        problems = validate_fmu(filename)

        self.assertEqual(problems, [])

        w_ref = np.array([(0.5, 0), (1.5, 1), (2, 1), (3, 0)],
                         dtype=[('time', 'f8'), ('w_ref', 'f8')])

        result = simulate_fmu(filename,
                              start_values={'k': 20},
                              input=w_ref,
                              output=['w_ref', 'w'],
                              stop_time=4)
Exemplo n.º 9
0
def test_create_fmu_container_cs(resources_dir):

    configuration = Configuration(
        parallelDoStep=True,
        description="A controlled drivetrain",
        variableNamingConvention='structured',
        unitDefinitions=[
            Unit(name='rad/s',
                 baseUnit=BaseUnit(rad=1, s=-1),
                 displayUnits=[
                     DisplayUnit(name='rpm', factor=0.1047197551196598)
                 ])
        ],
        typeDefinitions=[
            SimpleType(name='AngularVelocity', type='Real', unit='rad/s')
        ],
        variables=[
            Variable(type='Real',
                     variability='tunable',
                     causality='parameter',
                     initial='exact',
                     name='k',
                     start='40',
                     description='Gain of controller',
                     mapping=[('controller', 'PI.k')]),
            Variable(type='Real',
                     variability='continuous',
                     causality='input',
                     name='w_ref',
                     start='0',
                     description='Reference speed',
                     mapping=[('controller', 'u_s')],
                     declaredType='AngularVelocity'),
            Variable(type='Real',
                     variability='continuous',
                     causality='output',
                     initial='calculated',
                     name='w',
                     description="Gain of controller",
                     mapping=[('drivetrain', 'w')],
                     unit='rad/s',
                     displayUnit='rpm'),
        ],
        components=[
            Component(filename=resources_dir / 'Controller.fmu',
                      interfaceType='CoSimulation',
                      name='controller'),
            Component(
                filename=resources_dir / 'Drivetrain.fmu',
                interfaceType='CoSimulation',
                name='drivetrain',
            )
        ],
        connections=[
            Connection('drivetrain', 'w', 'controller', 'u_m'),
            Connection('controller', 'y', 'drivetrain', 'tau'),
        ])

    filename = 'ControlledDrivetrain.fmu'

    create_fmu_container(configuration, filename)

    problems = validate_fmu(filename)

    assert not problems

    input = read_csv(resources_dir / 'ControlledDrivetrain_in.csv')

    result = simulate_fmu(filename,
                          input=input,
                          output=['w_ref', 'w', 'k'],
                          stop_time=5,
                          output_interval=5e-2)

    t_band, y_min, y_max, i_out = validate_signal(t=result['time'],
                                                  y=result['w'],
                                                  t_ref=input['time'],
                                                  y_ref=input['w_ref'],
                                                  dx=100,
                                                  dy=0.4)

    assert result['k'][0] == 40, 'Default start value has not been set.'

    assert not i_out.any()
Exemplo n.º 10
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)
Exemplo n.º 11
0
def test_main_generates_valid_fmu(tmp_path, csvfile):
    fmu = str(tmp_path / "model.fmu")
    main([str(csvfile), "--inputs", "x", "y", "--outputs", "z", "-o", fmu])
    errors = validate_fmu(fmu)
    assert not errors
Exemplo n.º 12
0
 def test_fmpy_validate(self):
     errs = validate_fmu(fmu_path)
     self.assertCountEqual(errs, [], 'Errors list should be empty')