4.5.8. User Plugins
Warning
The use of user plugins should be considered a last resort, especially for production simulations - most custom capabilities can be implemented using GPU-compatible, well-tested capabilities like string functions, user expressions, and multi-dimensional tables.
User plugins are another way that users can extend Aria’s capabilities using custom compiled code. Unlike user subroutines, which are functions with a fixed signature called to evaluate certain properties or terms, plugins are C++ classes that extend the Expression class and can be used interchangeably with native Aria expressions. Before making a plugin, it is recommended that a user reviews the Expression Overview.
Since Aria uses a Newton formulation, sensitivities are required from each expression. There are three ways of providing sensitivities in an expression:
Manually code them
Use numerical finite-differences
Use Forward Automatic Differentiation (FAD)
The FAD method is ideal for plugins because the sensitivities are performed analytically and are exact, though there may be a small performance degradation relative to manually coded sensitivities. For that reason, we recommend using the FAD approach when writing plugins.
4.5.8.1. Writing a Plugin
In this section we’ll write an example plugin to calculate density as . The first part of our plugin C file will define the plugin class.
#include <Aria_Plugin_Expression.h>
using namespace sierra::Aria;
class My_Density : public FAD_Expression
{
public:
My_Density(Expression_Manager * const manager,
const sierra::Identifier & expr_model_name,
const Subdomain_Tag & subdomain_tag,
const int & subindex,
const Phase_Label & phase_label,
const PrsrParamMap& params);
virtual ~My_Density() {}
virtual void compute_FAD_values(const ElementRange & elem_range);
private:
double a = 0.0;
double b = 0.0;
double c = 0.0;
double d = 0.0;
Scalar_FAD_Expression_Handle values;
Scalar_FAD_Expression_Handle temperature;
};
There are a few things to note in this definition
The class inherits from
FAD_Expressionso it does not require acompute_sensitivitiesmethod.The arguments to the constructor must be as-shown here.
You must define expression handles for the value being computed and any prerequisites.
The next part of the plugin C file is the constructor definition. This is where you read in any user-supplied parameters needed for your expression, define what expression your model provides (EXPR::DENSITY here), define the rank (tensor order) of the provided value, and define any prerequisites your expression needs (EXPR::TEMPERATURE here).
My_Density::My_Density(Expression_Manager * const manager,
const sierra::Identifier & expr_model_name,
const Subdomain_Tag & subdomain_tag,
const Int & subindex,
const Phase_Label & phase_label,
const PrsrParamMap& params)
: FAD_Expression(manager,subdomain_tag,subindex,phase_label)
{
my_tensor_order = 0;
my_expression_tag = Expression_Tag(EXPR::DENSITY,NO_OP,subindex,phase_label);
my_expression_model_name = expr_model_name;
// List the expressions that are required for this model.
const Expression_Tag temperature_tag(EXPR::TEMPERATURE,NO_OP,subindex,phase_label);
add_prereq(temperature_tag,temperature);
// Get my model parameters.
get_optional_parameter(params,"A",a);
get_optional_parameter(params,"B",b);
get_optional_parameter(params,"C",c);
get_optional_parameter(params,"D",d);
if(a == 0.0 && b == 0.0 && c == 0.0 && d == 0.0)
{
throw sierra::RuntimeUserError() << "ERROR: All MY_MODEL_DENSITY parameters are zero.";
}
// Make myself known to the manager.
register_myself(values);
}
The next component of the plugin C file is the compute_FAD_values method, where the actual density value is computed
void My_Density::compute_FAD_values(const ElementRange & elem_range)
{
for (auto elem : elem_range)
{
for(auto point : my_sizes.get_points())
{
const auto& T = temperature(elem,point);
values(elem,point) = a + T*(b + T*(c + T*(d)));
}
}
}
Note that since our Expression is a scalar in this example, the signature of values is values(elem,point). If our result was a vector or tensor, the signature would be values(elem,point,r) and values(elem,point,r,s), respectively.
The last thing we need to do is register the plugin
ExprPluginFactory<My_Density> my_density_creator("MY_MODEL_DENSITY");
The name provided here will define how we refer to the plugin in the input file (MY_MODEL) and what property it is valid for (DENSITY).
Note
The naming of the plugin here must follow the pattern of SOMENAME_PROPERTYNAME where PROPERTYNAME defines what it can be used for in the input file. For example BEST_MODEL_DENSITY or STUPENDOUS_ENERGY_FLUX could be referred to in the input file using
density = user_plugin name=best_model
BC flux for energy on surface_1 = user_plugin name=stupendous
4.5.8.2. Compiling a Plugin
User plugins are compiled to shared object (.so) files using the same procedure as User Subroutines. The following command will scan the input file for import .so files and compile them from source files of the same name.
sierra --make aria -i input.i
4.5.8.3. Using a Plugin
In the Aria input file, the plugin is loaded using the command
Begin Sierra My_Sierra_Job
Load User Plugin file ./My_Model_Plugins.so
To use a plugin for a material property, source, or flux you use the following syntax, where the name of the plugin (e.g. My_Model, My_Source, or My_Flux) was defined in your C file. Any parameters needed for your plugin are supplied in-line (e.g. a=1 b=-0.01).
Begin Aria Material Kryptonite
Density = User_Plugin name=My_Model a=1.0 b=-0.01
...
End
...
Begin Aria Region Region
...
Source for Energy on block_1 = User_Plugin name=My_Source
BC flux for Energy on surface_1 = User_Plugin name=My_Flux d=5.
...
End
4.5.8.4. Debugging a Plugin
Many times, you can check that your plugin is functioning properly by Postprocessing the value your plugin is computing and verifying that the values are as expected. However, if this is not sufficient there are a few other techniques you can use.
If you have coded sensitivities manually, you can use the --tftklog sens_check command line option to verify that your analytical sensitivities match numerical finite difference sensitivities.
You can also add logging statements in the plugin to check values directly, using the arialog.m(LOG_PLUGIN) command.
void My_Density::compute_FAD_values(const ElementRange & elem_range)
{
for (auto elem : elem_range)
{
for(auto point : my_sizes.get_points())
{
const auto& T = temperature(elem,point);
values(elem,point) = a + T*(b + T*(c + T*(d)));
arialog.m(LOG_PLUGIN) << "value(" << elem << "," << point << ") = " << value(elem,point)
<< " when T = " << T << Diag::endl;
}
}
}
The LOG_PLUGIN mask means this log stream is only activated when you run the code with --arialog plugin. There’s a performance penalty for having this logging code option present (even if you don’t turn the logging on) so you probably want to remove it once you’re done debugging.