# 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:

> 1. Manually code them

> 2. Use numerical finite-differences

> 3. 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_Expression` so it does not require a `compute_sensitivities` method.

> - 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.