Hopsan
|
Components are written in header-only C++ files (.hpp). In this section we will look at "MyExampleConstantOrifice.hpp" from the exampleComponentLibrary. This is a very simple component that only includes a subset of functions. This is convenient when "getting started" but you should also see ComponentAuthorFunctions for a list of all functions that can be useful when creating components.
This part goes through the basic structure of the component file. You should look in the actual file for the full code and more detailed comments. You should also have a look at the MyExampleOrifice.hpp
file. The difference between the files illustrate the difference between having a constant parameter or a variable parameter. (Input Variable).
This first lines set a header guard to avoid including the same code twice. Technically you do not need the header guards if you can guarantee that you do not include the same file twice (usually not a problem in component libraries). Then we include the essential functions for the component from the HopsanCore. It may be necessary to include more files, for example "ComponentUtilities.h" for accessing built-in component utilities in Hopsan. You may also include other external header files if you wish to include function from other external libraries.
Next the component class is declared. We inherit from a ComponentQ as this is a Q-type component. This could also be ComponentC or ComponentSignal for C-type, Signal-type components. It is also possible to inherit ComponentSystem in order to write programmed subsystems. But this is VERY advanced and complicated and will not be covered in the documentation.
First the private part of the component is specified. Here we declare member variables, (variables that should be persistent in the component). In this case we have a restriction coefficient called mKc and two port pointers called mpP1 and mpP2 ("m" is an abbreviation of "member" and "mp" means "member and pointer"). The names do not actually matter, but using a naming convention makes the code easier to read.
In the public part we first define a static creator function, which is used to create instances of the component in the simulation core. Nothing needs to be changed except the name of the class.
The second member function you need to define is the configure function for the component. This function is run every time a new object of the class is created (added to the model). The function is used to register ports, input variables, output variables and constants, and to configure default values for member variables. First we create the ports used for communication with the surrounding components, in this case two hydraulic PowerPorts. Finally, we register the restrictor coefficient as constant parameter with a name, description and unit. This will make it available to the user in the graphical interface.
Adding Ports: The following functions are available for port creation: You need to specify a unique (within the component) portname, which nodetype it should use, and whether it must be connected: Port::Required or Port::NotRequired. If the final argument is omitted it will default to Port::Required. Description is optional (but recommended) The currently available built-in node types can be found here: NodeTypes
New node types can be added through external libraries, but this should be avoided if possible.
Adding Input and Output Variables: The following functions are available for input and output variable creation: If the last argument ppNodeData is given it will auto register and set the data pointer so that you can use it safely without having to worry about it dangling. In the output variable case, the defaultValue is optional. If a default vale is not given, then the output value will not get a start value. This is usefully in cases where the component should calculate its own initial values. Example: In a Sum component you want the initial output value to be the sum of the initial input values. Being able to set a different start value on the output does not make sense.
Adding Constants: The following functions are available for creating constants The correct function will automatically be selected depending on the rData type. The function will register the given name, unit and description and connect this information to a pointer pointing at the variable you specify as the rData argument. The conditional Constant associates description strings to integer numbers and is shown as a drop-down selection box in the HopsanGUI.
The next member function that must be defined is the initialize function. This function is run once before simulation starts. This function runs after connections have been established, you can read or write to/from connected components. If needed, you can use this information to initialize your component properly. This is also the place to allocate additional memory if needed. In this function you also typically initialize more advanced utilities such as (but not limited to) Delays, Integrators or Transfer-functions. In this example case we have nothing that needs to be initialized.
The next function simulateOneTimestep, is the most important member function. It contains the model equations that are executed each time step. We begin by creating local variables with copies of the values from the connected ports, this is intended to make the actual equations more human readable. If you know that one of these local variables will not change, then you should make it const. This will prevent you from accidentally assigning it and may allow the compiler to optimize the code better. The middle part consists of the actual equations. In this case we calculate flow and pressure through the orifice from wave variables and impedance in the neighboring C-type components. We end by writing back the new values that were calculated.
The next member function finalize, is optional. It is only useful if you want some code to be run after simulation has finished. This is usually only needed if you want to free memory that was additionally allocated in the initialize function.
The last member function deconfigure, is also optional. This code is run once the component is deleted. Here you can cleanup any custom memory allocation or similar that you have don in the configure function. Note! You do not need to remove ports, constants or variables. Hopsan will handle that automatically. Usually you never need to implement anything in this function.