Documentation for the ALFAsim-SDK
The ALFAsim-SDK help developers to create plugins for ALFAsim application. Plugins can extend the ALFAsim capabilities in a lot of ways, from adding a simple input to a custom calculation of solver equations.
Some examples of the capabilities that can be extended are:
Creation of user-defined GUI models, allowing custom models to be available over the Tree and in the Model Explorer. Registration of additional variables that are not nonlinear system’s unknowns, for the ALFAsim solver. Also called “secondary variables” Customization of the hydrodynamic models, supporting the additions of custom Phases/Fields/Layers. Support for inclusion of custom mass, momentum and energy equations. Support for calculating source terms to solver equations. Support for calculating/updating plugin-registered Simulation Variables
Creation of user-defined GUI models, allowing custom models to be available over the Tree and in the Model Explorer.
Registration of additional variables that are not nonlinear system’s unknowns, for the ALFAsim solver. Also called “secondary variables”
Customization of the hydrodynamic models, supporting the additions of custom Phases/Fields/Layers.
Support for inclusion of custom mass, momentum and energy equations.
Support for calculating source terms to solver equations.
Support for calculating/updating plugin-registered Simulation Variables
For a list of all capabilities that can be expanded check the Full API Reference in the documentation.
Anyone with programming skills can create a Plugin for ALFAsim. To make it possible, ALFAsim-SDK provides hooks to customize the solver and the user interface. To customize the solver, an API in C is provided for application written in C/C++, while, for user interface customization, an API in Python allows the developer to customize the interface in a declarative way.
To get quick and running with ALFAsim-SDK you can read the Quick Start and the Plugin by Example sections
In this section, it is shown how to create a plugin from scratch with the template command provided by ALFAsim-SDK. With this template, you can easily customize your application to extend ALFAsim functionality.
This allows you to experience the complete creation of a plugin workflow in a short time.
The ALFAsim-SDK is a Python package that helps developers in the process to create a Plugin for ALFAsim, to use this tool it is necessary to have a Python Interpreter with at least version 3.6. For more details on how to install Python check the official docs.
Note
It is recommended that you install the ALFAsim-SDK using a Python Virtual Environment. For more details, see Virtual Environments and Packages in Python documentation.
From a terminal, and inside a virtual environment, update pip:
>>> python -m pip install -U pip
Install the ALFAsim-SDK with:
>>> pip install alfasim-sdk
Also, make sure to have at least the following version for this software:
CMake at least version 3.5.2 Ninja at least version 1.7.0
CMake at least version 3.5.2
CMake
Ninja at least version 1.7.0
Ninja
The alfasim-sdk package has several commands that can be visualized by running the help option.
alfasim-sdk
>>> alfasim-sdk --help
To create a new project, run:
>>> alfasim-sdk template
After the execution of the command above, you will be prompted to fill the following options (all required):
>>> alfasim-sdk template ... -- Plugin Caption: Myplugin ... -- Plugin Id: myplugin ... -- Author Name: ESSS ... -- Author Email: <email>@<server>
The caption to be used across the application to identify the plugin.
The name of the plugin to be referenced during the development.
Name of the plugin author to be displayed.
Email of the plugin author to be displayed.
To check all options, call the help command:
>>> alfasim-sdk template --help
After the execution of the template command the generated plugin project will have the following structure:
\---myplugin | CMakeLists.txt | compile.py | +---assets | plugin.yaml | README.md | \---src | CMakeLists.txt | hook_specs.h | myplugin.cpp | \---python myplugin.py
The highlights here are for:
File with all information about the plugin that will be used by ALFAsim.
Implementation of the hooks for customization of the UI interface, or the solver configuration hooks.
Implementation of the hooks for customization of solver.
Check out the Plugin Structure section for more details about how the folder and files are structured, and also, check the Plugin by Example that shows how to create simple plugins that make use of the User Interface Hooks and the Solver Hooks.
From the root directory of the plugin, execute the command alfasim-sdk package. This command will compile your C/C++ implementation and include the shared libraries inside a artifacts directory and the generated plugin on the root directory with the extension hmplugin.
>>> cd myplugin >>> alfasim-sdk package ... -- Package Name: myplugin
The plugin directory will have the following structure when executing from a Windows Operating System:
\---myplugin | CMakeLists.txt | compile.py | myplugin-1.0.0-win64.hmplugin | +---artifacts | myplugin.dll | myplugin.lib | +---assets | plugin.yaml | README.md | +---build | < ... > | < ... > | +---package | myplugin.dll | plugin.yaml | README.md | \---src | CMakeLists.txt | hook_specs.h | myplugin.cpp | \---python myplugin.py
With myplugin.hmplugin file, it is now possible to install it on ALFAsim application. From the menu bar select the Plugin Manager option, as illustrated in the figure below:
myplugin.hmplugin
In the Plugin Manager windows, install myplugin.hmplugin file plugin.
By clicking on the plugin name, it is possible to visualize the content from the README.md file.
README.md
Restart the application and check the recently installed plugin available over the Tree.
In this section, we will continue to customize the template plugin created from the previous section, this plugin will calculate the liquid velocity to the power of a given exponent provided by the user from the user interface.
The following equation describes the plugin:
Where
Liquid Velocity
Exponent, input from user interface
Plugin calculated value, that will be shown on the output.
For this, we will need to:
Create a simple input, on the user interface.
Add a secondary variable, to keep track of the value.
Retrieve the input data on the HOOK_INITIALIZE.
HOOK_INITIALIZE
Save the input data on a plugin internal data.
Get the liquid velocity from the solver, during run-time.
Export the value into the created/registered plugin secondary variable.
Application customization
Solver Configuration
Hooks for Solver
ALFAsim-SDK allows plugins to customize the user interface of ALFAsim by adding custom models, the image below shows the locations where a custom model can be inserted using the hook alfasim_get_data_model_type().
alfasim_get_data_model_type()
illustrates the section where the models alfasim_sdk.container_model() or alfasim_sdk.data_model() will be placed. illustrates the section where the inputs fields will be placed.
alfasim_sdk.container_model()
alfasim_sdk.data_model()
For this example we will use a data_model() entry over the Tree, using a Quantity() field to get exponent value from the user.
data_model()
Quantity()
The hook alfasim_get_data_model_type() needs to be implemented on the myplugin.py, located on myplugin/src/python folder.
myplugin.py
myplugin/src/python
Implementation of myplugin.py
import alfasim_sdk from alfasim_sdk import data_model from alfasim_sdk import Quantity @data_model(caption='My plugin model', icon='') class MyPluginModel: exponent = Quantity(value=1, unit='-', caption='Exponent value') @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyPluginModel]
Notice that only models that are returned by the hook alfasim_get_data_model_type() will be included on the user interface of ALFAsim.
The image below illustrates the application with the output from the snippet above.
For more details about all input fields available, check the section Types. And for more detail about the available models, check the section Models.
ALFAsim-SDK provides hooks to customize the settings of the application that configures the solver internally, some of these configurations are:
Creation/Registration of new secondary variables
Creation of new phases/fields/layers.
Update of default phases and layers from the application.
For this example, a new alfasim_sdk.SecondaryVariable() will be created, to track the liquid velocity to the power of a custom value provided from the user.
alfasim_sdk.SecondaryVariable()
A Secondary Variable is a variable that can be calculated along the Network. Also, if configured as external, this variable will be set an Output, and will be available within the Trends and Profiles plots.
To create these variables, the hook alfasim_get_additional_variables() must be implemented in the myplugin.py file.
alfasim_get_additional_variables()
@alfasim_sdk.hookimpl def alfasim_get_additional_variables(): import alfasim_sdk from alfasim_sdk import SecondaryVariable from alfasim_sdk import Visibility from alfasim_sdk import Location from alfasim_sdk import Scope return [ SecondaryVariable( name='U_liq_n', caption='Powered Liquid Velocity', unit='-', visibility=Visibility.Output, location=Location.Center, multifield_scope=Scope.Global, checked_on_gui_default=True, ), ]
For more details about SecondaryVariable, check the section Secondary Variables.
SecondaryVariable
ALFAsim-SDK provides hooks that can customize the Solver behavior, this customization are implemented in C/C++ and can make use of the ALFAsim-SDK C/C++ API in order to fetch information from the application.
Solver
At this point, we are going to implement the Solver Hooks that updates the secondary variable declared from myplugin.py file and retrieve the Liquid Velocity from the ALFAsim’s Solver.
First, we need to implement two mandatory hooks, the HOOK_INITIALIZE and the HOOK_FINALIZE
HOOK_FINALIZE
With them it is possible to initialize any custom data (to store any important information) for internal use. Also it is needed to load and unload the ALFAsim-SDK API, in which will allows the plugin to use the API in any implemented hook.
Implementation of myplugin.cpp
myplugin.cpp
ALFAsimSDK_API alfasim_sdk_api; struct MyPluginModel { double exponential = 0.0; }; HOOK_INITIALIZE(ctx) { alfasim_sdk_open(&alfasim_sdk_api); int errcode = -1; double n = 0.0; errcode = alfasim_sdk_api.get_plugin_input_data_quantity( ctx, &n, get_plugin_id(), (const char*) "MyPluginModel.exponent"); if (errcode != 0) { std::cout << "input_data_quantity error=" << errcode << "\n"; return errcode; } int n_threads = -1; errcode = alfasim_sdk_api.get_number_of_threads(ctx, &n_threads); for (int thread_id = 0; thread_id < n_threads; ++thread_id) { // MyPluginModel is a class or struct defined by plugin auto* model = new MyPluginModel(); model->exponential = n; errcode = alfasim_sdk_api.set_plugin_data( ctx, get_plugin_id(), (void*) model, thread_id ); } return OK; } HOOK_FINALIZE(ctx) { auto errcode = -1; auto number_of_threads = -1; errcode = alfasim.get_number_of_threads(ctx, &number_of_threads); for (int thread_id = 0; thread_id < n_threads; ++thread_id) { MyPluginModel* model = nullptr; errcode = alfasim.get_plugin_data(ctx, (void**) (&model), get_plugin_id(), thread_id); delete model; } alfasim_sdk_close(&alfasim_sdk_api); return OK; }
Then, since the plugin wants to calculate its own secondary variable, the HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES must be implemented. As can be seen in the example below, to retrieve the velocity of the continuous liquid field it is necessary to use the get_simulation_array() API function.
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES
get_simulation_array()
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES(ctx) { int errcode = -1; // Get Liquid Field ID int liquid_field_id = -1; errcode = alfasim_sdk_api.get_field_id( ctx, &liquid_field_id, "liquid" ) if (errcode != 0) { std::cout << "get_field_id error = " << errcode << "\n"; return errcode; } // Get Liquid Field Velocity int n_faces = -1; double* U_liq = nullptr; errcode = alfasim_sdk_api.get_simulation_array( ctx, &U_liq, (char*) "U", VariableScope { GridScope::CENTER, MultiFieldDescriptionScope::FIELD, TimestepScope::CURRENT }, liquid_field_id, &n_faces); if (errcode != 0) { std::cout << "get_simulation_array error = " << errcode << "\n"; return OK; } // Get Exponent input data double n = 0.0; { int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); MyPluginModel* model = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) (&model), get_plugin_id(), thread_id ); n = model->exponential; } // Get Plugin Secondary Variable int size = -1; double* U_liq_n_ptr = nullptr; errcode = alfasim_sdk_api.get_plugin_variable( ctx, (void**) &U_liq_n_ptr, "U_liq_n", 0, // Global Scope TimestepScope::CURRENT, &size); if (errcode != 0) { std::cout << "get_plugin_variable error = " << errcode << "\n"; return errcode; } // Calculating the 'U_liq' to power of 'n' for (int i = 0; i < size; ++i) { U_liq_n_ptr[i] = std::pow(U_liq[i], n); }; return OK; }
The image below illustrates the output from the solver, when running the plugin created in this section with the given network.
After reading the quick start section and the plugin by example section, check out these additional resources to help better understand the plugins infrastructure:
As mentioned in Quick Start, the ALFAsim-SDK package has some utilities to help in the process to create a new plugin project and to create a plugin file.
At any moment, it is possible to invoke the help command to list all commands available.
help
You can also learn about the available options of each command invoking the –help option for each command:
>>> alfasim-sdk template --help >>> alfasim-sdk package --help >>> alfasim-sdk compile --help
Generate a template with the necessary files and structure to create a plugin.
The template folder will be placed on the dst option, that by default is the current directory from where the command was invoked.
dst
The files generated and their contents are ready to be used or customized and have the following structure:
alfasim-sdk template [OPTIONS]
Options
--dst
<dst>
A path to where the plugin project should be created. Default: Current directory
--caption
<caption>
Caption to be used across the application to identify the plugin
--plugin-id
<plugin_id>
The name of the plugin
--author-name
<author_name>
Name of the plugin author, this value is stored in plugin metadata and not displayed on the application explicitly
--author-email
<author_email>
Email of the plugin author, this value is stored in plugin metadata and not displayed on the application explicitly
Creates a new <package-name>.hmplugin file containing all the necessary files.
<package-name>.hmplugin
This command will first invoke the compile command to generate the shared library, and after that, the plugin package will be generated with all the content available from the directory assets and artifacts.
compile
By default, the package command will assume that the plugin project is the current directory and the generated file will be placed also in the current directory.
package
In order to change that, it’s possible to use the options plugin-dir and dst
plugin-dir
alfasim-sdk package [OPTIONS]
--plugin-dir
<plugin_dir>
Path to the plugin directory, where configuration and the shared library is located.
A path to where the output package should be created.
--package-name
<package_name>
Name of the package
Generate a <package_name>.hmplugin file with all the content from the directory assets and artifacts.
<package_name>.hmplugin
By default, the package will be created inside the folder plugin_dir, however, it’s possible to give another path filling the dst argument.
alfasim-sdk package_only [OPTIONS]
Compile the plugin from the given plugin-dir option. When not provided plugin-dir will be the current folder location.
This command basically calls the compile.py informing the location of the header files of alfasim_sdk_api. For more details about the build steps, check the compile.py generated for your plugin project.
alfasim-sdk compile [OPTIONS]
The application hooks allow plugins to add custom models or custom checks in the application. To add an application hook it is necessary to implement the given hook in a python file that is already available on your plugin project folder.
As an example, if the alfasim-sdk template command was created with the name myplugin, the necessary file to be customized will be located on: myplugin ‣ src ‣ python ‣ myplugin.py
alfasim-sdk template
myplugin
Plugin Model
Status Monitor
alfasim_get_data_model_type
This hook allows the creation of models in ALFAsim, models can:
Customize items on ALFAsim application, by adding new components over the Tree.
Hold input data information to be accessed from the solver.
Validate input data or configuration made on ALFAsim, to ensure that the plugin has all configuration necessary to be run successfully.
This hook needs to return a class decorated with one of the following options:
alfasim_sdk.models.container_model()
alfasim_sdk.models.data_model()
The image below shows the locations where a custom model can be inserted implementing the hook.
Location to where the models container_model() or data_model() will be placed. Location to where the inputs fields will be placed.
container_model()
Example 1: The following example shows how to create a new model.
import alfasim_sdk from alfasim_sdk import data_model from alfasim_sdk import Quantity @data_model(icon="", caption="My Plugin") class MyModel: distance = Quantity(value=1, unit="m", caption="Distance") @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModel]
The image below shows the output of example 1 when executing on ALFAsim.
Example 2: This second example shows how to create a new container model.
Notice that when using the container_model() you only need to inform the container class to the alfasim_get_data_model_type() hook
import alfasim_sdk from alfasim_sdk import data_model, container_model from alfasim_sdk import Quantity, String @data_model(icon="", caption="My Child") class ChildModel: distance = Quantity(value=1, unit="m", caption="Distance") @container_model(icon='', caption='My Container', model=ChildModel) class MyModelContainer: my_string = String(value='Initial Value', caption='My String') @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModelContainer]
The image below shows the output of example 2 when executing on ALFAsim.
Example 3: This third example demonstrates that it’s possible to create multiple models within the plugin
import alfasim_sdk from alfasim_sdk import data_model, container_model from alfasim_sdk import Quantity, String @data_model(icon="", caption="My Model") class MyModel: distance = Quantity(value=1, unit="m", caption="Distance") @data_model(icon="", caption="My Child") class ChildModel: distance = Quantity(value=1, unit="m", caption="Distance") @container_model(icon='', caption='My Container', model=ChildModel) class MyModelContainer: my_string = String(value='Initial Value', caption='My String') @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModelContainer, MyModel]
The image below shows the output of example 3 when executing on ALFAsim.
alfasim_get_status
Allows plugins to execute custom checks on ALFAsim. These checks can be used to guarantee the consistency of the data or compatibility with some configuration made on ALFAsim.
The status monitor accepts two types of message:
ErrorMessage():Signalize the application to block the simulation until the error is fixed. WarningMessage():Signalize the application that the user needs to fix this problem, but does not need to block the simulation.
ErrorMessage()
Signalize the application to block the simulation until the error is fixed.
WarningMessage()
Signalize the application that the user needs to fix this problem, but does not need to block the simulation.
When no errors are detected, an empty list must be returned.
The alfasim_get_status will be called for:
Each time a model from the plugin is modified. Each time a Physics options from ALFAsim are modified. Ex.: Hydrodynamic model changed
Each time a model from the plugin is modified.
Each time a Physics options from ALFAsim are modified. Ex.: Hydrodynamic model changed
Physics options
The ctx parameter is provided to retrieve information about the current state of the application and the current value of the models implemented by the user.
ctx
Check out the full documentation of alfasim_sdk.context.Context for more details.
alfasim_sdk.context.Context
The following example shows how to display an ErrorMessage when a Quantity() field does not have the desired value.
import alfasim_sdk from alfasim_sdk import data_model from alfasim_sdk import Quantity from alfasim_sdk import ErrorMessage # Define MyModel used in this plugin @data_model(icon="", caption="My Plugin Model") class MyModel: distance = Quantity( value=1, unit="m", caption="Distance" ) @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModel] # Add status monitor in the plugin @alfasim_sdk.hookimpl def alfasim_get_status(ctx): results = [] distance = ctx.GetModel("MyModel").distance.value if distance < 0: message = f"Distance must be greater than 0, got {distance}" results.append( ErrorMessage( model_name="MyModel", message=message, ) ) return results
For the status monitor above, the application will show the following message when the distance is less than 0:
The solver configuration hooks allow plugins to configure internal settings from ALFAsim. To add a configuration hook it is necessary to implement the given hook in a python file that is already available on your plugin project folder.
As an example, if a plugin was created using alfasim-sdk template command and named as myplugin the necessary file to be customized would be located on: myplugin ‣ src ‣ python ‣ myplugin.py
Additional Variables
Hydrodynamic Model
User Defined Tracers
alfasim_get_additional_variables
Allows plugins to register new additional variables on ALFAsim.
It can be used to store the internal variable from the plugin (on solver), or it can be used to expose as an output to the user in the plot window (on application). To calculate and update the registered variables the Solver hooks described on Update plugin variables section must be implemented.
This method expects to return a list of alfasim_sdk.variables.SecondaryVariable(), for more details checkout the reference section with all details about variables
alfasim_sdk.variables.SecondaryVariable()
Usage example:
from alfasim_sdk import SecondaryVariable from alfasim_sdk import Visibility from alfasim_sdk import Location from alfasim_sdk import Scope @alfasim_sdk.hookimpl def alfasim_get_additional_variables(): return [ SecondaryVariable( name='dummy_variable', caption='Plugin 1', unit='m', visibility=Visibility.Internal, location=Location.Center, multifield_scope=Scope.Global, checked_on_gui_default=True, )]
ALFAsim provides a way to customize the hydrodynamic model available within the application, with the usage of the hook listed below, the plugin can:
Add new fields Add/update phases Add/update layers
Add new fields
Add/update phases
Add/update layers
For each new added field is considered a mass conservation equation and for each new added layer is considered a momentum conservation and an energy conservation equations, depending on the energy model used at ALFAsim.
field
layer
.
alfasim_configure_fields
alfasim_configure_phases
alfasim_configure_layers
Allows plugins to configure new layers or associate a new field with a existing layer for ALFAsim’s hydrodynamic model
In order to configure a new layer, it is necessary to return an AddLayer object defining the required fields.
AddLayer
Example of usage:
@alfasim_sdk.hookimpl def alfasim_configure_layers(): return [ AddLayer( name='plugin_layer', fields=['plugin_continuous_field'], continuous_field='plugin_continuous_field', ), UpdateLayer( name=OIL_LAYER, additional_fields=['plugin_dispersed_field'], ), ]
The image below shows the new added phase on the application.
Allows plugins to configure new phases or associate a new field with a existing phase from the application. In order to configure a new phase it is necessary to return an AddPhase object defining the required fields.
AddPhase
@alfasim_sdk.hookimpl def alfasim_configure_phases(): return [ AddPhase( name='plugin_phase', fields=[ 'plugin_continuous_field', 'plugin_dispersed_field', ], primary_field='plugin_continuous_field', is_solid=False, ) ]
With this new phase, all existing hydrodynamic models from the application will have this additional phase. Notice that the fields parameter must be a field registered from the hook alfasim_configure_fields().
fields
alfasim_configure_fields()
You can restrict the operation of your plugin in the application to certain settings by using the status monitor. For example, if your plugin does not work with the water phase you can block the simulation if the user is using a hydrodynamic model with water.
For more details check out the documentation of alfasim_get_status()
alfasim_get_status()
It is also possible to add additional fields to existent phases using the UpdatePhase.
UpdatePhase
@alfasim_sdk.hookimpl def alfasim_configure_phases(): return [ UpdatePhase( name=OIL_PHASE, additional_fields=['plugin_dispersed_field'], ) ]
alfasim_get_phase_interaction_properties_calculated_from_plugin
Allows the plugin to calculate the properties that are related to a pair of phases, like surface tension.
Must return a list of tuple of phases in which state variables will be computed for. In order to implement the properties, HOOK_CALCULATE_PHASE_PAIR_STATE_VARIABLE must be implemented on the C/C++ part of the plugin.
HOOK_CALCULATE_PHASE_PAIR_STATE_VARIABLE
from alfasim_sdk import GAS_PHASE, OIL_PHASE, WATER_PHASE @alfasim_sdk.hookimpl def alfasim_get_phase_interaction_properties_calculated_from_plugin(): return [ (GAS_PHASE, OIL_PHASE), (GAS_PHASE, WATER_PHASE), ]
alfasim_get_phase_properties_calculated_from_plugin
Allows the plugin to calculate the properties (state variables) of a phase.
Must return a list of phase names in which state variables will be computed for. If the plugin implements this hook four C/C++ Solver hooks also must be implemented. They are:
HOOK_INITIALIZE_STATE_VARIABLE_CALCULATOR HOOK_CALCULATE_STATE_VARIABLE HOOK_CALCULATE_PHASE_PAIR_STATE_VARIABLE HOOK_FINALIZE_STATE_VARIABLE_CALCULATOR
HOOK_INITIALIZE_STATE_VARIABLE_CALCULATOR
HOOK_CALCULATE_STATE_VARIABLE
HOOK_FINALIZE_STATE_VARIABLE_CALCULATOR
The first and last hooks are called immediately before and after the state variables are calculated, respectively.
from alfasim_sdk import GAS_PHASE @alfasim_sdk.hookimpl def alfasim_get_phase_properties_calculated_from_plugin(): return [GAS_PHASE, 'solid']
alfasim_get_user_defined_tracers_from_plugin
Allows the plugin to add new tracers in the ALFAsim’s Tracer Solver, in which the transport equation will be modified by Solver hooks listed below.
Must return a list of tracers in which the internal tracer model hooks will be implemented. The following C/C++ Solver hooks must be implemented:
HOOK_INITIALIZE_MASS_FRACTION_OF_TRACER HOOK_COMPUTE_MASS_FRACTION_OF_TRACER_IN_PHASE HOOK_COMPUTE_MASS_FRACTION_OF_TRACER_IN_FIELD HOOK_SET_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER HOOK_UPDATE_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER
HOOK_INITIALIZE_MASS_FRACTION_OF_TRACER
HOOK_COMPUTE_MASS_FRACTION_OF_TRACER_IN_PHASE
HOOK_COMPUTE_MASS_FRACTION_OF_TRACER_IN_FIELD
HOOK_SET_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER
HOOK_UPDATE_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER
@alfasim_sdk.hookimpl def alfasim_get_user_defined_tracers_from_plugin(): return ['my_tracer']
The tracer added in the user-defined tracers from plugin list will not be considered as a standard tracer, which has an output of its mass fraction and appears in the tracer container at ALFAsim’s User Interface. The user-defined tracer is hidden (does not appear in the User Interface) and the plugin developer can modify the transport equation to use its results internally. However, the user-defined tracers will be solved together with the standard tracers (Added via User Interface).
In order to complement the Hydrodynamic model customization, it is possible to inform ALFAsim which phases (added from plugin or not) will have the state variables calculated by plugin.
Hydrodynamic model
Warning
This is advanced customization. We strongly encourage the plugin developer to read the Tracers chapter of ALFAsim’s Technical Report available on the Help menu at ALFAsim application.
The present section describes all solver Hooks available on ALFAsim plugin infrastructure. Solver Hooks are ALFAsim’s pre-defined functions that allows the plugin developer to modify/extend ALFAsim’s Solver. As already informed in Quick Start section once created a plugin using template option on ALFAsim-SDK’s CLI a new file named plugin.c will be available to implement those hooks.
template
plugin.c
There is no need to implement all solver hooks in the plugin. It depends on which functionality the developer wants to extend in the ALFAsim model.
In order to help the developer to decide which hooks to implement in the plugin, they are shown below according to their classification which identifies what part of the solver workflow is related to.
To better visualize the interaction of the hooks with the solver, the Simulator WorkFlow chapter has several graphs illustrating the solver’s walkthrough, showing where each hook will be called. The following graph is the same one available at Main Overview section from Simulator WorkFlow. It illustrates where the HOOK_INITIALIZE and the HOOK_FINALIZE will be called.
digraph { nodesep = 0.6; newrank=true node [fillcolor="#FAAC2C" style="rounded, filled" color="#FAAC2C" fontcolor="#ffffff"] edge [color="#8699A3" fontcolor="#2c3e50"] init [label="Initialize Simulation"] end [label="End of Simulation"] node [shape=point, width=0] invisible_point hook_initialize_point [peripheries="2"] hook_finalize_point [peripheries="2"] time [fixedsize=true, label="Time Step", width="1.0", shape="circle"]; decision [label="Final \n Time?", shape="diamond", fixedsize=true, width=2.0, height=1.0, labelcolor="#8699A3", style="filled"] node[shape="rectangular", target="_top"] config [label="Solver Configuration"] hyd_solver [label="Hydrodynamic Solver", URL="../07_workflow.html#hydrodynamic-solver"] tracer_solver [label="Tracer Solver" URL="../07_workflow.html#tracer-solver"] output [label="Output Variables"] node[peripheries="0" shape="cds", color="#DA5961", fontcolor="#DA5961", style=""] hook_initialize [ label="HOOK_INITIALIZE", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.initialize"] hook_finalize [label="HOOK_FINALIZE", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.finalize"] {rank = same; hook_initialize_point; hook_initialize} {rank = same; hook_finalize_point; hook_finalize; } {rank=same; time;tracer_solver} {rank=same; invisible_point;hyd_solver} init -> config; config -> hook_initialize_point [arrowhead= none]; hook_initialize_point -> invisible_point [arrowhead=none]; hook_initialize_point -> hook_initialize [style=dotted, color="#DA5961"]; subgraph cluster_a{ label="Transient Solver" style="dashed, rounded" shape="reactangular" color="#8699A3" fontcolor="#2c3e50" labeljust="l" invisible_point -> time; time:ne -> hyd_solver:w [style=dashed]; hyd_solver -> tracer_solver [weight=1000]; tracer_solver -> output [weight=1000]; output:nw -> time:se [style=dashed]; time:s -> decision:n [weight=10]; decision:w -> time:sw [label="No"]; } decision -> hook_finalize_point [arrowhead= none, label="Yes"]; hook_finalize_point -> hook_finalize [style=dotted, color="#DA5961"]; hook_finalize_point -> end; }
Initial configuration and plugin internal data
Update plugin variables
Source Terms
State Variables for additional phases
Additional solid phase
Internal deposit layer
Unit Cell Model (UCM) Friction Factor
In the User Interface Hooks section is explained that the plugins are allowed to customize the ALFAsim’s user interface extending it in order to get some input data from the user. Using the solver HOOK_INITIALIZE the developer can obtain the input data from user interface and use it to initialize the plugin internal data.
As already mentioned, it is allowed that the plugins have internal data, in which can hold some important information that will be used during the simulation, and also by other hooks.
The plugin internal data will be hold by ALFAsim’s solver, however the plugin has full responsibility to allocate and deallocate its data from memory.
initialize
c++ signature : HOOK_INITIALIZE(void* ctx)
HOOK_INITIALIZE(void* ctx)
This hook allows the plugin to initialize its internal data and also some simulator configurations available via API. If any API function is used the ALFAsim-SDK API must be loaded, see ALFAsim-SDK API Loading section for more information.
ctx – ALFAsim’s plugins context
Return OK if successful or anything different if failed
Examples of usage:
A minimal HOOK_INITIALIZE needed could be:
1 2 3 4 5 6 7 8 9 10 11
ALFAsimSDK_API alfasim_sdk_api; HOOK_INITIALIZE(ctx) { const char* plugin_id = get_plugin_id() // Loading ALFAsim-SDK API int load_error_code = alfasim_sdk_open(alfasim_sdk_api) if (load_error_code != 0){ return load_error_code; } return OK; }
Where OK is an error_code value and get_plugin_id() function is created automatically to each plugin template and accessible from hook_specs.h file. As can be seen in the example above at least the ALFAsim-SDK API should be loaded.
OK
error_code
get_plugin_id()
hook_specs.h
ALFAsim-SDK
However, if the plugin has internal data the minimal HOOK_INITIALIZE implementation would be
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
ALFAsimSDK_API alfasim_sdk_api; HOOK_INITIALIZE(ctx) { const char* plugin_id = get_plugin_id() // Loading ALFAsim-SDK API int load_error_code = alfasim_sdk_open(alfasim_sdk_api) if (load_error_code != 0){ return load_error_code; } // Threads Information int n_threads = -1; int errcode = alfasim_sdk_api.get_number_of_threads( ctx, &n_threads); if (errcode != 0){ return errcode; } // Setting internal data to each thread for (int thread_id = 0; thread_id < n_threads; ++thread_id){ double value; alfasim_sdk_api.get_plugin_input_data_quantity( ctx, &value, plugin_id, thread_id); void* data = new double(value); alfasim_sdk_api.set_plugin_data( ctx, plugin_id, data, thread_id); } return OK; }
It is important to note that void* data at line 22 could be a more complex data structure, like a class object for example.
void* data
As can be seen in the example above the function set_plugin_data() is used to tell the ALFAsim’s solver to hold the plugin internal data.
set_plugin_data()
Since ALFAsim’s solver uses multi-threading to perform all possible parallelizable calculation, it is important that the plugins provide internal data to each thread to avoid data access concurrency problems. As can be seen the HOOK_INITIALIZE example above, a for-loop is performed over the threads to set the plugin internal data. The ALFAsim-SDK API function get_number_of_threads() is used to do it properly. See Plugin Internal Data section for more information.
for-loop
get_number_of_threads()
finalize
c++ signature : HOOK_FINALIZE(void* ctx)
HOOK_FINALIZE(void* ctx)
This Hook must be used to delete all plugin internal data and unload the ALFAsim-SDK API. Otherwise, a memory leak could occur from your plugin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
HOOK_FINALIZE(ctx) { // Threads information int n_threads = -1; int errcode = alfasim.get_number_of_threads(ctx, &n_threads); f (errcode != 0){ // or errcode != OK return errcode; } const char* plugin_id = get_plugin_id(); // Plugin internal data for (int thread_id = 0; thread_id < n_threads; ++thread_id) { void* data = nullptr; errcode = alfasim.get_plugin_data( ctx, &data, plugin_id, thread_id); delete data; } // Unloading ALFAsim-SDK API int load_error_code = alfasim_sdk_close(alfasim_sdk_api) if (load_error_code != 0){ return load_error_code; } return OK; }
Where OK is an error_code value.
As can be seen in the example above the function get_plugin_data() is used to retrieved the plugin internal data for each thread identified as thread_id.
get_plugin_data()
thread_id
In the examples of usage of both HOOK_INITIALIZE and HOOK_FINALIZE there are many times where an error code is returned (return errcode; or return OK;). As can be seen, it is possible to return error codes from ALFAsim-SDK API functions, however the developer can intercept this error code and handle it instead of returning it to the ALFAsim’s Solver.
return errcode;
return OK;
The hooks described in this section are related to plugin secondary variables that were registered in the python config file, as already explained in Solver Configuration section. They are called secondary variables because they are not obtained from ALFAsim’s Solver, these ones are called primary variables and examples of those variables are pressure, temperature, volume fractions and velocities.
Using the following hooks the plugin is able to calculate/update those variables in three different moments of the simulation step. Two of them are called in the Hydrodynamic Solver scope and the last one is called in the Tracers Solver scope as illustrated on Hydrodynamic Solver and Tracer Solver workflow section. Once the solver obtain results for primary variables, it updates all secondary variables in which depend on primary variables. After that, HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES is called, but if it is running the first time step HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_FIRST_TIMESTEP is called before. It is necessary because usually during the first time step some initialization tasks are needed. Then, if the plugin needs to initialize with some value that is different from the initial nan value, this hook is the place to do that.
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_FIRST_TIMESTEP
nan
Different from plugin internal data, the secondary variables registered by plugins are allocated, deallocated and held by ALFAsim’s Solver. It is necessary because the variables arrays are dependent on network with its discretization and on hydrodynamic model, which defines the fluid flow’s phases, fields and layers.
update_plugins_secondary_variables
c++ signature : HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES(void* ctx)
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES(void* ctx)
Internal simulator hook to update plugin’s secondary variables. This is called as the last step on ALFAsim’s update internal secondary variables workflow.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES(ctx) { int errcode = -1; int size_U = -1; int size_E = -1; int liq_id = -1; errcode = alfasim_sdk_api.get_field_id( ctx, &oil_id, "oil"); double* vel; VariableScope Fields_OnFaces = { GridScope::FACE, MultiFieldDescriptionScope::FIELD, TimestepScope::CURRENT } errcode = alfasim_sdk_api.get_simulation_array( ctx, &vel, (char*) "U", Fields_OnFaces, liq_id, &size_U); double* kinetic_energy; char* name = "kinetic_energy_of_oil"; int global_idx = 0; errcode = alfasim_sdk_api.get_plugin_variable( ctx, (void**) (&kinetic_energy), name, global_idx, TimestepScope::CURRENT, &size_E); if (size_U != size_E){ return OUT_OF_BOUNDS; } for (int i =0; i < size_U; ++i){ kinetic_energy[i] = vel[i] * vel[i] / 2.; } return OK; }
In the example above the variable kinetic_energy_of_oil was registered as a global variable, but its value is obtained for oil field. If this variable would be calculated to all fields then the global_idx would be substituted by field_idx and it would be performed to each field.
kinetic_energy_of_oil
global_idx
field_idx
update_plugins_secondary_variables_on_first_timestep
c++ signature : HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_FIRST_TIMESTEP(void* ctx)
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_FIRST_TIMESTEP(void* ctx)
Internal simulator hook to update plugin’s secondary variables on the first timestep. This is called as the first step on ALFAsim’s update internal variables workflow. This method is specially important when you have a plugin which the secondary variables depend on old values. In the first timestep, there is no old values, so you may use this hook to initialize your variables contents.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_FIRST_TIMESTEP(ctx) { int errcode = -1; int size_E = -1; double* kinetic_energy; char* name = "kinetic_energy_of_oil"; int global_idx = 0; errcode = alfasim_sdk_api.get_plugin_variable( ctx, (void**) (&kinetic_energy), name, global_idx, TimestepScope::CURRENT, &size_E); for (int i =0; i < size_U; ++i){ kinetic_energy[i] = 0.0; } return OK; }
The ALFAsim’s Solver is divided in two non-linear solvers solvers that will solve different group of equations. The first one is the hydrodynamic solver which solves the Mass Conservation of fields, Momentum Conservation of layers and Energy Conservation equations all together for all elements in the network. The second one is the Tracer Solver which solves the Mass Conservation Equation for each added tracer. Since the tracers mass conservation is a transport equation it is solved after hydrodynamic solver and using its results (such as velocities) as input data in the Tracer Solver. See the ALFAsim’s Technical Report for more information.
To complete the group of hooks related to the plugins secondary variables there is the HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_TRACER_SOLVER. This hook is used to update plugin’s variables that depends on Tracer’s mass fractions and has to be updated in the Tracer Solver scope.
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_TRACER_SOLVER
update_plugins_secondary_variables_on_tracer_solver
c++ signature : HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_TRACER_SOLVER(void* ctx)
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_TRACER_SOLVER(void* ctx)
Internal simulator hook to update plugin’s secondary variables in the Tracer Solver scope. Tracer Solver is used to solve the tracer transport equation. This is called as the last step on ALFAsim’s Tracer Solver update variables workflow.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_TRACER_SOLVER(ctx) { const char* plugin_id = get_plugin_id() int errcode = -1; int size_t = -1; int size_p_var = -1; int liq_id = -1; errcode = alfasim_sdk_api.get_field_id( ctx, &oil_id, "oil"); double* tracer_mass_fraction; VariableScope global_OnCenters = { GridScope::FACE, MultiFieldDescriptionScope::FIELD, TimestepScope::CURRENT } // Tracer information void* tracer_ref; errcode = alfasim_sdk_api.get_tracer_ref_by_name( ctx, &tracer_ref, "my_tracer", // Added by User interface plugin_id); int tracer_id = -1; errcode = alfasim_sdk_api.get_tracer_id( ctx, &tracer_id, tracer_ref); double *tracer_mass_fraction errcode = alfasim_sdk_api.get_simulation_tracer_array( ctx, &tracer_mass_fraction, (char*) "phi", global_OnCenters, tracer_id, 0, // GLOBAL &size_t); // Plugin secondary variable array double* plugin_var; errcode = alfasim_sdk_api.get_plugin_variable( ctx, (void**) (&plugin_var), name, 0, // GLOBAL TimestepScope::CURRENT, &size_p_var); if (size_t != size_p_var){ return OUT_OF_BOUNDS; } for (int i =0; i < size_t; ++i){ // Do some calculations with plugin_var // using tracer_mass_fraction values } return OK; }
Note that functions like get_tracer_ref_by_name(), get_tracer_id() and get_simulation_tracer_array() were used to obtain information related to tracers.
get_tracer_ref_by_name()
get_tracer_id()
get_simulation_tracer_array()
It is important that the plugin developer take care of registered plugin’s secondary variables, since their values will be set equal to nan at first place and it will not be changed by ALFAsim’s Solver at any time during the simulation.
The hooks showed in this section can be considered as the most important. Since they allow the plugin to change the conservation equations. This is achieved by adding source terms in the residual function of mass, momentum and energy conservation equations. Since the equations are in residual form, the negative values of source terms indicate that mass, momentum and energy will be consumed. Otherwise, some amount of mass, momentum, and energy will be generated.
calculate_mass_source_term
c++ signature : HOOK_CALCULATE_MASS_SOURCE_TERM(void* ctx, void* mass_source, int n_fields, int n_control_volumes)
HOOK_CALCULATE_MASS_SOURCE_TERM(void* ctx, void* mass_source, int n_fields, int n_control_volumes)
Internal simulator hook to calculate source terms of mass equation. This is called after all residual functions are evaluated.
The plugin is supposed to change the given mass_source array pointer. Its values are contiguous in memory and the dimensions are given by n_fields and n_control_volumes. It has unit equal to [kg/s].
mass_source
n_fields
n_control_volumes
[kg/s]
mass_source – Source term of mass equation
n_fields – Number of fields
n_control_volumes – Number of control volumes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
HOOK_CALCULATE_MASS_SOURCE_TERM( ctx, mass_source, n_fields, n_control_volumes) { int liq_id = -1; errcode = alfasim_sdk_api.get_field_id( ctx, &oil_id, "oil"); // Convertion from void* to double* and getting the // array range related to oil field double* oil_mass_source = (double*) mass_source + n_control_volumes * liq_id; // Make some calculations and add it to oil_mass_source. // In this example, we add a mass source of 3.1415 kg/s to all control volumes. for (int i = 0; i < n_control_volumes; ++i) { oil_mass_source[i] = 3.1415; // [kg/s] } return OK; }
In the example above is shown how to manage the mass_source array to get the mass source term array related to a specific field (oil field in this case). Note that oil_mass_source has size equal to n_control_volumes.
oil_mass_source
calculate_momentum_source_term
c++ signature : HOOK_CALCULATE_MOMENTUM_SOURCE_TERM(void* ctx, void* momentum_source, int n_layers, int n_faces)
HOOK_CALCULATE_MOMENTUM_SOURCE_TERM(void* ctx, void* momentum_source, int n_layers, int n_faces)
Internal simulator hook to calculate source terms of momentum equation. This is called after all residual functions are evaluated.
The plugin is supposed to change the given momentum_source array pointer. Its values is contiguous in memory and the dimensions are given by n_layers and n_faces. It has unit equal to [N].
momentum_source
n_layers
n_faces
[N]
momentum_source – Source term of momentum equation
n_layers – Number of layers
n_faces – Number of faces (equal to n_control_volumes minus 1)
1 2 3 4 5 6 7 8 9 10 11 12 13
HOOK_CALCULATE_MOMENTUM_SOURCE_TERM( ctx, momentum_source, n_layers, n_faces) { int gas_id = -1; errcode = alfasim_sdk_api.get_layer_id( ctx, &gas_id, "gas"); // Convertion from void* to double* and getting the // array range related to gas layer double* gas_momentum_source = (double*) momentum_source + n_faces * gas_id; // Make some calculations and add it to gas_momentum_source return OK; }
In the example above is shown how to manage the momentum_source array to get the momentum source term array related to a specific layer (gas layer in this case). Note that gas_momentum_source has size equal to n_faces.
gas_momentum_source
calculate_energy_source_term
c++ signature : HOOK_CALCULATE_ENERGY_SOURCE_TERM(void* ctx, void* energy_source, int n_energy_equation, int n_control_volumes)
HOOK_CALCULATE_ENERGY_SOURCE_TERM(void* ctx, void* energy_source, int n_energy_equation, int n_control_volumes)
Internal simulator hook to calculate source terms of energy equation. This is called after all residual functions are evaluated.
The plugin is supposed to change the given energy_source array pointer. Its values is contiguous in memory and the dimensions are given by n_energy_equation and n_control_volumes. It has unit equal to [J/s].
energy_source
n_energy_equation
[J/s]
Since ALFAsim considers two energy models, if n_energy_equation is equal to 1 it means that the global energy model is being used. Otherwise the layers energy model is being used. See the ALFAsim’s Technical Report for more information about the equations system.
ALFAsim
energy_source – Source term of energy equation
n_energy_equation – Number of Energy Equation being solved
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
HOOK_CALCULATE_ENERGY_SOURCE_TERM( ctx, energy_source, n_energy_equation, n_control_volumes) { double* gas_energy_source; if (n_energy_equation > 1){ // Layers Energy model // One energy equation for each layer int gas_id = -1; errcode = alfasim_sdk_api.get_layer_id( ctx, &gas_id, "gas"); // Convertion from void* to double* and getting the // array range related to gas layer gas_energy_source = (double*) energy_source + n_faces * gas_id; } else { // Global energy model // Only one global energy equation // Convertion from void* to double* gas_energy_source = (double*) energy_source; } // Make some calculations and add it to gas_energy_source return OK; }
In the example above is shown how to manage the energy_source array to get the energy source term array related to a specific layer (gas layer in this case). Note that gas_energy_source has size equal to n_control_volumes.
gas_energy_source
calculate_tracer_source_term
c++ signature : HOOK_CALCULATE_TRACER_SOURCE_TERM(void* ctx, void* phi_source, int n_tracers, int n_control_volumes)
HOOK_CALCULATE_TRACER_SOURCE_TERM(void* ctx, void* phi_source, int n_tracers, int n_control_volumes)
Internal simulator hook to calculate source terms of tracer transport equation. This is called after all residual functions are evaluated.
The plugin is supposed to change the given phi_source array pointer. Its values is contiguous in memory and the dimensions are given by n_tracers and n_control_volumes. It has unit equal to [kg/s].
phi_source
n_tracers
phi_source – Source term of tracers mass equation
n_tracers – Number of tracers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
HOOK_CALCULATE_TRACER_SOURCE_TERM( ctx, phi_source, n_tracers, n_control_volumes) { // Tracer information void* tracer_ref; errcode = alfasim_sdk_api.get_tracer_ref_by_name( ctx, &tracer_ref, "my_tracer", // Added by User interface plugin_id); int tracer_id = -1; errcode = alfasim_sdk_api.get_tracer_id( ctx, &tracer_id, tracer_ref); // Convertion from void* to double* and getting the // array range related to gas layer double* my_tracer_phi_source = (double*) phi_source + n_control_volumes * tracer_id; // Make some calculations and add // it to my_tracer_phi_source return OK; }
In the example above is shown how to manage the phi_source array to get the tracer source term array related to a specific tracer (my_tracer in this case). Note that gas_energy_source has size equal to n_control_volumes.
As can be seen in Hydrodynamic Model section the plugins can add new fields, phases and layers. Also, it is possible to indicate if a phase will have its state variables calculated from plugin implementing the alfasim_get_phase_properties_calculated_from_plugin().
alfasim_get_phase_properties_calculated_from_plugin()
initialize_state_variables_calculator
c++ signature : HOOK_INITIALIZE_STATE_VARIABLES_CALCULATOR(void* ctx, void* P, void* T, void* T_mix, int n_control_volumes, int n_layers)
HOOK_INITIALIZE_STATE_VARIABLES_CALCULATOR(void* ctx, void* P, void* T, void* T_mix, int n_control_volumes, int n_layers)
Hook for the state variables calculator initialization (internal ALFAsim structure).
At this point, it is possible to pre-calculate and cache any relevant information. Then, for each state variable of the phases in the python configuration file, the hook HOOK_CALCULATE_STATE_VARIABLE is called and return the pre-calculated values.
P – Pressure values array
T – Temperature values array
T_mix – Mixture temperature values array
The P and T_mix have size equal to n_control_volumes. However, T has values contiguous in memory and its dimensions are given by n_layers and n_control_volumes
P
T_mix
T
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
HOOK_INITIALIZE_STATE_VARIABLES_CALCULATOR( void* ctx, void* P, void* T, void* T_mix, int n_control_volumes, int n_layers) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } // MyStruct is a developer defined struct to hold // all important information for plugin hooks. MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); // MyFunction is a function implemented by // plugin developer that computes de density data.density = MyFunction(P, T_mix, n_control_volumes); return OK; } // Then, to use the cached value: HOOK_CALCULATE_STATE_VARIABLE( void* ctx, void* P, void* T, int n_control_volumes, i nt n_layers, int phase_id, int property_id, void* output) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); if (phase_id != data.my_added_phase_id) { return OK; } if (property_id == StateVariable::RHO) { for (int i = 0; i < n_control_volumes; ++i) { output[i] = data.density[i]; } } return OK; }
For pre-calculated values, the plugin developer must cache it in the plugin internal data. See the example above.
However, if the state variable is considered constant or the developer doesn’t need to cache the values, just return OK.
calculate_state_variable
c++ signature : HOOK_CALCULATE_STATE_VARIABLE(void* ctx, void* P, void* T, int n_control_volumes, int n_layers, int phase_id, int property_id, void* output)
HOOK_CALCULATE_STATE_VARIABLE(void* ctx, void* P, void* T, int n_control_volumes, int n_layers, int phase_id, int property_id, void* output)
Hook to calculate the state variable given by the property_id (See StateVariable values), for the phase with phase_id (Note that the phase id is the same as the one retrieved from the get_phase_id() API function - It is not advisable to use hardcoded numbers).
StateVariable
get_phase_id()
List of affected variables: - Density - Viscosity - Heat Capacity - Partial Derivative of Density in Relation to Pressure - Partial Derivative of Density in Relation to Temperature - Enthalpy - Thermal Conductivity
Density
Viscosity
Heat Capacity
Partial Derivative of Density in Relation to Pressure
Partial Derivative of Density in Relation to Temperature
Enthalpy
Thermal Conductivity
n_phase_id – Id of phase in which the property must be calculated
property_id – A StateVariable value. It indicates which property must be calculated
output – Output values array
The output parameter must be filled with the calculated property for each control volume. The pressure P and layer or mixture temperature T (Depending on the energy model being used) are given in order to perform the calculation. The number of control volumes is also given for convenience.
output
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
HOOK_CALCULATE_STATE_VARIABLE( void* ctx, void* P, void* T, int n_control_volumes, int n_layers, int phase_id, int property_id, void* output) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); if (phase_id != data.my_added_phase_id) { return OK; } if (property_id == StateVariable::RHO) { for (int i = 0; i < n_control_volumes; ++i) { // If the property has a constant value output[i] = data.constant_density; // If the property must be computed // MyStruct has a function called 'compute_density()' output[i] = data.compute_density( (double *)P[i], (double *)T[i]); } } return OK; }
The plugin developer must NOT change any variable other than the output. The output size is n_control_volumes .
calculate_phase_pair_state_variable
c++ signature : HOOK_CALCULATE_PHASE_PAIR_STATE_VARIABLE(void* ctx, void* P, void* T_mix, int n_control_volumes, int phase1_id, int phase2_id, int property_id, void* output)
HOOK_CALCULATE_PHASE_PAIR_STATE_VARIABLE(void* ctx, void* P, void* T_mix, int n_control_volumes, int phase1_id, int phase2_id, int property_id, void* output)
Hook to calculate the state variable given by the property_id (See :cpp:enum:StateVariable values), for the phase pair (phase1_id, phase2_id) (Note that the phase id is the same as the one retrieved from the get_phase_id() API function - It is not advisable to use hardcoded numbers).
List of affected variables: - Interfacial Tension
Interfacial Tension
n_phase1_id – Id of phase one in which the property must be calculated
n_phase2_id – Id of phase two in which the property must be calculated
The output parameter must be filled with the calculated property for each control volume. The pressure P and mixture temperature T_mix are given in order to perform the calculation. The number of control volumes is also given for convenience.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
HOOK_CALCULATE_PHASE_PAIR_STATE_VARIABLE( void* ctx, void* P, void* T_mix, int n_control_volumes, int phase1_id, int phase2_id, int property_id, void* output) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); int gas_phase_id = -1; errcode = alfasim_sdk_api.get_phase_id( ctx, &gas_phase_id, "gas"); if (( (phase1_id == data.my_added_phase_id) && (phase1_id == gas_phase_id) ) || ( (phase1_id == gas_phase_id) && (phase1_id == data.my_added_phase_id) )) { for (int i = 0; i < n_control_volumes; ++i) { // If the property has a constant value output[i] = data.constant_surface_tension; // If the property must be computed // MyStruct has a function // called 'compute_surface_tension()' output[i] = data.compute_surface_tension( (double *)P[i], (double *)T_mix[i]); } } return OK; }
The plugin developer must NOT change any variable other than the output. The output size is n_control_volumes.
finalize_state_variables_calculator
c++ signature : HOOK_FINALIZE_STATE_VARIABLES_CALCULATOR(void* ctx)
HOOK_FINALIZE_STATE_VARIABLES_CALCULATOR(void* ctx)
Hook for the state variables calculator finalization. The plugin developer should free/delete any allocated data from the HOOK_INITIALIZE_STATE_VARIABLE_CALCULATOR.
If there is no need memory deallocation a minimal implementation would be:
1 2 3 4
HOOK_FINALIZE_STATE_VARIABLES_CALCULATOR(void* ctx) { return OK; }
When a new phase is added to the hydrodynamic model using the AddPhase type, it is possible to set as a solid phase. In this case, the particle size of fields that are solid phase can be calculated by implementing the following Solver Hooks.Otherwise, the particle size will be considered constant and equal to \(1\times10^{-4}\) meters.
initialize_particle_diameter_of_solids_fields
c++ signature : HOOK_INITIALIZE_PARTICLE_DIAMETER_OF_SOLIDS_FIELDS(void* ctx, void* particle_diameter, int n_control_volumes, int solids_field_id)
HOOK_INITIALIZE_PARTICLE_DIAMETER_OF_SOLIDS_FIELDS(void* ctx, void* particle_diameter, int n_control_volumes, int solids_field_id)
Internal simulator hook to initialize particle diameter of solid fields. This hook follows the same idea of HOOK_UPDATE_PLUGIN_SECONDARY_VARIABLES_ON_FIRST_TIMESTEP, which makes the initialization in the moment that there is no previous time step data available.
HOOK_UPDATE_PLUGIN_SECONDARY_VARIABLES_ON_FIRST_TIMESTEP
particle_diameter – Particle diameter of a given solid field,
n_control_volumes – Number of control volumes,
solid_field_id – Index of the solid field in which the particle_diameter Should be calculated.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
HOOK_INITIALIZE_PARTICLE_DIAMETER_OF_SOLIDS_FIELDS( void* ctx, void* particle_diameter, int n_control_volumes, int solids_field_id) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); if (solids_field_id != data.my_added_solid_field_id) { return OK; } else { for (int i = 0; i < n_control_volumes; ++i) { // If the particle size is constant output[i] = data.constant_particle_size; // The value is calculated // MyStruct has a function // called 'initial_particle_size()' output[i] = data.initial_particle_size( // List of params that can be // retrieved by get_simulation_array() ); } } return OK; }
update_particle_diameter_of_solids_fields
c++ signature : HOOK_UPDATE_PARTICLE_DIAMETER_OF_SOLIDS_FIELDS(void* ctx, void* particle_diameter, int n_control_volumes, int solids_field_id)
HOOK_UPDATE_PARTICLE_DIAMETER_OF_SOLIDS_FIELDS(void* ctx, void* particle_diameter, int n_control_volumes, int solids_field_id)
Internal simulator hook to update/calculate particle diameter of solid fields. It is called right before any update secondary variable from ALFAsim’s Solver, because they may depend on the solids particle size (for example Slurry Viscosity calculated by Solids Model)
Solids Model
HOOK_UPDATE_PARTICLE_DIAMETER_OF_SOLIDS_FIELDS( void* ctx, void* particle_diameter, int n_control_volumes, int solids_field_id) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); if (solids_field_id != data.my_added_solid_field_id) { return OK; } else { for (int i = 0; i < n_control_volumes; ++i) { // If the particle size is constant output[i] = data.constant_particle_size; // The value is calculated // MyStruct has a function // called 'compute_particle_size()' output[i] = data.compute_particle_size( // List of params that can be // retrieved by get_simulation_array() ); } } return OK; }
When a new phase is added to the hydrodynamic model using the AddPhase type, it is possible to consider the deposition inside the pipeline walls. If so, calculate the thickness of the deposited layer on a given phase through the usage of update_internal_deposition_layer(). By default, the layer thickness will be considered equal to zero meters.
update_internal_deposition_layer()
update_internal_deposition_layer
c++ signature : HOOK_UPDATE_INTERNAL_DEPOSITION_LAYER(void* ctx, void* thickness, void* density, void* heat_capacity, void* thermal_conductivity, int n_control_volumes)
HOOK_UPDATE_INTERNAL_DEPOSITION_LAYER(void* ctx, void* thickness, void* density, void* heat_capacity, void* thermal_conductivity, int n_control_volumes)
Internal simulator hook to evaluate the thickness and thermal properties of the deposited layer at the inside of the pipeline walls. This is called for accounting the diameter reduction and wall thermal effects.
The plugin is supposed to change the given thickness, density, heat_capacity, thermal_conductivity array pointers. Its values are contiguous in memory and the dimension is given by n_control_volumes. It has unit equal to [m].
thickness
density
heat_capacity
thermal_conductivity
[m]
thickness – Thickness of the internal deposit layer
density – Density of the internal deposit layer [kg/m3]
heat_capacity – Heat capacity of the internal deposit layer [J/(kg.K)]
thermal_conductivity – Thermal conductivity of the internal deposit layer [W/(m.K)]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
HOOK_UPDATE_INTERNAL_DEPOSITION_LAYER( ctx, thickness, density, heat_capacity, thermal_conductivity, n_control_volumes) { auto errcode = -1; double dt = -1.0; errcode = alfasim.get_simulation_quantity( ctx, &dt, TimestepScope::CURRENT, (char*) "dt"); if (errcode != 0) { return errcode; } // Handle first time step, because you won't have the previously information double current_time = -1.0; errcode = alfasim.get_simulation_quantity( ctx, ¤t_time, TimestepScope::CURRENT, (char*) "time"); if (errcode != 0) { return errcode; } double wax_density = 900.0 // [kg/m3] double wax_heat_capacity = 2140.0 // [J/(kg.K)] double wax_thermal_conductivity = 0.25 // [W/(m.K)] if (current_time == 0.0){ // Set a value for the deposition layer thickness for (int i = 0; i < n_control_volumes; ++i) { (double*) thickness[i] = 0.0; // [m] (double*) density[i] = wax_density; // [kg/m3] (double*) heat_capacity[i] = wax_heat_capacity; // [J/(kg.K)] (double*) thermal_conductivity[i] = wax_thermal_conductivity; // [W/(m.K)] } } else{ // Get previously deposition layer thickness to obtain the current void* thickness_old_raw_ptr; errcode = alfasim.get_plugin_variable( ctx, &thickness_old_raw_ptr, "thickness", 0, TimestepScope::PREVIOUS, &size); if (errcode != 0) { return errcode; } auto* thickness_old = (double*) (thickness_old_raw_ptr); // Calculate the variation of the deposition layer in one time step double* d_deposit_layer_dt = 0.0001; // [m/s] // Sum this variation with the thickness of the older time step for (int i = 0; i < n_control_volumes; ++i) { (double*) thickness[i] = thickness_old[i] + d_deposit_layer_dt * dt; // [m] (double*) density[i] = wax_density; // [kg/m3] (double*) heat_capacity[i] = wax_heat_capacity; // [J/(kg.K)] (double*) thermal_conductivity[i] = wax_thermal_conductivity; // [W/(m.K)] } } return OK; }
In the example above is shown how to manage the thickness, density, heat_capacity and thermal_conductivity arrays for each control volume. Note that the thickness should be always the total value for that time step, so the first time step should be handle in a separately way, since there is no previously information.
When the Unit Cell Model is used in ALFAsim simulation any plugin can implement your own friction factor correlation. For that, two hooks MUST be implemented, one for stratified flow and one for annular flow. Both of them must be implemented because the ALFAsim’s Solver will call them depending on which flow pattern the fluid flow is in the control volume.
It is important to know that the main input variables needed to compute the friction factor is available in the API function get_ucm_friction_factor_input_variable(). Note that, the variables listed in the documentation of the cited function are related to one control volume, in which the Unit Cell Model is applied.
get_ucm_friction_factor_input_variable()
calculate_ucm_friction_factor_stratified
c++ signature : HOOK_CALCULATE_UCM_FRICTION_FACTOR_STRATIFIED(void* ctx, double* ff_wG, double* ff_wL, double* ff_i)
HOOK_CALCULATE_UCM_FRICTION_FACTOR_STRATIFIED(void* ctx, double* ff_wG, double* ff_wL, double* ff_i)
Internal unit cell model hook to calculate the wall and interfacial friction factors for stratified fluid flow pattern. The unit cell model represents a two phase flow with Gas and Liquid Phases. The output variables ff_wG, ff_wL and ff_i are the Gas-Wall friction factor, Liquid-Wall friction factor and interfacial Gas-Liquid friction factor, respectively.
ff_wG
ff_wL
ff_i
This hook allows the developer to implement your own correlation for friction factor in a stratified flow.
ff_wG – Gas-Wall Friction Factor
ff_wL – Liquid-Wall Friction Factor
ff_i – Interfacial Gas-Liquid Friction Factor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
int HOOK_CALCULATE_UCM_FRICTION_FACTOR_STRATIFIED( void* ctx, double* ff_wG, double* ff_wL, double* ff_i) { int errcode = -1; int G = TwoPhaseSystem::GAS int L = TwoPhaseSystem::LIQUID // Getting friction factor input data from context double alpha[2]; errcode = alfasim_sdk_api.get_ucm_friction_factor_input_variable( ctx, &alpha[G], "alpha", TwoPhaseSystem::GAS); if (errcode != OK){ return errcode; } errcode = alfasim_plugins_api.get_ucm_friction_factor_input_variable( ctx, &alpha[L], "alpha", TwoPhaseSystem::LIQUID); if (errcode != OK){ return errcode; } // And so on to each friction factor input variable // U(velocities), rho(densities), mu(viscosities) and D(pipe diameter) // Getting the fluid geometrical properties double S_w[2]; double S_i; double H[2]; errcode = alfasim_sdk_api.get_ucm_fluid_geometrical_properties( ctx, S_w, &S_i, H, alpha[G], D); if (errcode != OK){ return errcode; } // Compute the friction factors using your own correlation. // Also, using the variables: alpha, U, rho, mu, D, S_w, S_i and H *ff_wG = gas_wall_ff; *ff_wL = liq_wall_ff; *ff_i = gas_liq_ff; return OK; }
calculate_ucm_friction_factor_annular
c++ signature : HOOK_CALCULATE_UCM_FRICTION_FACTOR_ANNULAR(void* ctx, double* ff_wG, double* ff_wL, double* ff_i)
HOOK_CALCULATE_UCM_FRICTION_FACTOR_ANNULAR(void* ctx, double* ff_wG, double* ff_wL, double* ff_i)
Internal unit cell model hook to calculate the wall and interfacial friction factors for annular fluid flow pattern. The unit cell model represents a two phase flow with Gas and Liquid Phases. The output variables ff_wG, ff_wL and ff_i are the Gas-Wall friction factor, Liquid-Wall friction factor and interfacial Gas-Liquid friction factor, respectively.
This hook allows the developer to implement your own correlation for friction factor in a annular flow.
The same example presented in HOOK_CALCULATE_UCM_FRICTION_FACTOR_STRATIFIED can be used, just change the hook name to HOOK_CALCULATE_UCM_FRICTION_FACTOR_ANNULAR.
HOOK_CALCULATE_UCM_FRICTION_FACTOR_STRATIFIED
Another important API function for UCM is get_ucm_fluid_geometrical_properties(). This function computes the geometrical properties properly in each previous presented hooks depending on the flow pattern.
get_ucm_fluid_geometrical_properties()
The hooks described in this section must be implemented when at least one user defined tracer is added via alfasim_get_user_defined_tracers_from_plugin() hook.
alfasim_get_user_defined_tracers_from_plugin()
These hooks can modify the tracer transport equation from the initialization to configuration of boundary conditions. The plugin developer has complete freedom to change the equations, however it is important to be aware that it can be made manipulating the transport equation terms inside the hooks. For that, it is important to read the Tracers Chapter at ALFAsim’s Technical Manual.
initialize_mass_fraction_of_tracer
c++ signature : HOOK_INITIALIZE_MASS_FRACTION_OF_TRACER(void* ctx, void* phi_initial, int tracer_index)
HOOK_INITIALIZE_MASS_FRACTION_OF_TRACER(void* ctx, void* phi_initial, int tracer_index)
Internal tracer model Hook to initialize the mass fraction of tracer, given by tracer_id, in the entire network. The output variable phi_initial is the initial mass fraction of the given tracer in respect to the mass of the mixture.
phi_initial – Initial mass fraction of tracer in respect to the mass of the mixture
tracer_index – Tracer ID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
HOOK_INITIALIZE_MASS_FRACTION_OF_TRACER( void* ctx, void* phi_initial, int tracer_index) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); if (tracer_index != data.my_user_defined_tracer_id) { return OK; } else { // Set a initial value to the tracer mass fraction // phi_initial has size equal to 1 *static_cast<double*>(phi_initial) = 0.0; } return OK; }
calculate_mass_fraction_of_tracer_in_phase
c++ signature : HOOK_CALCULATE_MASS_FRACTION_OF_TRACER_IN_PHASE(void* ctx, void* phi, void* phi_phase, int tracer_index, int phase_index, int n_control_volumes)
HOOK_CALCULATE_MASS_FRACTION_OF_TRACER_IN_PHASE(void* ctx, void* phi, void* phi_phase, int tracer_index, int phase_index, int n_control_volumes)
Internal tracer model Hook to calculate the mass fraction of tracer, given by tracer_index, in phase, given by phase_index. The input variable phi is the mass fraction of the given tracer in respect to the mass of the mixture. The output variable phi_phase is the mass fraction of the given tracer in respect to the mass of the given phase. Both phi and phi_phase have size equal to n_control_volumes.
tracer_index
phase_index
phi
phi_phase
phi – Array of mass fraction of tracer in respect to the mass of the mixture
phi_phase – Array of mass fraction of tracer in respect to the mass of the phase given by phase_index
phase_index – Phase ID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
HOOK_CALCULATE_MASS_FRACTION_OF_TRACER_IN_PHASE( void* ctx, void* phi, void* phi_phase, int tracer_index, int phase_index, int n_control_volumes) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); // Casting to double pointer double* phi_ptr = static_cast<double*>(phi); double* phi_phase_ptr = static_cast<double*>(phi_phase); // Check if the tracer_index was added by this plugin if (tracer_index != data.my_user_defined_tracer_id){ return OK } // Let suppose that this tracer is only in the gas phase if(phase_index != data.gas_id) { for (int i = 0; i < n_control_volumes; ++i) { phi_phase_ptr[i] = 0.0; } } else { // Calculate and set the Phi_phase value for (int i = 0; i < n_control_volumes; ++i) { phi_phase_ptr[i] = data.calculate_mass_fraction_of_tracer_in_gas( phi_ptr[i], // List of params that can be // retrieved by get_simulation_array() ); } } return OK; }
The plugin developer must NOT change phi variable, only the output variable phi_phase.
calculate_mass_fraction_of_tracer_in_field
c++ signature : HOOK_CALCULATE_MASS_FRACTION_OF_TRACER_IN_FIELD(void* ctx, void* phi_phase, void* phi_field, int tracer_index, int field_index, int phase_index_of_field, int n_control_volumes)
HOOK_CALCULATE_MASS_FRACTION_OF_TRACER_IN_FIELD(void* ctx, void* phi_phase, void* phi_field, int tracer_index, int field_index, int phase_index_of_field, int n_control_volumes)
Internal tracer model Hook to calculate the mass fraction of tracer, given by tracer_index, in field, given by field_index. The input variable phi_phase is the mass fraction of the given tracer in respect to the mass of the given phase, in which the ID is phase_index_of_field. The output variable phi_field is the mass fraction of the given tracer in respect to the mass of the given field. Both phi_phase and phi_field have size equal to n_control_volumes.
field_index
phi_field
phi_phase – Array of mass fraction of tracer in respect to the mass of the phase given by phase_index_of_field
phi_field – Array of mass fraction of tracer in respect to the mass of the field given by field_index
field_index – Field ID
phase_index_of_field – Phase ID of field
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
HOOK_CALCULATE_MASS_FRACTION_OF_TRACER_IN_FIELD( void* ctx, void* phi_phase, void* phi_field, int tracer_index, int field_index, int phase_index_of_field, int n_control_volumes) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); // Casting to double pointer double* phi_phase_ptr = static_cast<double*>(phi_phase); double* phi_field_ptr = static_cast<double*>(phi_field); // Check if the tracer_index was added by this plugin if (tracer_index != data.my_user_defined_tracer_id){ return OK } // Let suppose that this tracer is only in the gas phase if(phase_index_of_field != data.gas_phase_id) { for (int i = 0; i < n_control_volumes; ++i) { phi_field_ptr[i] = 0.0; } } else { // Calculate and set the Phi_field value for (int i = 0; i < n_control_volumes; ++i) { phi_field_ptr[i] = data.mass_fraction_of_tracer_in_gas_fields( phi_phase_ptr[i], // List of params that can be // retrieved by get_simulation_array() ); } } return OK; }
The plugin developer must NOT change phi_phase variable, only the output variable phi_field.
set_prescribed_boundary_condition_of_mass_fraction_of_tracer
c++ signature : HOOK_SET_PRESCRIBED_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER(void* ctx, void* phi_presc, int tracer_index)
HOOK_SET_PRESCRIBED_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER(void* ctx, void* phi_presc, int tracer_index)
Internal tracer model hook to set the initial prescribed boundary condition of mass fraction of tracer, given by tracer_index`. The output variable ``phi_presc is the prescribed mass fraction of the given tracer in respect to the mass of the mixture. Note that all boundary nodes will be populated with phi_presc value set by this hook.
tracer_index`. The output variable ``phi_presc
Please note that this hook sets the first mass fraction related boundary conditions value to the user defined tracer. However the hook HOOK_UPDATE_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER allows the plugin developer to update this value.
phi_presc – Prescribed mass fraction of tracer
HOOK_SET_PRESCRIBED_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER( void* ctx, void* phi_presc, int tracer_index) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); if (tracer_index != data.my_user_defined_tracer_id) { return OK; } else { // Set a initial boundary condition // to the tracer mass fraction. // phi_presc has size equal to 1. *static_cast<double*>(phi_presc) = 0.0; } return OK; }
update_boundary_condition_of_mass_fraction_of_tracer
c++ signature : HOOK_UPDATE_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER(void* ctx, void* phi_presc, void* phi_field, int tracer_index, void* vol_frac_bound, int n_fields)
HOOK_UPDATE_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER(void* ctx, void* phi_presc, void* phi_field, int tracer_index, void* vol_frac_bound, int n_fields)
Internal tracer model hook to update the prescribed mass fraction of tracer, given by tracer_id. The output variable phi_presc is the prescribed mass fraction of the given tracer in respect to the mass of the mixture. The vol_frac_bound is the volume fraction of fields at the boundary in which the phi_presc is being calculated.
tracer_id
phi_presc
vol_frac_bound
This hook allows the developer to update the boundary conditions of mass fraction that may depend on ALFAsim’s internal variables that may change during the simulation. So, this update will be performed to each time step.
vol_frac_bound – Volume fraction of fields in the boundary
HOOK_UPDATE_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER( void* ctx, void* phi_presc, int tracer_index) { // getting plugin internal data int errcode = -1; int thread_id = -1; errcode = alfasim_sdk_api.get_thread_id(ctx, &thread_id); if (errcode != OK) { return errcode; } MyStruct* data = nullptr; errcode = alfasim_sdk_api.get_plugin_data( ctx, (void**) &data, plugin_id, thread_id); // Casting to double pointer double* phi_presc_ptr = static_cast<double*>(phi_presc); double* vol_frac_bound_ptr = static_cast<double*>(vol_frac_bound); // Let suppose that this tracer is only in the gas field if (tracer_index != data.my_user_defined_tracer_id) { return OK; } else { // Update the boundary condition // to the tracer mass fraction. phi_presc_ptr = data.calc_bc_mass_fraction_of_tracer_in_gas_field( vol_frac_bound_ptr[data.continuous_gas_field_id], // List of params that can be // retrieved by get_simulation_array() ); } return OK; }
The plugin developer must NOT change vol_frac_bound variable, only the output variable phi_presc.
Dig deeper into specific topics:
Here are shown some graphs of the ALFAsim simulator workflow. In those graphs, it is possible to identify when the Solver Hooks are called during the simulation. Some of them, like Hydrodynamic Solver and Tracer Solver, have their graphs expanded to make it possible to see more internal hooks.
digraph { nodesep = 0.6; newrank=true; node [fillcolor="#FAAC2C", style="rounded, filled", color="#FAAC2C", fontcolor="#ffffff", target="_top"] edge [color="#8699A3", fontcolor="#2c3e50"] hydrodynamic_1 [label="Primary Variables \n (Solver Unknowns)"] hydrodynamic_2 [label="Calculate \n State Variables" URL="../07_workflow.html#state-variable-calculator"] hydrodynamic_3 [label="Calculate \n Secondary Variables"] hydrodynamic_4 [label="Calculate \n Source Terms"] invisible_init [shape=point, style=invis] invisible_end [shape=point, style=invis] hook_update_variables_point [shape = point, width = 0, peripheries="2"] hook_calculate_source_terms_point [shape = point, width = 0, peripheries="2" ] // Align Hooks node[peripheries="0", shape="cds", color="#DA5961", fontcolor="#DA5961", style=""] hook_update_variables [label="HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.update_plugins_secondary_variables"] hook_calculate_mass_source_terms [label="HOOK_CALCULATE_MASS_SOURCE_TERM", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.calculate_mass_source_term"] hook_calculate_momentum_source_terms [label="HOOK_CALCULATE_MOMENTUM_SOURCE_TERM", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.calculate_momentum_source_term"] hook_calculate_energy_source_terms [label="HOOK_CALCULATE_ENERGY_SOURCE_TERM", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.calculate_energy_source_term"] {rank = same; hook_update_variables_point; hook_update_variables } {rank = same; hook_calculate_source_terms_point; hook_calculate_momentum_source_terms; } invisible_init -> hydrodynamic_1 hook_calculate_source_terms_point -> invisible_end ; // Align all hooks hook_calculate_mass_source_terms -> hook_calculate_momentum_source_terms -> hook_calculate_energy_source_terms [constraint=true, style=invis] subgraph cluster1{ labeljust="l" style="rounded, dashed" color="#8699A3" hydrodynamic_1 -> hydrodynamic_2 -> hydrodynamic_3 hydrodynamic_3 -> hook_update_variables_point [arrowhead=none, ltail=cluster1] hook_update_variables_point -> hydrodynamic_4 hydrodynamic_4 -> hook_calculate_source_terms_point [arrowhead=none] } hook_update_variables_point -> hook_update_variables [constraint=false, style=dotted, color="#DA5961"] hook_calculate_source_terms_point -> hook_calculate_mass_source_terms:w [constraint=false, style=dotted, color="#DA5961"] hook_calculate_source_terms_point -> hook_calculate_momentum_source_terms [constraint=false, style=dotted, color="#DA5961"] hook_calculate_source_terms_point -> hook_calculate_energy_source_terms:w [constraint=false, style=dotted, color="#DA5961"] // Align Notes subgraph cluster2{ labeljust="l" style="rounded, dashed" fontcolor="#2c3e50" color="#8699A3" node[shape=box, color="#FAAC2C",fillcolor="#FFFFFF", fontcolor="#FAAC2C", peripheries="1" shape="rectangular"] note_1 [label="α, P, U, T"] note_2 [label="ρ, μ, Cₚ, ... = ƒ(P,T)"] note_3 [label="Mass Flow Rate, Flow Pattern ..."] } {rank=same; hydrodynamic_1; note_1} {rank=same; hydrodynamic_2; note_2} {rank=same; hydrodynamic_3; note_3} note_1->note_2->note_3[ style = invis ] edge[arrowhead=none, style=dashed, constraint=false,] hydrodynamic_1 -> note_1 hydrodynamic_2 -> note_2 hydrodynamic_3 -> note_3 }
digraph { newrank=true nodesep=0.7 node [fillcolor="#FAAC2C" style="rounded, filled" color="#FAAC2C" fontcolor="#ffffff" shape="rectangular"] edge [ color="#8699A3" fontcolor="#2c3e50" ] node_1 [label="Calculate State Variables \n for ALFAsim Phases"] node_2 [label="Setup"] node_3 [label="Calculate State Variables"] node_4 [label="Calculate Phase Pair \n State Variables \n (Surface Tension)"] node_5 [label="Finalize"] loop_1 [fixedsize=true, label="Phases Set \n By Plugins", width="1.2", shape="circle"] node [shape=point style=invis width=0] invisible_init invisible_end invisible_1 invisible_2 invisible_3 node[peripheries="0" shape="cds", color="#DA5961", fontcolor="#DA5961" , style="" target="_top" fontsize=10] hook_initialize_state_variables_calculator [label=" HOOK_INITIALIZE_STATE_VARIABLES_CALCULATOR" URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.initialize_state_variables_calculator"] hook_calculate_state_variable [label=" HOOK_CALCULATE_STATE_VARIABLE", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.calculate_state_variable"] hook_calculate_phase_pair_state_variable [label=" HOOK_CALCULATE_PHASE_PAIR_STATE_VARIABLE", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.calculate_phase_pair_state_variable"] hook_finalize_state_variables_calculator [label=" HOOK_FINALIZE_STATE_VARIABLES_CALCULATOR" URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.finalize_state_variables_calculator"] {rank = same node_2 hook_initialize_state_variables_calculator } {rank = same node_3 hook_calculate_state_variable } {rank = same node_4 hook_calculate_phase_pair_state_variable} {rank = same node_5 hook_finalize_state_variables_calculator } {rank=same loop_1 node_3} invisible_init -> node_1 subgraph cluster1{ label="" labeljust="l" style="rounded, dashed" color="#8699A3" node_2->node_3->node_4->node_5 [weight=9999] loop_1:ne -> node_2:w [style=dashed] node_5:w -> loop_1:se [style=dashed] invisible_1 -> loop_1 [weight=9999] edge[arrowhead=none style=normal] node_1 -> invisible_1 [weight=9999] loop_1 -> invisible_2 invisible_2 -> invisible_3 } invisible_3 -> invisible_end edge[constraint=false, style=dotted, color="#DA5961"] node_2 -> hook_initialize_state_variables_calculator node_3 -> hook_calculate_state_variable node_4 -> hook_calculate_phase_pair_state_variable node_5 -> hook_finalize_state_variables_calculator }
digraph { nodesep = 0.7 newrank=true node [fillcolor="#FAAC2C" style="rounded, filled" color="#FAAC2C" fontcolor="#ffffff" shape="rectangular"] edge [ color="#8699A3" fontcolor="#2c3e50" ] tracer_1 [label="Primary Variables \n (Solver Unknowns, ϕ) "] tracer_2 [label="Calculate \n Secondary Variables"] tracer_3 [label="Calculate \n Source Terms"] invisible_init [shape=point, style=invis] invisible_end [shape=point, style=invis] node[shape = point, width = 0, peripheries="2" ] hook_initialize_user_defined_tracer_point hook_set_bc_user_defined_tracer_point hook_update_variables_point hook_calculate_source_terms_point node[peripheries="0" shape="cds", color="#DA5961", fontcolor="#DA5961" , style="" target="_top" fontsize=10] hook_initialize_user_defined_tracer [label=" HOOK_INITIALIZE_MASS_FRACTION_OF_TRACER" URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.initialize_mass_fraction_of_tracer"] hook_set_bc_user_defined_tracer [label=" HOOK_SET_PRESCRIBED_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.set_prescribed_boundary_condition_of_mass_fraction_of_tracer"] hook_update_variables [label=" HOOK_UPDATE_PLUGINS_SECONDARY_VARIABLES_ON_TRACER_SOLVER", URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.update_plugins_secondary_variables_on_tracer_solver"] hook_calculate_tracer_source_terms [label=" HOOK_CALCULATE_TRACER_SOURCE_TERM" URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.calculate_tracer_source_term"] hook_update_bc_user_defined_tracer [label=" HOOK_UPDATE_BOUNDARY_CONDITION_OF_MASS_FRACTION_OF_TRACER" URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.update_boundary_condition_of_mass_fraction_of_tracer"] hook_calculate_mass_fraction_of_tracer_in_field [label=" HOOK_CALCULATE_MASS_FRACTION_OF_TRACER_IN_FIELD \n HOOK_CALCULATE_MASS_FRACTION_OF_TRACER_IN_PHASE" URL="../06_solver_hooks.html#_alfasim_sdk.hook_specs.calculate_mass_fraction_of_tracer_in_phase" ] {rank = same hook_update_variables_point hook_update_variables} {rank = same hook_calculate_source_terms_point hook_calculate_tracer_source_terms } {rank = same hook_initialize_user_defined_tracer_point hook_initialize_user_defined_tracer } {rank = same hook_set_bc_user_defined_tracer_point hook_set_bc_user_defined_tracer } {rank = same tracer_1 hook_update_bc_user_defined_tracer } {rank = same tracer_2 hook_calculate_mass_fraction_of_tracer_in_field} invisible_init -> hook_initialize_user_defined_tracer_point [arrowhead=none] hook_calculate_source_terms_point -> invisible_end subgraph cluster1{ labeljust="l" style="rounded, dashed" color="#8699A3" hook_initialize_user_defined_tracer_point -> hook_set_bc_user_defined_tracer_point [arrowhead=none] hook_set_bc_user_defined_tracer_point-> tracer_1 tracer_1 -> tracer_2 tracer_2 -> hook_update_variables_point [arrowhead=none] hook_update_variables_point -> tracer_3 tracer_3 -> hook_calculate_source_terms_point [arrowhead=none] } edge[constraint=false, style=dotted, color="#DA5961"] subgraph cluster2{ label="Hooks for User Defined Tracers" labeljust="l" fontcolor="#2c3e50" style="rounded, dashed" color="#8699A3" hook_initialize_user_defined_tracer_point -> hook_initialize_user_defined_tracer hook_set_bc_user_defined_tracer_point -> hook_set_bc_user_defined_tracer tracer_1 -> hook_update_bc_user_defined_tracer tracer_2 -> hook_calculate_mass_fraction_of_tracer_in_field } hook_calculate_source_terms_point -> hook_calculate_tracer_source_terms hook_update_variables_point -> hook_update_variables }
This page contains the full reference to ALFAsim-SDK API.
Here is listed the completed API available to implement the Solver Hooks.
Enums
Structs
Functions
ALFAsim-SDK API Loading
Plugin Input Data (From user interface)
Plugin Internal Data
ALFAsim’s Solver Data
Unit Cell Model (UCM) helpers
Variable Name Parsing
It holds the possible returning error code from ALFAsim-SDK-API functions.
Values:
REFERENCE_NOT_SET
Some reference from input data wasn’t set.
UNKNOWN_REFERENCE_TYPE
Reference type is unknown.
OUT_OF_BOUNDS
Index out of array bounds.
UNKNOWN_CONTEXT
The context is unknown.
NOT_AVAILABLE_DATA
Data from ALFAsim is not available.
BUFFER_SIZE_INSUFFICIENT
Buffer size is insufficient.
UNDEFINED_DATA
Plugin internal data is undefined.
NOT_IMPLEMENTED
A feature is not implemented in an API function.
Everything was fine.
GridScope
It holds the variable scope in the grid to retrieve a simulation array.
CENTER
Variable located in the control volume center
FACE
Variable located in the control volume face
MultiFieldDescriptionScope
It holds the variable scope in the Multifield description (phases/fields/layers) to retrieve a simulation array.
MIXTURE
Variable associated to the mixture
GLOBAL
Global variable
FIELD
Variable associated to the field
LAYER
Variable associated to the layer
PHASE
Variable associated to the phase
TimestepScope
It holds the variable scope in the time level to retrieve a simulation array.
CURRENT
Variable in the current time step
PREVIOUS
Variable in the previous (old) time step
It holds the possible state variables that can be computed to a phase inside the plugin. See solver hooks calculate_state_variable and calculate_phase_pair_state_variable
RHO
MU
CP
DRHO_DP
Partial derivative of density in relation to pressure
DRHO_DT
Partial derivative of density in relation to temperature
H
K
SIGMA
Interfacial tension
WallLayerProperty
It holds the possible wall layer property that can be obtained from ALFAsim’s solver. This Enum is used by The #set_wall_layer_property API function.
Enum
THICKNESS
Wall layer tickness
DENSITY
Wall layer material density
THERMAL_CONDUCTIVITY
Wall layer material thermal conductivity
HEAT_CAPACITY
Wall layer material specifc heat capacity
INNER_EMISSIVITY
Wall layer material inner emissivity
OUTER_EMISSIVITY
Wall layer material outer emissivity
EXPANSION
Wall layer material thermal expansion coefficient
VISCOSITY
Wall layer material viscosity (if it is a deposit layer)
sdk_load_error_code
It holds the possible returning error code when trying to open ALFAsim-SDK API.
SDK_DLL_PATH_TOO_LONG
Path to SDK dll too long (Windows limitation).
SDK_ALREADY_OPEN_ERROR
SDK dll already Opened.
SDK_OK
VariableScope
It holds all variable scopes (grid, multifield and timestep)
Public Members
grid_scope
Which grid scope of the variable
mfd_scope
Which multifield scope of the variable
ts_scope
Which timestep scope of the variable
The ALFAsim-SDK API must be loaded with alfasim_sdk_open() inside HOOK_INITIALIZE of any plugin, otherwise the plugin will not be able to use any function available in the API. In addition, to avoid memory leak it is important to unload the ALFAsim-SDK API in the last called hook, HOOK_FINALIZE, using the alfasim_sdk_close() function.
alfasim_sdk_open()
alfasim_sdk_close()
alfasim_sdk_open
alfasim_sdk_close
All API functions on this section has the parameter var_name. This parameter must be filled following the rules exposed in Variable Name Parsing section.
var_name
get_plugin_input_data_boolean
Gets the data provided from the user on a Boolean input field. For more detail about the Boolean input field check alfasim_sdk.types.Boolean
alfasim_sdk.types.Boolean
An error_code value.
[in] ctx: ALFAsim’s plugins context.
[in] ctx
[out] out: Retrieved variable value.
[out] out
[in] plugin_id: Plugin ID.
[in] plugin_id
[in] var_name: Name of the variable to be retrieved.
[in] var_name
get_plugin_input_data_enum
Gets the data provided from the user on a Enum input field. For more detail about the Enum input field check alfasim_sdk.types.Enum.
alfasim_sdk.types.Enum
get_plugin_input_data_quantity
Gets the data provided from the user on a Quantity input field. For more detail about the quantity input field check alfasim_sdk.types.Quantity.
alfasim_sdk.types.Quantity
get_plugin_input_data_string
Gets the data provided from the user on a String input field. For more detail about the string input field check alfasim_sdk.types.String.
alfasim_sdk.types.String
[in] size: Size of output string (param out).
[in] size
out
get_plugin_input_data_string_size
Gets the size of the data provided from the user on a String input field.
[out] out: String size of a variable in which its name is informed by var_name.
get_plugin_input_data_file_content
Gets the data provided from the user on a FileContent input field. For more detail about the FileContent input field check alfasim_sdk.types.FileContent
alfasim_sdk.types.FileContent
get_plugin_input_data_file_content_size
Gets the size of the data provided from the user on a FileContent input field.
[out] out: File content size of a variable in which its name is informed by var_name.
get_plugin_input_data_table_quantity
Gets the values from a column of an input table. column_id is the string defined in the plugin’s configuration file. If the var_name or the column_id are invalid, UNDEFINED_DATA is returned.
column_id
int size = -1; double* values = NULL; int errcode = get_plugin_input_data_table_quantity( ctx, &values, &size, "temperature", get_plugin_id(), "Model.some_table" ); for (int i = 0; i < size; ++i) { some_plugin_data = 1.1 * values[i]; }
[out] out: Variable values array.
[out] size: Size of variable values array.
[out] size
[in] column_id: Table column ID which values are retrieved.
[in] column_id
get_plugin_input_data_reference
Gets an ALFAsim input data internal Reference. Note that a Reference is a specific concept of ALFAsim-SDK and plugins - It is a way to retrieve data from an input outside of the current model. See the ALFAsim’s SDK python configuration file for more information.
void* tracer_ref = nullptr; errcode = get_plugin_input_data_reference( ctx, &tracer_ref, get_plugin_id(), "Model.tracer_reference"); int tracer_id = -1; errcode = get_tracer_id(ctx, &tracer_id, tracer_ref);
[out] out: ALFAsim data reference.
[in] var_name: Name of the variable to be retrived.
get_plugin_input_data_multiplereference_selected_size
Get the number of selected references in a multiple-reference selection. User should be able to iterate over the selections to get information.
int errcode = -1; int indexes_size = -1; errcode = get_plugin_input_data_multiplereference_selected_size( ctx, &indexes_size, get_plugin_id(), "Model.internal_multiple_reference"); for (int i = 0; i < indexes_size; ++i) { auto value = -1.0; auto reference_str = std::string( "Model.internal_multiple_reference[" + std::to_string(i) + "]->quantity"); errcode = get_plugin_input_data_quantity( ctx, &value, get_plugin_id(), reference_str.c_str()); } }
[out] indexes_size: Number of selected references in a multiple reference input data.
[out] indexes_size
[in] var_name: Name of the variable.
set_plugin_data
Set the data provided from the plugin developer.
When a plugin has internal data and it has to be accessed by all C/C++ Hooks during the simulation this function allows the plugin to ask the ALFAsim’s solver to hold its internal data.
Hooks
[in] data: Plugin internal data.
[in] data
[in] thread_id: Thread ID, see get_thread_id for more information.
[in] thread_id
get_plugin_data
Gets the data provided from the plugin developer.
Once the plugin set an internal data, it can be accessed from any C/C++ Hook during the simulation using this function.
Hook
[out] out: Plugin internal data.
get_number_of_threads
Gives the number of running threads on the solver
ALFAsim’s Solver runs in multiple threads. To avoid data access concurrency problems the plugin developer must set one internal data to each running thread during the initialization. Then, with number of threads, the developer can do it properly.
[out] n_threads: Number of threads.
[out] n_threads
get_thread_id
Gives the current running thread id (in the solver process)
Once the plugin sets one internal data to each running thread, to access this data properly it has to know which thread is trying to access this data. For that, this function retrieves this information from solver.
[out] thread_id: Thread ID.
[out] thread_id
get_plugin_variable
Gets the contents of a plugin-registered secondary variable (Given by name).
It is allowed to the plugins to add new secondary variables via python config file. ALFAsim’s solver registers/holds these variables and make them available in the solver hooks by this function.
hooks
[out] out: Plugin-registered secondary variable values array.
[in] variable_name: Name of the secondary variable.
[in] variable_name
[in] line_index: It can represent Layer/Field/Phase ID, since the secondary variables can be associated to different MultiFieldDescriptionScope.
[in] line_index
[in] ts_scope: A TimestepScope value.
[in] ts_scope
[out] size: Size of out array of values. It can be the number of volumes or number of faces depending in which GridScope the secondary variable was registered.
get_field_id
Gets the field ID of the given name. Althought this depends on the hydrodynamic model being solved, common values include “gas”, “oil”, “droplet” and “bubble”. This functions supports retrieve ID of field added by plugin.
[out] out: Field ID.
[in] name: Name of the field to retrieve the ID.
[in] name
get_primary_field_id_of_phase
Gets the primary field ID of the phase with given name. For example, the “oil” phase has primary field “oil”. Different phases may have different primary fields. Use this function when you need a variable from a field, but you aren’t sure about the field name, but you know the phase name.
[in] name: Name of the phase to retrieve the primary field ID.
get_phase_id
Gets the phase ID of the given name. Althought this depends on the hydrodynamic model being solved, common values include “gas”, “oil” and “water”. This functions supports retrieve ID of phase added by plugin.
[out] out: Phase ID.
[in] name: Name of the phase to retrieve the ID.
get_layer_id
Gets the layer ID of the given name. Althought this depends on the hydrodynamic model being solved, common values include “gas”, “oil” and “water”. This functions supports retrieve ID of layer added by plugin.
[out] out: Layer ID.
[in] name: Name of the layer to retrieve the ID.
get_state_variable_array
Gets the current contents of a given state variable (For an array data pointer). A state variable is any variable calculated from pressure and temperature, as any thermodynamic variable.
errcode = get_state_variable_array( ctx, enthalpy, StateVariable::H, FIELD_GAS, size);
[out] out: State Variable values array.
[in] state_var: A StateVariable value. It indicates which variable to be retrieved.
[in] state_var
[in] field_index: Index of the field in which the state variable is retrieved.
[in] field_index
[out] size: Size of the out array of values.
Changing the contents retrieved by this function (out array) has UNDEFINED BEHAVIOR. The plugin must NEVER change the contents returned by this function.
get_simulation_array
Gets the current contents of a given secondary variable (For an array data pointer). A secondary variable is any variable calculated in the solver iterative procedure. Note that not all variables are available at any time. If a given variable is not available in one of the chosen scopes, a NOT_AVAILABLE_DATA error is returned.
List of variable_name values:
variable_name
"rho": Density [kg/m3]
"rho"
"mu": Viscosity [Pa.s]
"mu"
"alpha": Volume Fraction [m3 of field|layer|phase /m3 of mixture]
"alpha"
field|layer|phase
"mass_concentration": Mass Concentration [kg of field|layer|phase /m3 of mixture]
"mass_concentration"
"P": Pressure [Pa]
"P"
"T": Temperature [K]
"T"
"h": Specific Enthalpy [J/kg]
"h"
"cp": Specific Heat Capacity [J/kg.K]
"cp"
"k": Thermal Conductivity [W/m.K]
"k"
"eta_inner": Heat Transfer Coefficient between inner pipe wall and the fluid layers [W/m2.K]
"eta_inner"
"U": Velocity [m/s]
"U"
"U_superficial": Superficial Velocity [m/s]
"U_superficial"
"RS": Gas mass fraction in gas/oil mixture [kg/kg]
"RS"
"RSW": Water vapour mass fraction in gas phase [kg/kg]
"RSW"
"ff_S_wall": Wall friction factor times wall perimeter [-]
"ff_S_wall"
"ff_S_interface": Interface Friction factor times interface perimeter [-]
"ff_S_interface"
"D": Pipe Inner Diameter [m]
"D"
"A": Cross-sectional Area in each control volume [m2]
"A"
"theta": Inclination of each control volume [rad]
"theta"
"dx": Control Volume Length along the Pipe Axis [m]
"dx"
"dv": Volume of the control volume [m3]
"dv"
"D_eff": Effective Pipe Inner Diameter, considering the internal deposit layers [m]
"D_eff"
"A_eff": Cross-sectional Area Effective in each control volume, considering the internal deposit layers [m2]
"A_eff"
"dv_eff": Effective Volume of the control volume, considering the internal deposit layers [m3]
"dv_eff"
It is important to know that the listed variable_names are no available in all MultiFieldDescriptionScope and GridScope. Because of that, the error_code must be checked.
[in] variable_name: String with the variable name. See the list of possible values above.
[in] var_scope: It must be configured to determine all scopes in which the variable will be retrieved. See VariableScope for more information.
[in] var_scope
[in] line_index: It can represent Layer/Field/Phase ID, since the secondary variables can be associated to different MultiFieldDescriptionScope. When it is a GLOBAL variable line_index must be 0.
line_index
get_simulation_tracer_array
Gets the current contents of a given tracer mass fraction (For an array data pointer). A tracer mass fraction is calculated in the extra solver iterative procedure. Note that not all tracer mass fraction are available at any time. If a given tracer mass fraction (in an inexistent field) is not available, a NOT_AVAILABLE_DATA error is returned.
List of variable_name_c values:
variable_name_c
"phi": Mass Fraction [kg of tracer (in field|phase) / kg of mixture]
"phi"
field|phase
[in] variable_name_c: String with the variable name. See the list of possible values above.
[in] variable_name_c
[in] tracer_index: Tracer ID, it can be retrieved by get_tracer_id.
[in] tracer_index
[in] line_index: It can represent Field or Phase ID, since the tracer masss fraction can be calculated related to Field or Phase. When it is the mass fraction on mixture the line_index must be 0.
get_simulation_quantity
Gets the current contents of a given secondary variable (For a single quantity). A secondary variable is any variable calculated in the solver iterative procedure. Note that not all variables are available at any time. If a given variable is not available, a NOT_AVAILABLE_DATA error is returned.
"dt": Time Step [s]
"dt"
"time": Current time [s]
"time"
[out] out: Variable value.
[in] ts_scope: TimestepScope value.
get_tracer_id
Retrieves the tracer ID given a tracer reference. A tracer reference may be obtained from the user input data (See get_plugin_input_data_reference API function for an example).
[out] tracer_id: Tracer ID.
[out] tracer_id
[in] reference: Tracer reference.
[in] reference
get_tracer_name_size
Retrieves the size of the tracer name, given a tracer reference. A tracer reference may be obtained from the user input data (See get_plugin_input_data_reference API function for an example).
[out] tracer_name_size: Size of tracer name string.
[out] tracer_name_size
get_tracer_name
Retrieves the tracer name, given a tracer reference. The tracer_name parameter must be a valid and pre-allocated memory region where the name string will be copied to. A tracer reference may be obtained from the user input data (See get_plugin_input_data_reference API function for an example).
tracer_name
int tracer_name_size = -1; errcode = get_tracer_name_size(ctx, &tracer_name_size, tracer_ref); char* tracer_name = (char*)malloc(sizeof(char) * tracer_name_size); errcode = get_tracer_name( ctx, tracer_name, tracer_ref, tracer_name_size); std::cout << "TRACER NAME: " << tracer_name << std::endl; free(tracer_name);
[out] out: String with tracer name.
[in] size: Size of out string.
get_tracer_ref_by_name
Gets the tracer reference for a given tracer name. This function is important to obtain the tracer reference of a user defined tracer added by the plugin.
[out] reference: Tracer reference.
[out] reference
[in] tracer_name: Tracer name.
[in] tracer_name
get_tracer_partition_coefficient
Gets the partition coefficient input data for a given tracer reference. The phase_id must also be given (See get_phase_id API function). A tracer reference may be obtained from the user input data (See get_plugin_input_data_reference API function for an example).
phase_id
[out] out: Partition coefficient value related to the specified phase.
[in] phase_id: Phase ID.
[in] phase_id
get_wall_interfaces_temperature
Gets the wall interface temperature for a given control volume. Each control volume has an array of temperatures, one for each wall layer. The temperatures are given in the wall interfaces.
[out] out: Wall interfaces temperature values array.
[in] control_volume: Control Volume ID.
[in] control_volume
[in] size: Size of out array of values.
get_flow_pattern
Gets the flow pattern for each control volume.
List of possible values of Flow Pattern is:
Flow Pattern
0 - Unknown
1 - Stratified
2 - Dispersed Bubble
3 - Bubble
4 - Slug
5 - Annular Mist
[out] out: Flow Pattern values array. See list of possible values above.
[in] grid_scope: A GridScope value.
[in] grid_scope
get_ucm_friction_factor_input_variable
Gets the current UCM (unit cell model) input data for friction factor calculation. Any available variable by this function is considered for a unit cell, which means that there are variables with one value and there are variables with two values related to the two phase system (GAS and LIQUID). If a given variable name is not available, a NOT_AVAILABLE_DATA error is returned.
List of variable_name with two values (Two phase):
"alpha": Volume Fraction [m3 of phase /m3 of mixture]
phase
It is important to know that the listed variable_names are not available in any phase, only for two phase systems, in which Gas id is 0 (zero) and Liquid id (sum of all liquid phases) is 1 (One). Because of that, the error_code must be checked.
Gas id
Liquid id
List of variable_name with one value:
"D": Unit Cell Inner Diameter [m]
"ks": Roughness [m]
"ks"
"theta": Inclination of the Unit Cell [rad]
"sigma": Gas-liquid Surface Tension [N/m]
"sigma"
[in] var_name: String with the variable name. See the list of possible values above.
[in] phase_id: A TwoPhaseSystem value. When the requested variable is not associated to a phase any value can be passed.
get_ucm_fluid_geometrical_properties
Gets the current UCM (unit cell model) fluid geometrical properties for friction factor calculation.
During the implementation of any HOOK related to the UCM friction factor, this function provides the following fluid geometrical properties:
"S_w": Wetted perimeters of phases [m].
"S_w"
"S_i": Interface perimeter [m].
"S_i"
"H": Phase height [m].
"H"
It is important to know that
S_w
[out] S_w: Wetted Perimeters [m].
[out] S_w
[out] S_i: Interface Perimeter [m].
[out] S_i
[out] H: Phase height [m]. For annular flow, H[GAS] is the core diameter and H[LIQUID] is the total liquid film height.
[out] H
[in] alpha_G: Unit Cell Gas Volume Fraction [m3 of gas phase /m3 of mixture].
[in] alpha_G
gas phase
[in] D: Unit Cell Inner Diameter [m].
[in] D
To retrieve input data from the plugin’s GUI, the plugin must pass a var_name in a specific format. API functions that use this kind of variable described on Plugin Input Data (From user interface) section.
All variables must begin with the model name described on the plugin model, followed by . (For nested objects) or -> (For references). Lists must be accessed with the list index directly, for example, Model.lst[0] will be the first element of the list “lst”, inside the plugin model named “Model”. References can be internal (Reference to a plugin model) or external (Reference to an ALFAsim model).
->
Model.lst[0]
Imagine you have the following simple GUI model defined as
@data_model(icon='', caption='Plugin Model') class Model: boolean_data = Boolean(value=True, caption="BOOLEAN CAPTION")
To extract the plugin input data content on C++, the plugin must use the proper API function call:
int errcode = 0; bool test_api_boolean = false; errcode = alfasim_sdk_api.get_plugin_input_data_boolean( ctx, &test_api_boolean, get_plugin_id(), "Model.boolean_data"); std::cout << " BOOLEAN:" << test_api_boolean << " ERROR CODE:" << errcode << std::endl;
See get_plugin_input_data_boolean() for details.
get_plugin_input_data_boolean()
For the cases were the model is a container, it is possible to retrieve the information for each element individually.
@data_model(icon='', caption='Plugin Model 2') class Model: name = String(value='default', caption='Name') boolean = Boolean(value=True, caption="Boolean") quantity = Quantity(value=1, unit='m', caption='Quantity') @container_model(icon='', caption='Plugin Container', model=Model) class ModelContainer: pass
int errcode = 0; double test_api_quantity = 0.; errcode = alfasim_sdk_api.get_plugin_input_data_quantity( ctx, &test_api_quantity, get_plugin_id(), "ModelContainer[0].quantity"); std::cout << " Quantity from container[0]:" << test_api_quantity << " ERROR CODE:" << errcode << std::endl;
See get_plugin_input_data_quantity() for details.
get_plugin_input_data_quantity()
Internal references are references to models defined in the plugin itself. They are useful when you have a list of models, for example, but need to let the user decide from one specific model from the list. Assuming the model container defined in the previous, example, an internal reference for an element inside that container can be programmed as follows. The plugin must use -> to access referenced data, instead of . as in other examples.
@data_model(icon='', caption='Plugin Model') class OtherModel: internal_reference = Reference( container_type='ModelContainer', ref_type=Model, caption="Internal Reference" )
Data from the referenced model can then be extracted on C++ code as follows. Note that the developer will extract the values directly, not the model itself, that is, in the example below, there is never an object of type Model. Raw data values such as boolean, strings or floats are directly retrieved instead.
Model
int errcode = 0; double test_api_quantity = 0.; errcode = alfasim_sdk_api.get_plugin_input_data_quantity( ctx, &test_api_quantity, get_plugin_id(), "OtherModel.internal_reference->quantity"); std::cout << " Quantity from internal reference:" << test_api_quantity << " ERROR CODE:" << errcode << std::endl;
External references gives the user a way to interact with references to specific ALFAsim’s GUI object configurations. Those types of references work a bit different, because ALFAsim developers must provide an API for each specific entity. As of today, the only exposed ALFAsim model is the Tracer model. See the tracer-related functions to have an overview about the available data that can be used:
get_simulation_tracer_array() get_tracer_id() get_tracer_name() get_tracer_ref_by_name() get_tracer_partition_coefficient()
get_tracer_name()
get_tracer_partition_coefficient()
The example below extracts the tracer Id configured in the plugin.
@data_model(icon='', caption='Plugin Model') class OtherModel: tracer_reference = Reference( ref_type=TracerType, caption="Tracer Reference", )
int errcode = 0; void* tracer_ref = nullptr; errcode = get_plugin_input_data_reference( ctx, &tracer_ref, get_plugin_id(), "OtherModel.tracer_reference"); int tracer_id = -1; errcode = get_tracer_id(ctx, &tracer_id, tracer_ref); std::cout << "TRACER ID: " << tracer_id << std::endl;
See get_plugin_input_data_reference() for details.
get_plugin_input_data_reference()
The plugin developer may need to let the user select not one, but several references (This is valid for both internal and external references). To tackle this problem, ALFAsim developers created the notion of Multiple References. It is basically a container of referenced objects, and the usage is simply a mix of the container with the reference syntax.
Example of a GUI model in which has both types of multiple references:
@data_model(icon='', caption='Plugin Model') class OtherModel: multiple_reference = MultipleReference( ref_type=TracerType, caption='Multiple Reference' ) internal_multiple_reference = MultipleReference( ref_type=Model, container_type='ModelContainer', caption='Internal Multiple Reference' )
Example of accessing the external multiple references:
int errcode = -1; int indexes_size = -1; errcode = get_plugin_input_data_multiplereference_selected_size( ctx, &indexes_size, get_plugin_id(), "OtherModel.multiple_reference"); void* tracer_ref = nullptr; for (int i = 0; i < indexes_size; ++i) { auto reference_str = std::string( "OtherModel.multiple_reference[" + std::to_string(i) + "]"); errcode = get_plugin_input_data_reference( ctx, &tracer_ref, get_plugin_id(), reference_str.c_str()); int tracer_id = -1; errcode = get_tracer_id(ctx, &tracer_id, tracer_ref); std::cout << "TRACER ID: " << tracer_id << std::endl; }
Example of accessing the internal multiple references:
int errcode = -1; int indexes_size = -1; errcode = get_plugin_input_data_multiplereference_selected_size( ctx, &indexes_size, get_plugin_id(), "OtherModel.internal_multiple_reference"); for (int i = 0; i < indexes_size; ++i) { auto test_api_bool = false; auto reference_str = std::string( "OtherModel.internal_multiple_reference[" + std::to_string(i) + "]->boolean"); errcode = get_plugin_input_data_boolean( ctx, &test_api_bool, get_plugin_id(), reference_str.c_str()); std::cout << " Bool from referenced container[" << i << "]:" << (test_api_bool ? "true" : "false") << " ERROR CODE:" << errcode << std::endl; }
see get_plugin_input_data_multiplereference_selected_size() for details.
get_plugin_input_data_multiplereference_selected_size()
Here is listed the completed API available to implement the Application Hooks.
Models
Types
Layout
Status
Context
data_model
`data_model` is an object that keeps together many different properties defined by the plugin and allows developers to build user interfaces in a declarative way.
`data_model`
Application Required:
The following options are required when declaring a data_model and are used into the user interface
caption A text to be displayed over the Tree. icon Name of the icon to be used over the Tree.
A text to be displayed over the Tree.
Name of the icon to be used over the Tree.
Even though the icon parameter is required, it’s not currently being used.
Plugin Defined:
Visual elements that allow the user to input information into the application, or to arrange better the user interface.
Visual elements that allow the user to provide input information into the application.
Elements that assist the developer to arrange input fields in a meaningful way.
Check the section visual elements to see all inputs available, and layout elements to see all layouts available.
Example:
@data_model(icon='', caption='My Plugin') class MyModel: distance = Quantity(value=1, unit='m', caption='Distance') @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModel]
container_model
container_model is an object that keeps together many different properties defined by the plugin and allows developers to build user interfaces in a declarative way similar to data_model().
container_model() can also hold a reference to a data_model() declared from the plugin, making this object a parent for all new data_model() created.
The following options are required when declaring a container_model().
A reference to a class decorated with data_model().
Plugin defined:
Elements that assist the developer to arrange input fields in meaningfully way.
Example myplugin.py
@data_model(icon="", caption="My Child") class ChildModel: distance = Quantity(value=1, unit="m", caption="Distance") @container_model(icon='', caption='My Container', model=ChildModel) class MyModelContainer: my_string = String(value='Initial Value', caption='My String') @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModelContainer]
Container data also includes automatically two actions for the model:
Action: Create new Model
An action that creates a new model inside the container selected, you can activate this action by right-clicking in the container over the Tree, or by clicking on the “Plus” icon available at the Model Explorer.
Model Explorer
Action: Remove
An action that remove the selected model, only available for models inside a container, you can activate this action by right-clicking the model over the Tree, or by clicking on the “Trash” icon available at the Model Explorer.
The types module supplies UI elements for creating user interfaces with the classic desktop-style, each type has a related model.
types
Models are the primary elements to create user interfaces on ALFAsim, models can display data, receive user input, and provide a container for other fields that should be grouped together.
BaseField
A base field for all types available at ALFAsim.
caption – Label to be displayed on the right side of the component.
tooltip – Shows a tip, a short piece of text.
enable_expr (Callable) – Function to evaluate if the component will be enabled or not.
visible_expr (Callable) – Function to inform if the component will be visible or not.
Caption and Tooltip:
Caption is the most basic information that all fields must inform, it will display Label over the right side of the component on the Model Explorer window.
Tooltips are short pieces of text to reminder/inform the user about some specificity about the property when they keep the mouse over the field. Tooltips must be a string and can have HTML tags and Unicode characters as well.
TypeError – if the tooltip informed is not a string.
@data_model(icon='', caption='My Plugin') class MyModel: my_string_1= String( value='String 1', caption='My String 1', tooltip="Some Text <br> <b> More Information</b>", ) my_string_2 = String( value='String 2', caption='My String 2', tooltip="∩ ∪ ∫ ∬ ∮", ) @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModel]
The images below shows the output from the example above.
Enable Expression:
Accepts a python function that controls either the component will be enabled, or disabled. The python function will receive two arguments, an instance of itself (to check local values) and an instance of alfasim_sdk.context.Context() to retrieve information about the application.
alfasim_sdk.context.Context()
This function must return a boolean, informing True (for enabled) or False (for disabled).
enabled: The component will handle keyboard and mouse events.
disabled: The component will not handle events and it will be grayed out.
def my_check(self, ctx): return self.bool_value @data_model(icon="", caption="My Plugin") class MyModel: bool_value = Boolean(value=True, caption="Enabled") N_ions = Quantity( caption='Number of Ions', value=1, unit='-', enable_expr=my_check, ) @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModel]
The image below shows the N_ions property disabled, when the property bool_value is disabled (False)
N_ions
bool_value
Visible Expression:
Accepts a python function that controls either the component will be visible, or not. The python function will receive two arguments, an instance of itself (to check local values) and an instance of alfasim_sdk.context.Context() to retrieve information about the application.
This function must return a boolean, informing True (for visible) or False (for invisible).
def my_check(self, ctx): return self.bool_value @data_model(icon="", caption="My Plugin") class MyModel: bool_value = Boolean(value=True, caption="Enabled") N_ions = Quantity( caption="Number of Ions", value=1, unit="-", visible_expr=my_check, ) @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModel]
The image below shows the N_ions property visible, when the property bool_value is enabled (True)
String
The String field represents an input that allows the user to enter and edit a single line of plain text.
The String field have all options available from BaseField(), plus the following ones
BaseField()
value (str) – property to hold the value informed by the user.
@data_model(icon="", caption="My Plugin") class MyModel: string_field = String( value="Default Value", caption="String Field", )
Accessing String Field from Plugin:
In order to access this field from inside the plugin implementation, in C/C++ you need to use get_plugin_input_data_string_size() together with get_plugin_input_data_string_size()
get_plugin_input_data_string_size()
Accessing String Field from Context:
When accessed from the Context(), the String field will return the currently text as str.
Context()
str
>>> ctx.get_model("MyModel").string_field 'Default Value' >>> type(ctx.get_model("MyModel").string_field) <class 'str'>
The Enum field provides list of options to the user, showing only the select item but providing a way to display a list of all options through a combo-box.
The Enum field have all options available from BaseField(), besides the listed the ones listed above:
values – A list of strings with the available options.
initial – Indicates which one of the options should be selected per default. If not given, the first item in values will be used as default.
values
@data_model(icon="", caption="My Plugin") class MyModel: enum_field = Enum( values=["Option 1, Option 2"], initial="Option 1", caption="Enum Field", )
Accessing Enum Field from Plugin:
In order to access this field from inside the plugin implementation, in C/C++, you need to use get_plugin_input_data_enum()
get_plugin_input_data_enum()
Accessing Enum Field from Context:
When accessed from the Context(), the Enum field will return the currently selected option as str.
@data_model(icon="", caption="My Plugin") class MyModel: enum_field = Enum( values=["Option 1", "Option 2"], initial="Option 1", caption="Enum Field", )
# From Terminal >>> ctx.get_model("MyModel").enum_field 'Option 1' >>> type(ctx.get_model("MyModel").enum_field) <class 'str'>
Reference
The Reference field provides a list of options to the user and displays the current item selected.
There are two types of models supported by this field.
models from ALFAsim, for example, Tracers.
a model defined within the plugin.
In order to reference custom data, the model must be inside a container.
caption (str) – Property used as a label for the field.
ref_type – Property that indicates which type of data the Reference will hold.
container_type – The name of the class that holds the ref_type, this property must be used when the ref_type references model from the plugin.
ref_type
Example using ALFAsimTypes on myplugin.py
ALFAsimTypes
@data_model(icon="", caption="My Plugin") class MyModel: tracer_ref = Reference( ref_type=TracerType, caption="Tracer Type", )
Example using Custom Data on myplugin.py
Custom Data
@data_model(caption="My Model") class MyModel: field_1 = String(value="Value 1", caption="String 1") @container_model(caption="My Container", model=MyModel, icon="") class MyContainer: internal_ref = Reference( ref_type=MyModel, container_type="MyContainer", caption="Internal Reference", )
Accessing Reference Field from Plugin:
In order to access this field from inside the plugin implementation, in C/C++, you need to use get_plugin_input_data_reference()
Accessing Reference Field from Context:
When accessed from the Context(), the Reference field will return the currently selected option object instance.
With the instance, you can access all attributes from the object normally. Check the example below.
@data_model(caption="My Model") class MyModel: field_1 = String(value="Value 1", caption="String 1") @container_model(caption="My Container", model=MyModel, icon="") class MyContainer: tracer_ref = Reference( ref_type=TracerType, caption="Tracer Type", ) internal_ref = Reference( ref_type=MyModel, container_type="MyContainer", caption="Internal Reference", ) # Example with Tracer >>> ctx.get_model("MyContainer").tracer_ref TracerModel(gas_partition_coefficient=[...]) >>> ctx.get_model("MyContainer").tracer_ref.gas_partition_coefficient Scalar(0.0, 'kg/kg', 'mass fraction') # Example with Custom Data >>> ctx.get_model("MyContainer").internal_ref MyModel(field_1='Value 1', name='My Model 1') >>> ctx.get_model("MyContainer").internal_ref.field_1 'Value 1'
MultipleReference
The MultipleReference field works similar to Reference(), providing a list of options to the user, but allowing multiple values, of the same type, to be chosen.
Reference()
There are two types of models supported by this field. :ALFAsimTypes: models from ALFAsim, for example, Tracers. :Custom Data: a model defined within the plugin.
In order to reference a custom data the model must be inside a container.
@data_model(icon="", caption="My Plugin") class MyModel: tracer_ref = MultipleReference( ref_type=TracerType, caption="Tracer Type" )
@data_model(caption="My Model") class MyModel: field_1 = String(value="Value 1", caption="String 1") @container_model(caption="My Container", model=MyModel, icon="") class MyContainer: internal_ref = MultipleReference( ref_type=MyModel, container_type="MyContainer", caption="Internal Reference", )
Accessing MultipleReference Field from Plugin:
In order to access this field from inside the plugin implementation, in C/C++, you need to use get_plugin_input_data_multiplereference_selected_size()
Accessing MultipleReference Field from Context:
When accessed from the Context(), the MultipleReference field will return a list with the currently selected option objects instances.
With the instance, you can access all attributes from the object. Check the example below.
@data_model(caption="My Model") class MyModel: field_1 = String(value="Value 1", caption="String 1") @container_model(caption="My Container", model=MyModel, icon="") class MyContainer: internal_ref = MultipleReference( ref_type=MyModel, container_type="MyContainer", caption="Internal Reference", ) # Example >>> ctx.get_model("MyContainer").internal_ref [MyModel(field_1='Value 1', name='My Model 1'), MyModel(field_1='Value 1', name='My Model 4')] >>> type(ctx.get_model("MyContainer").internal_ref) <class 'list'> >>> ctx.get_model("MyContainer").internal_ref[0] MyModel(field_1='Value 1', name='My Model 1')
Quantity
The Quantity field provides a way to the user provide a scalar value into the application.
The Quantity field have all options available from BaseField(), besides the listed the ones listed above: :param values: A number value. :param unit: Unit for the given scalar.
All scalar values are created using the Barril library
Checkout the Barril documentation, to see all available units
If you want to check the input value, is recommended to include a status monitor in your plugin to make sure that the provided value is valid.
For more details about status monitor check alfasim_get_status()
@data_model(icon="", caption="My Plugin") class MyModel: quantity_field = Quantity( value=1, unit="degC", caption="Quantity Field" )
Accessing Quantity Field from Plugin:
In order to access this field from inside the plugin implementation, in C/C++, you need to use get_plugin_input_data_quantity()
Accessing Quantity Field from Context:
When accessed from the Context(), the Quantity field will return a Scalar object, with the current value and unit. Check out the Scalar documentation from Barril for more details about the usage.
Scalar
@data_model(icon="", caption="My Plugin") class MyModel: quantity_field = Enum( values=["Option 1", "Option 2"], initial="Option 1", caption="Enum Field", ) # From Terminal >>> ctx.get_model("MyModel").quantity_field Scalar(1.0, 'degC', 'temperature') >>> ctx.get_model("MyModel").quantity_field.value 1.0 >>> ctx.get_model("MyModel").quantity_field.unit 'degC' >>> ctx.get_model("MyModel").quantity_field.GetValue('K') 274.15
Table
The Table component provides a table to the user to be able input values manually or by importing it from a file.
@data_model(icon="", caption="My Model") class MyModel: Table( rows=[ TableColumn( id="temperature", value=Quantity( value=1, unit="K", caption="Temperature Column Caption", ), ), TableColumn( id="pressure", value=Quantity( value=2, unit="bar", caption="Pressure Column Caption", ), ), ], caption="Table Field", )
The image above illustrates the output from the example above.
With this component, the user can easily import the content from a file by clicking on the last icon from the toolbar menu.
The wizard assistance supports multiple types of file, the user just needs to inform which kind of configuration the file has.
By the end, it’s possible for the user select to which unit the values must be converted and which columns.
Accessing Table Field from Plugin:
In order to access this field from inside the plugin implementation, in C/C++, you need to use get_plugin_input_data_table_quantity()
get_plugin_input_data_table_quantity()
Accessing Table Field from Context:
When accessed from the Context(), the Table field will return a model, with information about all columns.
@data_model(icon="", caption="My Model") class MyModel: Table( rows=[ TableColumn( id='temperature', value=Quantity(value=1, unit='K', caption='Temperature Column Caption'), ), TableColumn( id='pressure', value=Quantity(value=2, unit='bar', caption='Pressure Column Caption'), ), ], caption="Table Field" ) # From Terminal >>> ctx.get_model("MyModel").table_field TableContainer([...]) >>> len(ctx.get_model("MyModel").table_field) 6 >>> len(ctx.get_model("MyModel").table_field) TableRow(temperature=Scalar(1.0, 'K', 'temperature'), pressure=Scalar(2.0, 'bar', 'pressure')) >>> ctx.get_model("MyModel").table_field[0].pressure Scalar(2.0, 'bar', 'pressure')
TableColumn
The TableColumn component provides columns for a Table() field. Currently only columns with a Quantity() fields are available.
Table()
Check out the documentation from Table() to see more details about the usage and how to retrieve values.
Boolean
The Boolean field provides a checkbox to select/deselect a property.
The Boolean fields have all options available from BaseField(), besides the listed the ones listed above: :param value: A boolean informing the initial state from the Field
@data_model(icon="", caption="My Plugin") class MyModel: boolean_field = Boolean( value=False, caption="Boolean Field", )
Accessing Boolean Field from Plugin:
In order to access this field from inside the plugin implementation, in C/C++, you need to use get_plugin_input_data_boolean()
When accessed from the Context(), the Boolean field will return a boolean value
@data_model(icon="", caption="My Plugin") class MyModel: quantity_field = Boolean( value=False, caption="Boolean Field", ) # From Terminal >>> ctx.get_model("MyModel").boolean_field False
FileContent
The FileContent component provides a platform-native file dialog to the user to be able to select a file. The name of the selected file will be available over the GUI.
If you want to make the file mandatory it is recommended to include a status monitor in your plugin to make sure that a file is selected.
@data_model(icon="", caption="My Plugin") class MyModel: file_content_field = FileContent(caption="FileContent Field")
Accessing FileContent Field from Plugin:
In order to access this field from inside the plugin implementation, in C/C++, you need to use get_plugin_input_data_file_content() together with get_plugin_input_data_file_content_size()
get_plugin_input_data_file_content()
get_plugin_input_data_file_content_size()
When accessed from the Context(), the FileContent field will return a FileContent object, a Model that represent a file from the filesystem.
Class FileContent
Return a Path object of the file.
The content from the file in binary format.
The size of the file in bytes.
Return a Datetime object, with the last time the file was modified
>>> ctx.get_model("MyModel").file_content_field.path WindowsPath('C:/ol-wax-1.wax')
>>> ctx.get_model("MyModel").file_content_field.content b"!Name of Table [...] "
>>> ctx.get_model("MyModel").file_content_field.size 90379
>>> ctx.get_model("MyModel").file_content_field.modified_data datetime.datetime(2019, 5, 10, 14, 22, 11, 50795)
group
The group layout is a container to organize ALFAsim types, only fields that derives from BaseField can be defined inside a group.
Example.:
@data_model(icon="", caption="My Model") class MyModel: string_field_1 = String(caption="Outside", value="Default") @group(caption="Group Container") class GroupMain: string_field_2 = String(value="Group 1", caption="Inside") bool_field = Boolean(value=True, caption="Boolean Field")
The image below shows the output from the example above.
group is a layout component, and will not have an attribute to be accessed through context or API.
tabs
Create a tab bar layout, to group multiples :func:”~alfasim_sdk.layout.tab” instances.
With the tabs, you can split up complex dialog into “pages” using a :func:”~alfasim_sdk.layout.tab” instance.
Notice that only classes decorated with :func:”~alfasim_sdk.layout.tab” can be placed inside a tab bar.
tab bar
@data_model(icon="", caption="My Model") class MyModel: field = String(caption="String outside tabs", value="Default") @tabs() class MainPage: @tab(caption="Fist Tab") class Tab1: field_1 = String(caption="First Tab", value="Default") @tab(caption="Second Tab") class Tab2: field_2 = String(caption="Second Tab", value="Default")
The image below shows the output from the command above.
tabs is a layout component, and will not have an attribute to be accessed through context or API.
tab
The tab represents a single entry, on the tabs() layout.
tabs()
Notice that only components available at the types modules can be placed inside a tab.
ErrorMessage
ErrorMessage allows the plugin to display a message over the status monitor, and signalize to the application to block the simulation until the issue is fixed.
model_name – Name of the model that issues the error.
message – Message that will be displayed over the status monitor.
Checkout the alfasim_get_status() for some examples of ErrorMessage() in action.
WarningMessage
WarningMessage allows the plugin to display a message to the user over the status monitor, and signalizes a minor issue that needs to be fixed but doesn’t block the simulation.
model_name – Name of the model that issues the warning.
Checkout the alfasim_get_status() for some examples of WarningMessage() in action.
The context class provides information about the current state of the application and the models implemented by the user.
The following methods provide an instance of Context() to inform the current state of the application:
Visible Expression parameter from all fields
Enable Expression parameter from all fields
alfasim_get_status() hook
get_edges
Return a list of all Edges available on ALFAsim. Each Edge is represented by an instance of EdgeInfo().
EdgeInfo()
Example of GetEdges
The image above has two Edges configured, in order to access the available Edges, it’s possible to use the method GetEdges as demonstrated below.
GetEdges
Accessing GetEdges from the context
>>> ctx.get_edges()[0] EdgeInfo(name='Pipe 1', number_of_phases_from_associated_pvt=2) >>> ctx.get_pipelines()[0].number_of_phases_from_associated_pvt 'Pipe 1'
Checkout the EdgeInfo() section to know more about the properties available.
get_model
Returns an instance of the given model_name.
model_name
The parameter model_name must be the name of a model defined within the plugin.
In the example below, the Context is used to access a property from the model MyModel
MyModel
ctx.GetModel("Acme") as exemplified in the code below.
ctx.GetModel("Acme")
Setting up the model
@data_model(caption="MyPlugin", icon="") class MyModel: name = String(value="ALFAsim", caption="Field") scalar = Quantity(value=1, unit="degC", caption="Field") @alfasim_sdk.hookimpl def alfasim_get_data_model_type(): return [MyModel]
Accessing the context
>>> ctx.get_model('MyModel') MyModel(name='ALFAsim', scalar=Scalar(1.0, 'degC', 'temperature')) >>> ctx.get_model('MyModel').name 'ALFAsim'
At runtime, you can also verify the names of the models defined by a given plugin. For this, you need to For more information check GetPluginInfoById()
GetPluginInfoById()
TypeError – When the given model_name does not exist.
FrozenInstanceError – When trying to modify a value
get_nodes
Return a list of all Nodes available on ALFAsim. Each Node is represented by an instance of alfasim_sdk.context.NodeInfo().
alfasim_sdk.context.NodeInfo()
Usage Example of GetNodes
The image above has three nodes configured, you can access this information by using the method GetNodes as demonstrated below.
GetNodes
>>> ctx.get_nodes[0] NodeInfo(name='Node 1', number_of_phases_from_associated_pvt=2) >>> ctx.get_nodes[0].name 'Node 1'
The values from NodeInfo are read-only, they cannot be modified.
Checkout the NodeInfo() section to know more about the properties available.
NodeInfo()
get_physics_options
Return the physics options from the current project from ALFAsim.
Example of GetPhysicsOptions
The image below shows a configuration from a given project.
It’s possible to access this information from inside the plugin, by using context api as demonstrate below.
Accessing GetPhysicsOptions from the context
>>> ctx.get_physics_options() PhysicsOptionsInfo( [...] ) >>> ctx.get_physics_options().emulsion_model.value 'EmulsionModelType.brinkman1952' >>> ctx.get_physics_options().hydrodynamic_model HydrodynamicModelInfo( [ ... ] ) >>> ctx.get_physics_options().hydrodynamic_model.fields ['gas', 'oil', 'droplet', 'bubble'] >>> ctx.get_physics_options().hydrodynamic_model.layers ['gas', 'oil'] >>> ctx.get_physics_options().hydrodynamic_model.phases ['gas', 'oil']
Checkout the PhysicsOptionsInfo() section to know more about the properties available.
PhysicsOptionsInfo()
get_pipelines
Return a list with all Pipes available on the Network from the Project. Each Pipe is represented by an instance of PipelineInfo().
PipelineInfo()
Usage Example of GetPipelines
The image above has two Pipelines configured, you can access this information by using the method GetPipelines as demonstrated below.
GetPipelines
>>> ctx.get_pipelines()[0] PipelineInfo(name='Pipe 1 > Pipeline', [ ... ]) >>> ctx.get_pipelines()[0].edge_name 'Pipe 1' >>> ctx.get_pipelines()[0].total_length Scalar(1000.0, 'm', 'length') >>> len(ctx.get_pipelines()[0].segments) 1
The values from PipelineInfo are read-only, they cannot be modified.
Checkout the PipelineInfo() section to know more about the properties available.
get_plugin_info_by_id
Similar to GetPluginsInfos() but returns a single instance of PluginInfo() from the given plugin_id parameter.
GetPluginsInfos()
PluginInfo()
plugin_id
Checkout the PluginInfo() section to know more about the properties available.
ValueError – When the plugin informed by plugin_id it’s not available.
get_plugins_infos
Return a list of all plugins available on ALFAsim. Each plugin is represented by an instance of PluginInfo().
Usage Example of GetPluginsInfos
The example demonstrated how you can access information about the plugin from using the GetPluginsInfos() method.
>>> ctx.get_plugins_infos() [PluginInfo(caption='myplugin', name='myplugin', enabled=True, models=['MyModel'])] >>> ctx.get_plugins_infos()[0].enabled True >>> ctx.get_plugins_infos()[0].models ['MyModel']
EdgeInfo
The EdgeInfo provides information about a Edge from ALFAsim, it provides the name of the Node and the number of phases that the associate pvt model has.
Edge
HydrodynamicModelInfo
HydrodynamicModelInfo provides information about which layer, fields, and phases the currently Hydrodynamic model is using.
NodeInfo
The NodeInfo provides information about a Node from ALFAsim, it provides the name of the Node and the number of phases that the associate PVT model has.
Node
PhysicsOptionsInfo
PhysicsOptionsInfo provides information about the Physics Options available at ALFAsim.
Physics Options
The following option can be accessed:
Emulsion Model: Informs which emulsion model the application is currently using. For more information about all options available check alfasim_sdk.context.EmulsionModelType
alfasim_sdk.context.EmulsionModelType
Solids Model: Informs the current solid model being used by the application For more information about all options available check alfasim_sdk.context.SolidsModelType
alfasim_sdk.context.SolidsModelType
Hydrodynamic Model: Provides a alfasim_sdk.context.HydrodynamicModelInfo informing which layers, fields and phases the application is currently using. For more information about all options available check alfasim_sdk.context.HydrodynamicModelInfo
alfasim_sdk.context.HydrodynamicModelInfo
PipelineInfo
The PipelineInfo provides information about the geometry of a pipeline.
name: Name associated with this Pipeline on ALFAsim
Pipeline
edge_name: Name of the edge that this Pipeline is associated with.
segments: List of segments associates with this Pipeline For more information check alfasim_sdk.context.PipelineSegmentInfo
total_length: Total length of the pipeline.
PipelineSegmentInfo
The PipelineSegmentInfo provides information about segments associated with a pipeline.
edge_name: name of the edge that the segment is associated with
start_position: Defines point where this segment starts in MD (measured depth).
inner_diameter: Inner diameter of pipe annulus.
When `is_custom` is true, the reported roughness is customized for this segment, otherwise, the reported roughness is the original reported by the wall.
`is_custom`
is_custom: Informs either the roughness value is custom or original from the wall
PluginInfo
PluginInfo provides information about the plugin name, its current state (either enabled or not) and all models defined from this plugin.
Here is listed the completed API available to implement the Solver Configuration Hooks.
Secondary Variables
Constants
AddField
Allows the plugin to add new fields to Hydrodynamic model.
An added field must be associated to a phase (Using AddPhase or UpdatePhase) and added to a layer (Using AddLayer or UpdateLayer)
UpdateLayer
name – Name of the new field.
This type is supposed to be used in the alfasim_configure_fields() hook.
Allows the plugin to add new phases to Hydrodynamic model.
name – Name of the new phase.
fields – List of fields names associated to the added phase. It is important to know how to calculate the state variables of fields.
primary_field – Reference field when a phase property calculation is performed through the fields of the phase.
is_solid – A boolean variable to identify if the added phase is solid.
This type is supposed to be used in the alfasim_configure_phases() hook.
alfasim_configure_phases()
Allows the plugin update existing phases of the Hydrodynamic model.
GAS_PHASE
OIL_PHASE
WATER_PHASE (If a three phase hydrodynamic model is used)
WATER_PHASE
additional_fields – List of additional fields names to be appended in the fields list of the phase.
Allows the plugin to add new layers to Hydrodynamic model.
name – Name of the new layer.
fields – List of fields names contained in the added layer.
continuous_field – Name of the continuous field of the added layer (must be in the fields list).
This type is supposed to be used in the alfasim_configure_layers() hook.
alfasim_configure_layers()
Allows the plugin to update existing layer of the Hydrodynamic model.
GAS_LAYER
OIL_LAYER
WATER_LAYER (If a three phase hydrodynamic model is used)
WATER_LAYER
name – Name of the updated layer.
additional_fields – List of additional fields names to be appended in the fields list of the layer.
Secondary variables are those variables that are not unknowns from the nonlinear system. That is, they are not directly solved in the nonlinear system, but they are calculated based on the nonlinear system results.
name – Plugin secondary variable name. This name will be used to access it in the Solver Hooks.
caption – Caption to be shown in the GUI (For output purpose).
type – a Type value.
Type
unit – A string with the unit of the variable.
visibility – a Visibility value.
Visibility
location – a Location value.
Location
multifield_scope – a Scope.
Scope
default_value – Default value to be set.
checked_on_gui_default – If the added variable has Visibility equal to Output, it indicates that this variable will be exported as output by default.
Output
The unit param accepts all units available on Barril Unit Manager, for more information read its documentation.
unit
This type is supposed to be used in the alfasim_get_additional_variables() hook.
Double: Double precision floating point data type.
Double
Int: Integral data type.
Int
Internal: The variable should only be used by the plugin, but not available to the end-user.
Internal
Output: The variable should be available to the end user, as a Property on Plot Window
Center: Center of the control volumes.
Center
Face: Faces of control volumes.
Face
Energy: One value for each energy equation (One for GLOBAL model and number of layers for LAYER model).
Energy
Global: One global value (or related to the mixture).
Global
Field: One value for each field of the hydrodynamic model.
Field
Layer: One value for each layer of the hydrodynamic model.
Layer
Phase: One value for each phase of the hydrodynamic model.
Phase
str(object=’‘) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.