MortarScalarBase

The MortarScalarBase is part of the scalar augmentation system to complement the ScalarKernel class. Its principal purpose is to add standard quadrature loops and assembly routines to handle the contributions from a single added scalar variable to a Mortar Constraint class, including the entire row of the Jacobian. These routines will assist with representing weak form terms in a partial differential equation involving scalar variables integrated over the surface of a non-conforming interface (mortar surface). As usual, this piece of physics is referred to as the "residual" and is evaluated at integration quadrature points along that interface. To implement your own physics in MOOSE, you create your own mortar constraint by subclassing the MOOSE MortarScalarBase class.

The mortar scalar augmentation system supports the use of automatic differentiation (AD) for residual calculations, as such there are two options for creating field-scalar coupling objects: MortarScalarBase and ADMortarScalarBase. To further understand automatic differentiation, please refer to the Automatic Differentiation page for more information.

Developers should read all sections; users can find Parameters described at the bottom.

Creation of Mortar Constraint Scalar Coupling Classes

MortarConstraint objects are designed to handle weak-form contributions from both surface-based lower-dimensional (Lagrange multiplier) variables and domain-based (primary and secondary) spatial variables. For these three focus spatial variables, its job is to contribute to the associated rows of the residual and the Jacobian matrix. Herein, as in the source code of MortarConstraints, these spatial variables will be called (see Mortar Parameters for the analogous input parameter names):

  • _var: Corresponds to a Lagrange Multiplier variable that lives on the lower dimensional block on the secondary face

  • _secondary_var: Primal variable on the secondary side of the mortar interface (lives on the interior elements)

  • _primary_var: Primal variable on the primary side of the mortar interface (lives on the interior elements).

The three focus spatial variables _var, _secondary_var, and _primary_var are each indicated by the name var below, and are differentiated by the mortar_type flag as one of "Primary". "Secondary", or "Lower". In a coupled (multi-physics) weak form, all interface integral terms containing the test function of one of these three variables are potential candidates for MortarConstraint contributions.

The philosophy of the scalar augmentation class MortarScalarBase is to add a single focus scalar variable referred to as _kappa to the MortarConstraint object so that all terms in the coupled weak form that involve the spatial variables, scalar variable, and/or their test functions can be assembled in one or multiple class instances. This philosophy is similar to how the lower dimensional variable _lambda is added to the element faces of DGKernel and IntegratedBC objects associated with the hybrid finite element method (HFEM). Documentation for that approach can be found HFEMDiffusion and HFEMDirichletBC along with the base classes DGLowerDKernel and LowerDIntegratedBC.

In a MortarScalarBase subclass, a naming scheme is established for the quadrature point methods of the two variable types: methods contributing to the test function of _kappa have "Scalar" near the front and methods contributing to the trial function of scalar variables in the Jacobian have "Scalar" at the end. The computeScalarQpResidual() function should be overridden (see Parameters for cases where the scalar should be suppressed). The computeQpResidual(mortar_type) function must be overridden as usual for MortarConstraint, although it may return zero.

For non-AD objects, several contributions to the Jacobian matrix can be optionally overridden for use in Newton-type nonlinear solvers. As mentioned later, the developer should choose and document which terms (rows) of the residual and terms (rows and columns) of the Jacobian will be attributed to an instance of the developed class. These choices can be motivated by whether some terms in the weak form can be or have already been implemented within other MOOSE classes.

  • computeQpJacobian(jacobian_type, jvar_num): Jacobian component d-var-residual / d-jvar

  • computeQpOffDiagJacobianScalar(mortar_type, svar_num): Jacobian component d-var-residual / d-svar

  • computeScalarQpJacobian(): Jacobian component d-_kappa-residual / d-_kappa

  • computeScalarQpOffDiagJacobian(mortar_type, jvar_num): Jacobian component d-_kappa-residual / d-jvar

  • computeScalarQpOffDiagJacobianScalar(svar_num): Jacobian component d-_kappa-residual / d-svar

Similar to the mortar_type flag mentioned above, the jacobian_type flag is used to distinguish the couplings between the test and trial functions of the three focus spatial variables. For example, jacobian_type = LowerSecondary indicates that the linearized weak form term from the _var test function and _secondary_var trial function should be evaluated. All nine combinations from the three focus variables are visited during the loops that call computeQpJacobian(jacobian_type, jvar_num). Also, loops over the coupled variables wrap around these quadrature loops: the integer for the spatial variable is jvar_num and the integer for the scalar variable is svar_num. Note that the jvar_num integer is different for _var and _secondary_var and for any other spatial variables coupled by the developer through derived classes of MortarScalarBase, so consult the examples of these methods below in Examples from Source Code for how to query the current target using logical tests.

