def validate_cross_check_result(result_dir): """ Validate a cross-check result Parameters: result_dir path to the directory that contains the results Returns: a list of problems """ problems = [] if os.path.isfile(os.path.join(result_dir, 'notCompliantWithLatestRules')): return problems # check ReadMe if not os.path.isfile(os.path.join( result_dir, 'ReadMe.txt')) and not os.path.isfile( os.path.join(result_dir, 'ReadMe.pdf')): problems.append("Readme.[txt|pdf] is missing in %s" % result_dir) if not os.path.isfile(os.path.join(result_dir, 'passed')): return problems _, model_name = os.path.split(result_dir) # check the output file csv_filename = os.path.join(result_dir, model_name + '_out.csv') try: read_csv(csv_filename) except Exception as e: problems.append("Error in %s. %s" % (csv_filename, e)) return problems
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') 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) dev = validate_result(result, ref) self.assertLess(dev, 0.2, "Failed to validate " + model)
def test_structured_csv(self): cols = [ ('time', np.float64, None), ('y', np.float64, (3,)), ] rows = [ (0.0, (1.0, 2.0, 3.0)), (0.1, (1.1, 2.1, 3.1)), (0.2, (1.2, 2.2, 3.2)), (0.3, (1.3, 2.3, 3.3)), ] # create a structured array with a 1-d array signal result = np.array(rows, dtype=np.dtype(cols)) # arrays are saved as single columns write_csv('structured.csv', result) # read as-is (single columns) traj = read_csv('structured.csv') self.assertEqual(traj.dtype.names, ('time', 'y[1]', 'y[2]', 'y[3]')) # read structured (restore arrays) traj = read_csv('structured.csv', structured=True) self.assertEqual(traj.dtype.names, ('time', 'y')) self.assertEqual(traj['y'].shape, (4, 3))
def simulate(options): # read the input file if 'input_filename' in options: input = read_csv(options['input_filename']) else: input = None step_size = options['step_size'] # select solver based on step_size if step_size > 0: solver = 'Euler' else: solver = 'CVode' solver = 'CVode' # simulate the FMU result = fmpy.simulate_fmu(filename=options['fmu_filename'], validate=False, solver=solver, step_size=step_size, stop_time=options['stop_time'], input=input, output=options['output_variable_names'], timeout=5) return result
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
def simulate(options): # read the input file if 'input_filename' in options: input = read_csv(options['input_filename']) else: input = None # simulate the FMU result = fmpy.simulate_fmu(filename=options['fmu_filename'], validate=False, step_size=options['step_size'], stop_time=options['stop_time'], input=input, output=options['output_variable_names']) return result
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)
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 """ parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent(description)) parser.add_argument( 'command', choices=['info', 'validate', 'simulate', 'compile', 'add-remoting'], 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") args = parser.parse_args() if args.command == 'info': from fmpy import dump dump(args.fmu_filename) elif args.command == 'validate': import sys from fmpy.util import validate_fmu messages = validate_fmu(args.fmu_filename) if len(messages) == 0: print('The validation passed') else: print('The following errors were found:') for message in messages: print() print(message) sys.exit(1) elif args.command == 'compile': from fmpy.util import compile_platform_binary compile_platform_binary(args.fmu_filename) elif args.command == 'add-remoting': from fmpy.util import add_remoting add_remoting(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)
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()
def validate_test_fmu(model_dir): """ Validate an exported FMU Parameters: model_dir path to the directory that contains the exported FMU Returns: a list of problems """ problems = [] _, model_name = os.path.split(model_dir) if os.path.isfile(os.path.join(model_dir, 'notCompliantWithLatestRules')): return [] fmu_filename = os.path.join(model_dir, model_name + '.fmu') # validate the modelDescription.xml try: model_description = read_model_description(fmu_filename, validate=True) except Exception as e: problems.append("Error in %s. %s" % (fmu_filename, e)) return problems # stop here # collect the variable names variable_names = [v.name for v in model_description.modelVariables] # check the reference options file try: ref_opts_filename = os.path.join(model_dir, model_name + '_ref.opt') read_ref_opt_file(ref_opts_filename) except Exception as e: problems.append("Error in %s. %s" % (ref_opts_filename, e)) # check the CSVs for suffix, required in [('_cc.csv', True), ('_in.csv', False), ('_ref.csv', True)]: csv_filename = os.path.join(model_dir, model_name + suffix) if not required and not os.path.isfile(csv_filename): continue try: read_csv(csv_filename, variable_names=variable_names) except Exception as e: problems.append("Error in %s. %s" % (csv_filename, e)) # check compliance checker log file cc_logfile = model_name + '_cc.log' if not os.path.isfile(os.path.join(model_dir, cc_logfile)): problems.append("%s is missing in %s" % (cc_logfile, model_dir)) # check ReadMe if not os.path.isfile(os.path.join( model_dir, 'ReadMe.txt')) and not os.path.isfile( os.path.join(model_dir, 'ReadMe.pdf')): problems.append("Readme.[txt|pdf] is missing in %s" % model_dir) if platform in ['win32', 'win64']: cc_script = model_name + '_cc.bat' else: cc_script = model_name + '_cc.sh' if not os.path.isfile(os.path.join(model_dir, cc_script)): problems.append("%s is missing in %s" % (cc_script, model_dir)) return problems
def startSimulation(self): from fmpy.gui.simulation import SimulationThread try: start_time = float(self.startTimeLineEdit.text()) stop_time = float(self.stopTimeLineEdit.text()) step_size = float(self.ui.stepSizeLineEdit.text()) relative_tolerance = float( self.ui.relativeToleranceLineEdit.text()) if self.ui.outputIntervalRadioButton.isChecked(): output_interval = float(self.ui.outputIntervalLineEdit.text()) else: max_samples = float(self.ui.maxSamplesLineEdit.text()) output_interval = stop_time / max_samples except Exception as ex: self.log.log('error', "Failed to start simulation: %s" % ex) self.ui.stackedWidget.setCurrentWidget(self.ui.logPage) return step_size = min(step_size, output_interval) if self.ui.solverComboBox.currentText() == 'Fixed-step': solver = 'Euler' else: solver = 'CVode' if self.ui.inputCheckBox.isChecked(): input_variables = [] for variable in self.modelDescription.modelVariables: if variable.causality == 'input': input_variables.append(variable.name) try: from fmpy.util import read_csv filename = self.ui.inputFilenameLineEdit.text() input = read_csv(filename, variable_names=input_variables) except Exception as e: self.log.log( 'error', "Failed to load input from '%s'. %s" % (filename, e)) return else: input = None output = [] for variable in self.modelDescription.modelVariables: output.append(variable.name) fmi_type = 'CoSimulation' if self.fmiTypeComboBox.currentText( ) == 'Co-Simulation' else 'ModelExchange' self.simulationThread = SimulationThread( filename=self.filename, fmiType=fmi_type, startTime=start_time, stopTime=stop_time, solver=solver, stepSize=step_size, relativeTolerance=relative_tolerance, outputInterval=output_interval, startValues=self.startValues, applyDefaultStartValues=self.ui.applyDefaultStartValuesCheckBox. isChecked(), input=input, output=output, debugLogging=self.ui.debugLoggingCheckBox.isChecked(), fmiLogging=self.ui.logFMICallsCheckBox.isChecked()) self.ui.actionSimulate.setIcon(QIcon(':/icons/stop.png')) self.ui.actionSimulate.setToolTip("Stop simulation") self.ui.actionSimulate.triggered.disconnect(self.startSimulation) self.ui.actionSimulate.triggered.connect(self.simulationThread.stop) self.simulationProgressBar.setVisible(True) self.simulationThread.messageChanged.connect(self.log.log) self.simulationThread.progressChanged.connect( self.simulationProgressBar.setValue) self.simulationThread.finished.connect(self.simulationFinished) if self.ui.clearLogOnStartButton.isChecked(): self.log.clear() self.setCurrentPage(self.ui.resultPage) self.simulationThread.start() self.plotUpdateTimer.start(100) self.updatePlotLayout()
def main(): import argparse import textwrap description = """\ Simulate an FMU Example: > python -m fmpy.simulate Rectifier.fmu """ parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent(description)) parser.add_argument('command', choices=['info', 'simulate'], help="Command to execute") parser.add_argument('fmu_filename', help="filename of the FMU") parser.add_argument('--solver', choices=['Euler', 'CVode'], default='CVode', help="solver to use for Model Exchange") 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('--num-samples', default=500, type=int, help="number of samples to record") parser.add_argument('--step-size', type=float, help="step size for fixed-step solvers") 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('--show-plot', action='store_true', help="plot the results") parser.add_argument('--timeout', type=float, help="max. time to wait for the simulation to finish") parser.add_argument('--fmi-logging', action='store_true', help="enable FMI logging") parser.add_argument('--start-values', nargs='+', help="name-value pairs of start values") args = parser.parse_args() if args.command == 'info': from fmpy import dump dump(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 result = simulate_fmu(args.fmu_filename, validate=True, start_time=args.start_time, stop_time=args.stop_time, solver=args.solver, step_size=args.step_size, output_interval=None, fmi_type=None, start_values=start_values, input=input, output=args.output_variables, timeout=args.timeout, fmi_logging=args.fmi_logging) if args.output_file: write_csv(args.output_file, result) if args.show_plot: plot_result(result=result, window_title=args.fmu_filename)
('Feedthrough', {'real_fixed_param': 1}, 0.2), ('Resource', {}, 0.2), ('Stair', {}, 10), ('VanDerPol', {}, 0.1), ] for model_name, start_values, output_interval in info: print(model_name) fmu = os.path.join(dist_dir, model_name + '.fmu') ref_csv = os.path.join(src_dir, model_name, model_name + '_ref.csv') ref_png = os.path.join(src_dir, model_name, model_name + '_ref.svg') in_csv = os.path.join(src_dir, model_name, model_name + '_in.csv') if os.path.isfile(in_csv): input = read_csv(in_csv) else: input = None result = simulate_fmu(fmu, fmi_type='ModelExchange', # solver='Euler', start_values=start_values, input=input, output_interval=output_interval ) write_csv(ref_csv, result) ref = read_csv(ref_csv) plot_result(ref, events=True, filename=ref_png)
def startSimulation(self): from .simulation import SimulationThread # TODO: catch exceptions stop_time = float(self.stopTimeLineEdit.text()) step_size = float(self.ui.stepSizeLineEdit.text()) relative_tolerance = float(self.ui.relativeToleranceLineEdit.text()) max_samples = float(self.ui.maxSamplesLineEdit.text()) output_interval = stop_time / max_samples if self.ui.solverComboBox.currentText() == 'Fixed-step': solver = 'Euler' else: solver = 'CVode' if self.ui.inputCheckBox.isChecked(): input_variables = [] for variable in self.modelDescription.modelVariables: if variable.causality == 'input': input_variables.append(variable.name) try: from fmpy.util import read_csv filename = self.ui.inputFilenameLineEdit.text() input = read_csv(filename, variable_names=input_variables) except Exception as e: self.log.log( 'error', "Failed to load input from '%s'. %s" % (filename, e)) return else: input = None output = [] for variable in self.modelDescription.modelVariables: output.append(variable.name) self.simulationThread = SimulationThread( filename=self.filename, stopTime=stop_time, solver=solver, stepSize=step_size, relativeTolerance=relative_tolerance, outputInterval=output_interval, startValues=self.startValues, input=input, output=output) self.ui.actionSimulate.setIcon(QIcon(':/icons/stop.png')) self.ui.actionSimulate.setToolTip("Stop simulation") self.ui.actionSimulate.triggered.disconnect(self.startSimulation) self.ui.actionSimulate.triggered.connect(self.simulationThread.stop) self.simulationProgressBar.setVisible(True) self.simulationThread.messageChanged.connect(self.log.log) self.simulationThread.progressChanged.connect( self.simulationProgressBar.setValue) self.simulationThread.finished.connect(self.simulationFinished) if self.ui.clearLogOnStartButton.isChecked(): self.log.clear() self.setCurrentPage(self.ui.resultPage) self.simulationThread.start() self.plotUpdateTimer.start(100) self.updatePlotLayout()