Step 5: Develop a Kernel Object
In this step, the basic components of Kernel Objects will be presented. To demonstrate their use, a new Kernel
will be created to solve Darcy's Pressure equation, whose weak form was derived in the previous step. The concept of class inheritance shall also be demonstrated, as the object to solve Darcy's equation will inherit from the ADKernel
class.
Kernel Objects
The Kernels System in MOOSE is one for computing the residual contribution from a volumetric term within a PDE using the Galerkin FEM. One way to identify kernels is to check which term(s) in a weak form expression are multiplied by the gradient of the test function , or to check those which represent an integral over the whole domain .
In Listing 1, notice that the ADDiffusion
object is declared as one that inherits from the ADKernelGrad
base class, which, in turn, inherits from the ADKernel
base class. This means that ADDiffusion
is a unique object that builds upon what the MOOSE Kernels system is designed to do.
class ADDiffusion : public ADKernelGrad
(framework/include/kernels/ADDiffusion.h)"AD" (often used as a prefix for class names) stands for "Automatic Differentiation," which is a feature available to MOOSE application developers that drastically simplifies the implementation of new a MooseObject
.
Now, one might inspect the files that provide the base class, i.e., ADKernel.h
and ADKernel.C
, to see what tools are available and decide how to properly implement a new object of this type. Variable members that ADKernel
objects have access to include, but are not limited to, the following:
_u
,_grad_u
Value and gradient of the variable being operated on._test
,_grad_test
Value () and gradient () of the test function._i
Current index for the test function component._q_point
Coordinates of the current quadrature point._qp
Current quadrature point index.
There are several methods that ADKernel
(and Kernel
) objects may override. The most important ones are those which work to evaluate the kernel term in the weak form and they are explained in Table 1.
Base | Override | Use |
---|---|---|
Kernel ADKernel | computeQpResidual | Use when the term in the PDE is multiplied by both the test function and the gradient of the test function (_test and _grad_test must be applied) |
KernelValue ADKernelValue | precomputeQpResidual | Use when the term computed in the PDE is only multiplied by the test function (do not use _test in the override, it is applied automatically) |
KernelGrad ADKernelGrad | precomputeQpResidual | Use when the term computed in the PDE is only multiplied by the gradient of the test function (do not use _grad_test in the override, it is applied automatically) |
For this step, the method to be aware of is precomputeQpResidual()
; the same one used by ADDiffusion
. The "Qp
" stands for "quadrature point," which is a characteristic feature of the Gaussian quadrature. These are the points at which the weak form of a PDE is numerically integrated. Finite elements usually contain multiple quadrature points at specific, symmetrically placed locations and the Gauss quadrature formula is a summation over all of these points that yields an exact value for the integral of a polynomial over .
Demonstration
In the previous step, it was shown that the weak form of the Darcy pressure equation is the following:
(1)Eq. (1) must satisfy the following BVP: at the inlet (left) boundary, at the outlet (right) boundary, and on the remaining boundaries. Therefore, it is possible to drop the second term in Eq. (1) and express it, more simply, as
(2)where is a scalar and was substituted under the assertion that the permeability be isotropic, such that the full tensor may be consolidated into a single value. It was specified on the Problem Statement page that the viscosity of the fluid (water) is . Also, assume that the isotropic permeability represents the 1 mm steel sphere medium inside the pipe (obtained by setting in Problem Statement, Eq. (8)).
Source Code
To evaluate Eq. (2), a new ADKernelGrad
object can be created and it shall be called DarcyPressure
. Start by making the directories to store files for objects that are part of the Kernels System:
cd ~/projects/babbler
mkdir include/kernels src/kernels
In include/kernels
, create a file named DarcyPressure.h
and add the code given in Listing 2. Here, the DarcyPressure
class was defined as a type of ADKernelGrad
object, and so the header file ADKernelGrad.h
was included. A MooseObject
must have a validParams()
method and a constructor, and so these were included as part of the public
members. The precomputeQpResidual()
method was overridden so that Eq. (2) may set its returned value in accordance with Table 1. Finally, two variables, _permeability
and _viscosity
, were created to store the values for and , respectively, and were assigned const
types to ensure that their values aren't accidentally modified after they are set by the constructor method.
#pragma once
// Including the "ADKernel" base class here so we can extend it
#include "ADKernelGrad.h"
/**
* Computes the residual contribution: K / mu * grad_test * grad_u.
*/
class DarcyPressure : public ADKernelGrad
{
public:
static InputParameters validParams();
DarcyPressure(const InputParameters & parameters);
protected:
/// ADKernel objects must override precomputeQpResidual
virtual ADRealVectorValue precomputeQpResidual() override;
/// The variables which hold the value for K and mu
const Real _permeability;
const Real _viscosity;
};
In src/kernels
, create a file named DarcyPressure.C
and add the code given in Listing 3. Here, the header file for this object was included. Next, the registerMooseObject()
method was called for "BabblerApp"
and the DarcyPressure
object. The validParams()
method is the subject of the next step, so, for now, it is okay to simply copy and paste its definition. In the constructor method the _permeability
and _viscosity
attributes were set according to the values that were given earlier. Finally, the precomputeQpResidual()
method was programmed to return the left-hand side of Eq. (2), except that the (_grad_test
) term is automatically handled by the base class.
#include "DarcyPressure.h"
registerMooseObject("BabblerApp", DarcyPressure);
InputParameters
DarcyPressure::validParams()
{
InputParameters params = ADKernelGrad::validParams();
params.addClassDescription("Compute the diffusion term for Darcy pressure ($p$) equation: "
"$-\\nabla \\cdot \\frac{\\mathbf{K}}{\\mu} \\nabla p = 0$");
return params;
}
DarcyPressure::DarcyPressure(const InputParameters & parameters)
: ADKernelGrad(parameters),
// Set the coefficients for the pressure kernel
_permeability(0.8451e-09),
_viscosity(7.98e-04)
{
}
ADRealVectorValue
DarcyPressure::precomputeQpResidual()
{
return (_permeability / _viscosity) * _grad_u[_qp];
}
Since the source code has been modified, the executable must be recompiled:
cd ~/projects/babbler
make -j4
Input File
Instead of the ADDiffusion
class, the problem model should now use DarcyPressure
. In the pressure_diffusion.i
input file, which was created in Step 2, replace the [Kernels]
block with the following:
[Kernels]
[diffusion]
type = DarcyPressure # Zero-gravity, divergence-free form of Darcy's law
variable = pressure # Operate on the "pressure" variable from above
[]
[]
Now, execute the input file:
cd ~/projects/babbler/problems
../babbler-opt -i pressure_diffusion.i
Results
Visualize the solution with PEACOCK and confirm that it resembles that which is shown in Figure 1:
cd ~/projects/babbler/problems
~/projects/moose/python/peacock/peacock -r pressure_diffusion_out.e
One should be convinced that, because this is a steady-state problem, where the coefficient is constant and since the foregoing BVP is a simple one that involves only essential boundary conditions, the solution produced by the new DarcyPressure
object, shown in Figure 1, is identical to the previous one produced by the ADDiffusion
object.
Commit
Add the two new files, DarcyPressure.h
and DarcyPressure.C
, to the git tracker and update the pressure_diffusion.i
file:
cd ~/projects/babbler
git add include/kernels src/kernels problems/pressure_diffusion.i
Now, commit and push the changes to the remote repository:
git commit -m "developed kernel to solve Darcy pressure and updated the problem input file"
git push