Also, there are some pre-calculation routines that are called within the quadrature loop once before the loop over spatial variable test and shape functions as well as before the loop over scalar components. These methods are useful for material or stabilization calculations.

  • initScalarQpResidual(): evaluations depending on qp but independent of test functions

  • initScalarQpJacobian(jvar_num): evaluations depending on qp but independent of test and shape functions

  • initScalarQpOffDiagJacobian(jsvar): evaluations depending on qp but independent of test and shape functions

In addition to those mentioned in the MortarConstraints documentation, you have access to several member variables inside your MortarScalarBase class for computing the residual and Jacobian values in the above mentioned functions:

  • _h, _l: indices for the current test and trial scalar component respectively.

  • _kappa: value of the scalar variable this MortarConstraint operates on; indexed by _h (i.e. _kappa[_h]).

  • _kappa_var: ID number of this scalar variable; useful to differentiate from others.

  • _k_order: order (number of components) of the scalar variable.

Since the test and trial "shape" functions of a scalar are "1", variables are not required for that value. Examples of the source codes below demonstrate this fact.

warningwarning:AD global indexing required

ADMortarScalarBase only works with MOOSE configured with global AD indexing (the default).

Examples from Source Code

As mentioned, the computeScalarQpResidual method should be overridden for both flavors of mortar constraints, non-AD and AD. As an example, consider the scalar residual weak form terms of the PeriodicSegmentalConstraint class:

(1)

The computeScalarQpResidual method for the non-AD version of this class is provided in Listing 1, where _kappa_aux is equal to .

Listing 1: The C++ weak-form residual statement of Eq. (1).

