Model Developer step-by-step tutorial for the creation of the Pasteurization example
Model developers who want to develop process models with the LIBPF® process modeling technology.
Give a step-by-step tutorial and a reference for the steps required to re-create from scratch the Pasteurization example.
Each kernel / service is typically arranged in a subdirectory of the LIBPF® SDK.
For this example we assume there is a pasteur directory for the kernel / service. Inside the kernel / service subdirectory, create two directories include and src.
The resulting directory structure is then:
LIBPF --- include | L- src | L- scripts | L- pasteur --- include | | | L--- src | | | L--- ... | L- ...
Declare the classes
Multiple models can be provided by one kernel.
The best practice is to declare each class in a separate file, with the same name as the class.
In this example there is only one, the Pasteur class so create a header file in the pasteur/include directory and name it “Pasteur.h”
Type (or copy-paste) the flowsheet model declaration:
We strongly suggest to start here the code documentation in a doxygen-fashion.
Take care of “CUSTOM variables”, “MANDATORY” methods and “NON-MANDATORY” methods.
The “CUSTOM variables” are input/output parameters, that the model will show in the foreground. Some of them are simply pulled-up internal variable (see FlowSheet::pre and FlowSheet::post).Other can be involved in complex calculation (see Makeuserequation).
Create a model driver file in the pasteur/src directory and name it “PasteurDriver.cc”
Include all model headers
If your models’ declarations are scattered in several header files, include them all.
In this example there is only one:
Register the kernel / service
A kernel / service must be registered with:
name: the kernel name as an UTF-8 encoded string
description: the kernel description as an UTF-8 encoded string
license: the kernel copyright / license as an UTF-8 encoded string
version: the kernel version as an UTF-8 encoded string; this will appear during the execution and in the reports
default type: the model that will be instantiated when the user does not specify the type
uuid: the service UUID (Universally Unique Identifier) uniquely identifies a specific service + service version
For the pasteur example:
Register the provided types
Here the types are registered, along with a description and a list of options that will be used by the various interfaces to present these information to the user.
Type aliases can be defined, where a base type is pre-configured with certain default values for some options.
String options are defined using enumerators (see below).
We suggest to keep eventual tentative models, components, and model registrations. When you do not need them anymore just comment “//” them.
Populate the list of components
These components will be available for all the models using this driver.
In this case we will use four components already available in LIBPF®:
Populate the enumerators
These enumerators can be used to supply the string options during type construction
Define the classes
Now it is possible to start to write the actual model.
Multiple models can be provided by one kernel.
As for the class declaration in separate headers, the best practice is to define each class in a separate file, with the same name as the class.
In this example there is only one, the Pasteur class so create the model source file in the pasteur/src directory and name it “Pasteur.cc”
The usual workflow is an iterative improvement of the model through the following steps:
Implement the constructor
The class constructor must contain:
The “CUSTOM variables” declared in the Pasteur.h file must be “registered” as part of the model.
There are two required steps:
- first a DEFINE statement must be added for each of them in the constructor member initialization list; this operation enriches them with a default value, UOM and description:
- second an addVariable statement must be added for each of them in the body of the constructor; this operation makes the list of variables accessible at runtime to each live instance of the class via the reflection:
If the model supports any string or integer option, specify in this section their default value; for each string option also specify the associated enumerator.
Define the units
We suggest to start always with the simplest model possible (i.e. none or one unit operation) and try to connect this with source and sink vertexes.
Units are vertexes defined using strings which specify: the type of the unit, a custom name and a description.
It is also possible to specify some options. These options are useful to pass the number of stages or the number of splits or the reaction list or any other unit-specific-supported option.
Define the streams and the flowsheet connections
Streams are edges which connect vertexes. The streams are defined using strings which specify: the type of the stream, a custom name and a description.
In addition to the user-defined units, all flowsheets come with the predefined “source” and “sink” vertexes, that represent the battery limits.
The inlet streams to the flowsheet come from the “source” vertex “out” port. The outlet streams from the flowsheet go to the “sink” vertex “in” port.
The registration of vertex and edge is sequential, so before the “addStream” of a certain stream, the “addUnit” connected by that stream must be known.
Fine-tune the graphical appearance
It is also possible to customize here the icons and icon sized of the units that will be used in the process flow diagram
Implement the setup
This method initializes the model by setting up input variables at their default value and by providing estimates to some results to help convergence.
The options supplied to the constructor can be used to customize the model here:
Specify inlet streams
Provide default values for the inlet streams to the flowsheet:
Specify unit operations
Provide default values for the operating conditions of the units:
When a flowsheet has a recycle, this must be temporary cut and initialized for the correct system calculation.
In the present example, the “recycle” is represented by the stream around the recuperation. We choose to cut and initialize the hot inlet stream (S05) of the exchanger.
Set/modify the Input/Output behavior of the model variable in order to show or hide them in the graphic interface as input or output parameters.
This operation is strongly suggested for the “CUSTOM variable” since their default behavior is to hide them all.
Sometime it could be useful to modify the default behavior of internal parameter such as the volumetric flow of the stream.
Implement the pre-calculations
You can optionally run some pre-calculate operations; the most frequent use is to compute the model inputs based on some custom variable.
Only put here explicit equations, which do not need a “simultaneous” resolution. If you have calculations that have to be solved iteratively, insert them in the makeUserEquation method below.
In this example model we do not have pre-calculate operations.
Implement the post-calculations
You can optionally run some post-calculate operations after the flowsheet has been solved.
You can have two type of post-calculation instructions:
Custom warning / error messages
Some checks on the results are possible as “warnings” or as “errors”.
This will alert in case of possible wrong behavior of the model
In this example model we do not have custom messages.
Assignment of the output “CUSTOM variables”
This is the right place to compute custom results.
Only put here explicit equations. If you have implicit calculations that have to be solved iteratively, insert them in the makeUserEquation method below.
Implement the makeUserEquation method
This method is used to implement custom implicit user equation that have to be solved iteratively.
In this example we have just bound some internal parameter to some “CUSTOM variables”:
Control flowsheet resolution
Finally there are some method useful to control the flowsheet calculation: iteration, simultaneous support and pre-sequential computation: