Skip to content

Linearize Example🔗

Below is the code for the Custom Function linearize.py.

The actual file is a part of the Linearize Example introduced in Project Specific Custom Functions and which parts are referenced Creating Custom Functions.

The actual file can also be found in the installation folder: modelon_impact_installation_folder/custom_function_examples.

``` import os import numbers import tempfile

import pandas import numpy as np import scipy

def signature(): """ The signature function specifying how the Custom Function is presented to the user in Modelon Impact. :return: A dictionary on the format: "version" - The version number of the Custom Function "name" - The name of the Custom Function to appear in Modelon Impact "description" - A description of what the Custom Function does. "parameters" - A list of parameters to be set by the user via the Modelon Impact simulation browser (optional). Each parameter is specified by a dictionary on the format (all optional): "name" - the name of the parameter to appear in Modelon Impact "type" - The type of the parameter: String, Number, Boolean or Enumeration "description" - A description of the parameter "defaultValue" - The default value of the parameter """ return { "version": "0.0.1", "name": "Linearize", "description": "Linearize the model and compute its state space representation" "(matrices A, B, C and D).", "parameters": [ { "name": "t_linearize", "type": "Number", "description": "Time (in seconds) at which to perform linearization." "To linearize at initialization," "set t=0.", "defaultValue": 1, }, { "name": "print_to_log", "type": "Boolean", "description": "Linearized model statistics are printed in the log, " "if this option is set to True", "defaultValue": True, }, ], }

def run(get_fmu, environment, parametrization, upload_custom_artifact, t_linearize, print_to_log): """ The run function, defining the operation or computation of the Custom Function. :param get_fmu: A function returning an FMU object for the model the custom function is applied on, with applied non-structural parameters as set in Modelon Impact. :param environment: A dictionary specifying environment variables: "result_folder_path" - The path to the folder where the result is to be saved "result_file_name" - The name of the file where the results are to be saved "workspace_id" - The workspace ID "case_id" - The case ID "experiment_id" - The experiment ID "log_file_name" - The name of the log file :param upload_custom_artifact: Function for uploading a custom artifact to the storage. Takes arguments: "artifact_id": The artifact ID. "local_file_path": Path to the local custom artifact to upload Returns the route for downloading the artifact. :param parametrization: The parametrization of the model as set in Modelon Impact experiment mode. :param t_linearize: Time (in seconds) at which to perform linearization. :param print_to_log: Toggle weather the linearized model statistics should be shown in the simulation log. """

# In this case, the linearization is packaged into a separate function. This
# enables to use it outside of Modelon Impact and thereby also makes
# it convenient to test.
model = get_fmu()
for key, value in parametrization.items():
    model.set(key, value)
return linearize(model, environment, upload_custom_artifact, t_linearize, print_to_log)

def linearize(model, environment, upload_custom_artifact, t_linearize, print_to_log): """ Compute the ABCD state space representation for a model and write the result to a .csv-file. Also save the result as a .mat-file and upload it as a custom artifact. The .mat-file can be used to load the result into MATLAB. :param model: An FMU object for the model to linearize. :param environment: A dictionary specifying environment variables: "result_folder_path" - The path to the folder where the result is to be saved "result_file_name" - The name of the file where the results are to be saved "workspace_id" - The workspace ID "case_id" - The case ID "experiment_id" - The experiment ID "log_file_name" - The name of the log file :param upload_custom_artifact: Function for uploading a custom artifact to the storage. Takes arguments: "artifact_id": The artifact ID. "local_file_path": Path to the local custom artifact to upload Returns the route for downloading the artifact. :param t_linearize: The time to simulate the model before linearizing. :param print_to_log: Toggle weather the linearized model statistics should be shown in the simulation log """

# Start by type checking the parameter, in case an invalid entry is given by
# the user
if not isinstance(t_linearize, numbers.Number) or t_linearize < 0:
    raise ValueError("The parameter t_linearize needs to be a non-negative number.")
if t_linearize == 0:
    model.initialize()
else:
    model.simulate(final_time=t_linearize)

# Retrieve the state space representation of the linearized model
result = model.get_state_space_representation(use_structure_info=False)

ss = {matrix_name: result[i] for i, matrix_name in enumerate(["A", "B", "C", "D"])}

# Pretty print the matrices to the simulation log
if print_to_log:
    for matrix_name, result in ss.items():
        print('\n' + matrix_name + '= [')
        matrix_shape = result.shape
        if not (matrix_shape[0] == 0 or matrix_shape[1] == 0):
            max_len = max(len(str(e)) for row in result for e in row)
            for row in result:
                print(
                    "\t".join(
                        ['{:<{max_len}}'.format(e, max_len=max_len) for e in row]
                    )
                )
        print(']')

# Scalarize the state space matrices
scalarized_ss = {
    "{}[{},{}]".format(matrix_name, index[0], index[1]): [x]
    for matrix_name, matrix in ss.items()
    for index, x in np.ndenumerate(matrix)
}

# Write the matrices to a csv file in the prescribed path
csv_file_path = os.path.join(
    environment["result_folder_path"], environment["result_file_name"]
)
df = pandas.DataFrame(data=scalarized_ss)
df.to_csv(csv_file_path, index=False)

# Add variable names
state_names = list(model.get_states_list().keys())
input_names = list(model.get_input_list().keys())
output_names = list(model.get_output_list().keys())

ss['state_names'] = state_names
ss['input_names'] = input_names
ss['output_names'] = output_names

# Add operating point
operating_point_time = t_linearize
operating_point_states = [x[0] for x in model.get(state_names)]
operating_point_derivatives = list(model.get_derivatives())
operating_point_inputs = [x[0] for x in model.get(input_names)]
operating_point_outputs = [x[0] for x in model.get(output_names)]

ss['operating_point_time'] = operating_point_time
ss['operating_point_states'] = np.array(operating_point_states)
ss['operating_point_derivatives'] = np.array(operating_point_derivatives)
ss['operating_point_inputs'] = np.array(operating_point_inputs)
ss['operating_point_outputs'] = np.array(operating_point_outputs)

# Pretty print the linearization statistics to the simulation log
if print_to_log:
    if state_names:
        print('\n' + "# At operating point {}s :".format(str(t_linearize)) + '\n')
        print(f'state_names = {state_names}\n')
        print(f'operating_point_states = {operating_point_states}\n')
        print(f'operating_point_derivatives = {operating_point_derivatives}\n')
    if input_names:
        print(f'input_names = {input_names}\n')
        print(f'operating_point_inputs = {operating_point_inputs}\n')
    if output_names:
        print(f'output_names = {output_names}\n')
        print(f'operating_point_outputs = {operating_point_outputs}\n')

# Write result to a .mat file that can be imported in MATLAB
# First write the result to a temporary directory
temp_dir = tempfile.mkdtemp()
temp_mat_file = os.path.join(temp_dir, "result.mat")
scipy.io.savemat(temp_mat_file, ss)

# Now upload the result to the server as a custom artifact
artifact_id = "ABCD"
artifact_route = upload_custom_artifact(artifact_id, temp_mat_file)

# Finally print the route where the artifact can be accessed
print('Stored artifact with ID: {}'.format(artifact_id))
print('')
print('Artifact can be downloaded from @artifact[here]({})'.format(artifact_route))

```

Back to top