Real
PeriodicSegmentalConstraint::computeScalarQpResidual()
{
  // Stability/penalty term for residual of scalar variable
  RealVectorValue dx(_phys_points_primary[_qp] - _phys_points_secondary[_qp]);
  Real r = -dx(_h) * _lambda[_qp];

  RealVectorValue kappa_aux_vec(_kappa_aux[0], 0, 0);
  if (_k_order == 2)
  {
    kappa_aux_vec(1) = _kappa_aux[1];
  }
(moose/framework/src/constraints/PeriodicSegmentalConstraint.C)

Meanwhile, the contribution to the lower spatial variable residual of this object is associated with Eq. (2) and implemented in Listing 2 (note that the scalar variable _kappa is termed as in this weak form).

(2)

Listing 2: The C++ weak-form residual statement of Eq. (2).

Real
PeriodicSegmentalConstraint::computeQpResidual(const Moose::MortarType mortar_type)
{
  RealVectorValue dx(_phys_points_primary[_qp] - _phys_points_secondary[_qp]);
  RealVectorValue kappa_vec(_kappa[0], 0, 0);
  if (_k_order == 2)
    kappa_vec(1) = _kappa[1];
  else if (_k_order == 3)
  {
    kappa_vec(1) = _kappa[1];
    kappa_vec(2) = _kappa[2];
  }
(moose/framework/src/constraints/PeriodicSegmentalConstraint.C)

The other contributions to the lower spatial variable residual of this object is associated with Eq. (3) and implemented in Listing 3 within the EqualValueConstraint class.

(3)

Listing 3: The C++ weak-form residual statement of Eq. (3).

case Moose::MortarType::Lower:
    {
      auto residual = (_u_primary[_qp] - _u_secondary[_qp]) * _test[_i][_qp];

      if (_stabilize)
      {
        // secondary
        residual -= _delta * _lower_secondary_volume * _test[_i][_qp] *
                    (_lambda[_qp] - _diff_secondary[_qp] * _grad_u_secondary[_qp] * _normals[_qp]);

        // primary
        residual -= _delta * _lower_primary_volume * _test[_i][_qp] *
                    (_lambda[_qp] - _diff_primary[_qp] * _grad_u_primary[_qp] * _normals[_qp]);
      }
(moose/framework/src/constraints/EqualValueConstraint.C)

For an example of the contributions to _primary_var and _secondary_var residuals, consider the penalty version of the periodic constraint associated with Eq. (4) and implemented in Listing 4 within the PenaltyPeriodicSegmentalConstraint class.

(4)

Listing 4: The C++ weak-form residual statement of Eq. (4).

Real
PenaltyPeriodicSegmentalConstraint::computeQpResidual(const Moose::MortarType mortar_type)
{
  // Compute penalty parameter times x-jump times average heat flux
  RealVectorValue dx(_phys_points_primary[_qp] - _phys_points_secondary[_qp]);
  RealVectorValue kappa_vec(_kappa[0], 0, 0);
  if (_k_order == 2)
    kappa_vec(1) = _kappa[1];
  else if (_k_order == 3)
  {
    kappa_vec(1) = _kappa[1];
    kappa_vec(2) = _kappa[2];
  }
(moose/framework/src/constraints/PenaltyPeriodicSegmentalConstraint.C)

The PeriodicSegmentalConstraint class also overrides the computeScalarQpOffDiagJacobian method to define the Jacobian term related to Eq. (1) as shown in Listing 5.

Listing 5: The C++ weak-form Jacobian for d-_kappa-residual / d-jvar.

Real
PeriodicSegmentalConstraint::computeScalarQpOffDiagJacobian(const Moose::MortarType mortar_type,
                                                            const unsigned int jvar_num)
{
  // Test if jvar is the ID of the primary variables and not some other random variable
  switch (mortar_type)
  {
    case Moose::MortarType::Lower:
      if (!_var || _var->number() != jvar_num)
        return 0;
      break;
    default:
      return 0;
  }
(moose/framework/src/constraints/PeriodicSegmentalConstraint.C)

Notice that there is a conditional to confirm that the coupled jvar is the focus lower variable _var, otherwise it returns zero.

Similarly, it also overrides the computeQpOffDiagJacobianScalar method to define the Jacobian term related to Eq. (2) as shown in Listing 6.

Listing 6: The C++ weak-form Jacobian for d-_var-residual / d-svar.

Real
PeriodicSegmentalConstraint::computeQpOffDiagJacobianScalar(const Moose::MortarType mortar_type,
                                                            const unsigned int svar_num)
{
  if (svar_num != _kappa_var)
    return 0;

  // Stability/penalty term for Jacobian
  RealVectorValue dx(_phys_points_primary[_qp] - _phys_points_secondary[_qp]);
  Real jac = -dx(_h);

  switch (mortar_type)
  {
    case Moose::MortarType::Lower: // Residual_sign -1  ddeltaU_ddisp sign 1;
      jac *= _test[_i][_qp];
      break;
    default:
      return 0;
  }
(moose/framework/src/constraints/PeriodicSegmentalConstraint.C)

Also notice the conditional that confirms the coupled svar is the focus scalar _kappa, otherwise it returns zero.

The AD version of this object, ADPeriodicSegmentalConstraint, only requires the residual implementation; as such it overrides computeScalarQpResidual and computeQpResidual as follows.

Listing 7: The C++ AD weak-form residual statement of Eq. (1).

ADPeriodicSegmentalConstraint::computeScalarQpResidual()
{
  // Stability/penalty term for residual of scalar variable
  RealVectorValue dx(_phys_points_primary[_qp] - _phys_points_secondary[_qp]);
  ADReal r = -dx(_h) * _lambda[_qp];

  RealVectorValue kappa_aux_vec(_kappa_aux[0], 0, 0);
  if (_k_order == 2)
  {
    kappa_aux_vec(1) = _kappa_aux[1];
  }
(moose/framework/src/constraints/ADPeriodicSegmentalConstraint.C)

Listing 8: The C++ AD weak-form residual statement of Eq. (2).

ADPeriodicSegmentalConstraint::computeQpResidual(const Moose::MortarType mortar_type)
{
  RealVectorValue dx(_phys_points_primary[_qp] - _phys_points_secondary[_qp]);
  ADRealVectorValue kappa_vec(_kappa[0], 0, 0);
  Moose::derivInsert(kappa_vec(0).derivatives(), _kappa_var_ptr->dofIndices()[0], 1);
  if (_k_order == 2)
  {
    kappa_vec(1) = _kappa[1];
    Moose::derivInsert(kappa_vec(1).derivatives(), _kappa_var_ptr->dofIndices()[1], 1);
  }
(moose/framework/src/constraints/ADPeriodicSegmentalConstraint.C)

Depending upon the weak form and its coupling terms between spatial and scalar variables, not all of the methods listed in Creation of Mortar Constraint Scalar Coupling Classes need to be overridden.

The scalar augmentation system is designed such that multiple scalar variables can be coupled to an instance of the MortarConstraint class, each focusing on one scalar from the list. This approach is similar to how SolidMechanics module classes operator on one component variable of the displacement vector field and are coupled to the other components. The developer can decide how to organize the coupling and off-diagonal Jacobian terms in a logical way and document this for the user.

An example for decomposing the coupling terms and having multiple scalar variables are contained in the source files of the framework test directory as well as input file testperiodicsole.i, with listings below. The comments within these header and source files should be consulted to visualize how the rows and columns of the relevant residual and Jacobian contributions are handled.

Listing 9: Decomposition of spatial and scalar variable contributions by x and y components.

/// Test object to illustrate coupling between mortar spatial variables
/// and two separate scalar variables, kappa and kappa_other. The solution
/// is equivalent to PenaltyPeriodicSegmentalConstraint.
///
/// The kappa variable is split into two scalars: the first component called '_kappa'
/// herein and all other components called '_kappa_other' herein.
/// This decomposition is ONLY valid for 2D problems with a macro scalar vector [kappa_x kappa_y]
/// applied as a periodic boundary condition. For parameter _alpha = 0, the primary
/// scalar (_kappa) is kappa_x and the coupled scalar is kappa_y. For parameter _alpha = 1, the primary
/// scalar (_kappa) is kappa_y and the coupled scalar is kappa_x.
/// That mapping MUST be handled in the input file.
///
/// Thus, each instance of TestPeriodicSole acts on both primary/secondary field variables
/// and one scalar variable (_svar_alpha). The job of the constraint is to assemble the
/// residual and Jacobian terms arising from _svar_alpha (namely, selected entries).
/// It assembles one row of the Jacobian for the scalar variable as well as the couplings of that
/// scalar to the primary and secondary spatial variables, using logical checks with _alpha.
/// The entries for the other field/scalar variables are handled by the other instance of the
/// constraint, which have the opposite value of _alpha. The logical checks ensure the proper
/// decomposition of the jobs.
///
/// In summary, for x=disp_x etc. and k=kappa_x and a=kappa_y, then the contributions of the instances are
/// PenaltyEqualValueConstraint
/// R = [Rss+p, Rps+p, 0, 0]^T
/// J = [Jss, Jsp, 000, 000
///      Jps, Jpp, 000, 000
///      000, 000, 000, 000
///      000, 000, 000, 000]
/// TestPeriodicSole
/// _alpha=0
/// R = [Rsk, Rpk,  Rk,  00]^T
/// J = [000, 000, Jsk, 000
///      000, 000, Jpk, 000
///      Jks, Jkp, Jkk, Jka
///      000, 000, 000, 000]
/// _alpha=1
/// R = [Rsa, Rpa,  00,  Ra]^T
/// J = [000, 000, 000, Jsa
///      000, 000, 000, Jpa
///      000, 000, 000, 000
///      Jas, Jap, Jak, Jaa]
///
/// In this manner, the full R and J are obtained with NO duplication of jobs:
/// R = [Rs,  Rp,  Rk,  Ra ]^T
/// J = [Jss, Jsp, Jsk, Jsa
///      Jps, Jpp, Jpk, Jpa
///      Jks, Jkp, Jkk, Jka
///      Jas, Jap, Jak, Jaa]
///

class TestPeriodicSole : public DerivativeMaterialInterface<MortarScalarBase>
{
public:
  static InputParameters validParams();
  TestPeriodicSole(const InputParameters & parameters);

protected:
  virtual void precalculateResidual() override;
  virtual void precalculateJacobian() override;
  virtual void initScalarQpResidual() override;

  /**
   * Method for computing the residual at quadrature points
   */
  virtual Real computeQpResidual(Moose::MortarType mortar_type) override;
  Real computeQpJacobian(Moose::ConstraintJacobianType /*jacobian_type*/,
                         unsigned int /*jvar*/) override
  {
    return 0;
  }
(moose/test/include/constraints/TestPeriodicSole.h)
[Constraints]
  [mortarlr]
    type = PenaltyEqualValueConstraint
    primary_boundary = '11'
    secondary_boundary = '13'
    primary_subdomain = 'primary_right'
    secondary_subdomain = 'secondary_left'
    secondary_variable = u
    correct_edge_dropping = true
    penalty_value = 1.e3
  []
  [periodiclrx]
    type = TestPeriodicSole
    primary_boundary = '11'
    secondary_boundary = '13'
    primary_subdomain = 'primary_right'
    secondary_subdomain = 'secondary_left'
    secondary_variable = u
    kappa = kappa_x
    kappa_aux = kappa_aux
    component = 0
    kappa_other = kappa_y
    correct_edge_dropping = true
    penalty_value = 1.e3
  []
  [periodiclry]
    type = TestPeriodicSole
    primary_boundary = '11'
    secondary_boundary = '13'
    primary_subdomain = 'primary_right'
    secondary_subdomain = 'secondary_left'
    secondary_variable = u
    kappa = kappa_y
    kappa_aux = kappa_aux
    component = 1
    kappa_other = kappa_x
    correct_edge_dropping = true
    penalty_value = 1.e3
  []
  [mortarbt]
    type = PenaltyEqualValueConstraint
    primary_boundary = '12'
    secondary_boundary = '10'
    primary_subdomain = 'primary_top'
    secondary_subdomain = 'secondary_bottom'
    secondary_variable = u
    correct_edge_dropping = true
    penalty_value = 1.e3
  []
  [periodicbtx]
    type = TestPeriodicSole
    primary_boundary = '12'
    secondary_boundary = '10'
    primary_subdomain = 'primary_top'
    secondary_subdomain = 'secondary_bottom'
    secondary_variable = u
    kappa = kappa_x
    kappa_aux = kappa_aux
    component = 0
    kappa_other = kappa_y
    correct_edge_dropping = true
    penalty_value = 1.e3
  []
  [periodicbty]
    type = TestPeriodicSole
    primary_boundary = '12'
    secondary_boundary = '10'
    primary_subdomain = 'primary_top'
    secondary_subdomain = 'secondary_bottom'
    secondary_variable = u
    kappa = kappa_y
    kappa_aux = kappa_aux
    component = 1
    kappa_other = kappa_x
    correct_edge_dropping = true
    compute_scalar_residuals = true
    penalty_value = 1.e3
  []
[]
(moose/test/tests/mortar/periodic_segmental_constraint/testperiodicsole.i)
commentnote:Displaced mesh features untested

The displaced mesh features are not yet tested for the scalar augmentation system.

Parameters

There is one required parameters the user must supply for a mortar constraint derived from MortarScalarBase:

  • scalar_variable: the focus scalar variable of the mortar constraint, for which assembly of the residual and Jacobian contributions will occur. It must be a MooseScalarVariable. This parameter may be renamed in a derived class to be more physically meaningful.

If the scalar_variable parameter is not specified, then the derived class will behave identically to a regular MortarConstraint, namely without any scalar functionality. This feature is useful if the scalar augmentation in inserted into a class structure with several levels and not all derived members use scalar variables.

As an example, the parameter listing is shown below for the PeriodicSegmentalConstraint object with the scalar_variable parameter renamed to epsilon:

[Constraints]
  [mortarlr]
    type = EqualValueConstraint
    primary_boundary = '11'
    secondary_boundary = '13'
    primary_subdomain = 'primary_right'
    secondary_subdomain = 'secondary_left'
    secondary_variable = u
    variable = lm1
    correct_edge_dropping = true
  []
  [periodiclr]
    type = PeriodicSegmentalConstraint
    primary_boundary = '11'
    secondary_boundary = '13'
    primary_subdomain = 'primary_right'
    secondary_subdomain = 'secondary_left'
    secondary_variable = u
    epsilon = epsilon
    sigma = sigma
    variable = lm1
    correct_edge_dropping = true
  []
  [mortarbt]
    type = EqualValueConstraint
    primary_boundary = '12'
    secondary_boundary = '10'
    primary_subdomain = 'primary_top'
    secondary_subdomain = 'secondary_bottom'
    secondary_variable = u
    variable = lm2
    correct_edge_dropping = true
  []
  [periodicbt]
    type = PeriodicSegmentalConstraint
    primary_boundary = '12'
    secondary_boundary = '10'
    primary_subdomain = 'primary_top'
    secondary_subdomain = 'secondary_bottom'
    secondary_variable = u
    epsilon = epsilon
    sigma = sigma
    variable = lm2
    correct_edge_dropping = true
  []
[]
(moose/test/tests/mortar/periodic_segmental_constraint/periodic_simple2d.i)

Note: to avoid an error message "Variable 'kappa' does not exist in this system", the following block should be added to the input file:

[Problem]
  kernel_coverage_check = false
  error_on_jacobian_nonzero_reallocation = true
[]
(moose/test/tests/mortar/periodic_segmental_constraint/periodic_simple2d.i)

There is one optional parameters that can be supplied to`MortarScalarBase` classes:

  • compute_scalar_residuals: Whether to compute scalar residuals. This will automatically be set to false if a scalar_variable parameter is not supplied. Other cases where the user may want to set this to false is during testing when the scalar variable is an AuxVariable and not a solution variable in the system. This parameter has a similar usage as the compute_lm_residuals and compute_primal_residuals for all Mortar objects.