LCOV - code coverage report
Current view: top level - src/kernels - PorousFlowDarcyBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose porous_flow: #31405 (292dce) with base fef103 Lines: 196 219 89.5 %
Date: 2025-09-04 07:55:56 Functions: 11 15 73.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "PorousFlowDarcyBase.h"
      11             : 
      12             : #include "Assembly.h"
      13             : #include "MooseMesh.h"
      14             : #include "MooseVariable.h"
      15             : #include "SystemBase.h"
      16             : 
      17             : #include "libmesh/quadrature.h"
      18             : 
      19             : InputParameters
      20        9256 : PorousFlowDarcyBase::validParams()
      21             : {
      22        9256 :   InputParameters params = Kernel::validParams();
      23       18512 :   params.addRequiredParam<RealVectorValue>("gravity",
      24             :                                            "Gravitational acceleration vector downwards (m/s^2)");
      25       18512 :   params.addRequiredParam<UserObjectName>(
      26             :       "PorousFlowDictator", "The UserObject that holds the list of PorousFlow variable names");
      27       18512 :   params.addParam<unsigned>("full_upwind_threshold",
      28       18512 :                             5,
      29             :                             "If, for each timestep, the number of "
      30             :                             "upwind-downwind swaps in an element is less than "
      31             :                             "this quantity, then full upwinding is used for that element.  "
      32             :                             "Otherwise the fallback scheme is employed.");
      33       18512 :   MooseEnum fallback_enum("quick harmonic", "quick");
      34       18512 :   params.addParam<MooseEnum>("fallback_scheme",
      35             :                              fallback_enum,
      36             :                              "quick: use nodal mobility without "
      37             :                              "preserving mass.  harmonic: use a "
      38             :                              "harmonic mean of nodal mobilities "
      39             :                              "and preserve fluid mass");
      40        9256 :   params.addClassDescription("Fully-upwinded advective Darcy flux");
      41        9256 :   return params;
      42        9256 : }
      43             : 
      44        5100 : PorousFlowDarcyBase::PorousFlowDarcyBase(const InputParameters & parameters)
      45             :   : Kernel(parameters),
      46        5100 :     _permeability(getMaterialProperty<RealTensorValue>("PorousFlow_permeability_qp")),
      47        5100 :     _dpermeability_dvar(
      48        5100 :         getMaterialProperty<std::vector<RealTensorValue>>("dPorousFlow_permeability_qp_dvar")),
      49       10200 :     _dpermeability_dgradvar(getMaterialProperty<std::vector<std::vector<RealTensorValue>>>(
      50             :         "dPorousFlow_permeability_qp_dgradvar")),
      51        5100 :     _fluid_density_node(
      52        5100 :         getMaterialProperty<std::vector<Real>>("PorousFlow_fluid_phase_density_nodal")),
      53       10200 :     _dfluid_density_node_dvar(getMaterialProperty<std::vector<std::vector<Real>>>(
      54             :         "dPorousFlow_fluid_phase_density_nodal_dvar")),
      55       10200 :     _fluid_density_qp(getMaterialProperty<std::vector<Real>>("PorousFlow_fluid_phase_density_qp")),
      56       10200 :     _dfluid_density_qp_dvar(getMaterialProperty<std::vector<std::vector<Real>>>(
      57             :         "dPorousFlow_fluid_phase_density_qp_dvar")),
      58       10200 :     _fluid_viscosity(getMaterialProperty<std::vector<Real>>("PorousFlow_viscosity_nodal")),
      59        5100 :     _dfluid_viscosity_dvar(
      60        5100 :         getMaterialProperty<std::vector<std::vector<Real>>>("dPorousFlow_viscosity_nodal_dvar")),
      61       10200 :     _pp(getMaterialProperty<std::vector<Real>>("PorousFlow_porepressure_nodal")),
      62       10200 :     _grad_p(getMaterialProperty<std::vector<RealGradient>>("PorousFlow_grad_porepressure_qp")),
      63       10200 :     _dgrad_p_dgrad_var(getMaterialProperty<std::vector<std::vector<Real>>>(
      64             :         "dPorousFlow_grad_porepressure_qp_dgradvar")),
      65       10200 :     _dgrad_p_dvar(getMaterialProperty<std::vector<std::vector<RealGradient>>>(
      66             :         "dPorousFlow_grad_porepressure_qp_dvar")),
      67        5100 :     _dictator(getUserObject<PorousFlowDictator>("PorousFlowDictator")),
      68        5100 :     _num_phases(_dictator.numPhases()),
      69       10200 :     _gravity(getParam<RealVectorValue>("gravity")),
      70        5100 :     _perm_derivs(_dictator.usePermDerivs()),
      71       10200 :     _full_upwind_threshold(getParam<unsigned>("full_upwind_threshold")),
      72       10200 :     _fallback_scheme(getParam<MooseEnum>("fallback_scheme").getEnum<FallbackEnum>()),
      73        5100 :     _proto_flux(_num_phases),
      74        5100 :     _jacobian(_num_phases),
      75        5100 :     _num_upwinds(),
      76        5100 :     _num_downwinds()
      77             : {
      78             : #ifdef LIBMESH_HAVE_TBB_API
      79             :   if (libMesh::n_threads() > 1)
      80             :     mooseWarning("PorousFlowDarcyBase: num_upwinds and num_downwinds may not be computed "
      81             :                  "accurately when using TBB and greater than 1 thread");
      82             : #endif
      83        5100 : }
      84             : 
      85             : void
      86       44791 : PorousFlowDarcyBase::timestepSetup()
      87             : {
      88       44791 :   Kernel::timestepSetup();
      89       44791 :   _num_upwinds = std::unordered_map<unsigned, std::vector<std::vector<unsigned>>>();
      90       44791 :   _num_downwinds = std::unordered_map<unsigned, std::vector<std::vector<unsigned>>>();
      91       44791 : }
      92             : 
      93             : Real
      94   292638888 : PorousFlowDarcyBase::darcyQp(unsigned int ph) const
      95             : {
      96   292638888 :   return _grad_test[_i][_qp] *
      97   292638888 :          (_permeability[_qp] * (_grad_p[_qp][ph] - _fluid_density_qp[_qp][ph] * _gravity));
      98             : }
      99             : 
     100             : Real
     101   948858376 : PorousFlowDarcyBase::darcyQpJacobian(unsigned int jvar, unsigned int ph) const
     102             : {
     103   948858376 :   if (_dictator.notPorousFlowVariable(jvar))
     104             :     return 0.0;
     105             : 
     106   948858376 :   const unsigned int pvar = _dictator.porousFlowVariableNum(jvar);
     107             : 
     108             :   RealVectorValue deriv =
     109   948858376 :       _permeability[_qp] * (_grad_phi[_j][_qp] * _dgrad_p_dgrad_var[_qp][ph][pvar] -
     110   948858376 :                             _phi[_j][_qp] * _dfluid_density_qp_dvar[_qp][ph][pvar] * _gravity);
     111             : 
     112   948858376 :   deriv += _permeability[_qp] * (_dgrad_p_dvar[_qp][ph][pvar] * _phi[_j][_qp]);
     113             : 
     114   948858376 :   if (_perm_derivs)
     115             :   {
     116     6668872 :     deriv += _dpermeability_dvar[_qp][pvar] * _phi[_j][_qp] *
     117     6668872 :              (_grad_p[_qp][ph] - _fluid_density_qp[_qp][ph] * _gravity);
     118    26675488 :     for (const auto i : make_range(Moose::dim))
     119    20006616 :       deriv += _dpermeability_dgradvar[_qp][i][pvar] * _grad_phi[_j][_qp](i) *
     120    20006616 :                (_grad_p[_qp][ph] - _fluid_density_qp[_qp][ph] * _gravity);
     121             :   }
     122             : 
     123   948858376 :   return _grad_test[_i][_qp] * deriv;
     124             : }
     125             : 
     126             : Real
     127           0 : PorousFlowDarcyBase::computeQpResidual()
     128             : {
     129           0 :   mooseError("PorousFlowDarcyBase: computeQpResidual called");
     130             :   return 0.0;
     131             : }
     132             : 
     133             : void
     134     5330621 : PorousFlowDarcyBase::computeResidual()
     135             : {
     136     5330621 :   computeResidualAndJacobian(JacRes::CALCULATE_RESIDUAL, 0.0);
     137     5330621 : }
     138             : 
     139             : void
     140           0 : PorousFlowDarcyBase::computeJacobian()
     141             : {
     142           0 :   computeResidualAndJacobian(JacRes::CALCULATE_JACOBIAN, _var.number());
     143           0 : }
     144             : 
     145             : void
     146     6434243 : PorousFlowDarcyBase::computeOffDiagJacobian(const unsigned int jvar)
     147             : {
     148     6434243 :   computeResidualAndJacobian(JacRes::CALCULATE_JACOBIAN, jvar);
     149     6434243 : }
     150             : 
     151             : void
     152    11764864 : PorousFlowDarcyBase::computeResidualAndJacobian(JacRes res_or_jac, unsigned int jvar)
     153             : {
     154    11764864 :   if ((res_or_jac == JacRes::CALCULATE_JACOBIAN) && _dictator.notPorousFlowVariable(jvar))
     155           0 :     return;
     156             : 
     157             :   // The PorousFlow variable index corresponding to the variable number jvar
     158             :   const unsigned int pvar =
     159    11764864 :       ((res_or_jac == JacRes::CALCULATE_JACOBIAN) ? _dictator.porousFlowVariableNum(jvar) : 0);
     160             : 
     161    11764864 :   prepareMatrixTag(_assembly, _var.number(), jvar);
     162    11764864 :   if ((_local_ke.n() == 0) && (res_or_jac == JacRes::CALCULATE_JACOBIAN)) // this removes a problem
     163             :                                                                           // encountered in the
     164             :                                                                           // initial timestep when
     165             :                                                                           // use_displaced_mesh=true
     166             :     return;
     167             : 
     168             :   // The number of nodes in the element
     169    11764864 :   const unsigned int num_nodes = _test.size();
     170             : 
     171             :   // Compute the residual and jacobian without the mobility terms. Even if we are computing the
     172             :   // Jacobian we still need this in order to see which nodes are upwind and which are downwind.
     173    26121565 :   for (unsigned ph = 0; ph < _num_phases; ++ph)
     174             :   {
     175    14356701 :     _proto_flux[ph].assign(num_nodes, 0);
     176    68415025 :     for (_qp = 0; _qp < _qrule->n_points(); _qp++)
     177             :     {
     178   346697212 :       for (_i = 0; _i < num_nodes; ++_i)
     179   292638888 :         _proto_flux[ph][_i] += _JxW[_qp] * _coord[_qp] * darcyQp(ph);
     180             :     }
     181             :   }
     182             : 
     183             :   // for this element, record whether each node is "upwind" or "downwind" (or neither)
     184    11764864 :   const unsigned elem = _current_elem->id();
     185    11764864 :   if (_num_upwinds.find(elem) == _num_upwinds.end())
     186             :   {
     187     2665294 :     _num_upwinds[elem] = std::vector<std::vector<unsigned>>(_num_phases);
     188     2665294 :     _num_downwinds[elem] = std::vector<std::vector<unsigned>>(_num_phases);
     189     2847538 :     for (unsigned ph = 0; ph < _num_phases; ++ph)
     190             :     {
     191     1514891 :       _num_upwinds[elem][ph].assign(num_nodes, 0);
     192     1514891 :       _num_downwinds[elem][ph].assign(num_nodes, 0);
     193             :     }
     194             :   }
     195             :   // record the information once per nonlinear iteration
     196    11764864 :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN && jvar == _var.number())
     197             :   {
     198     6903932 :     for (unsigned ph = 0; ph < _num_phases; ++ph)
     199             :     {
     200    16893215 :       for (unsigned nod = 0; nod < num_nodes; ++nod)
     201             :       {
     202    13098238 :         if (_proto_flux[ph][nod] > 0)
     203     6337336 :           _num_upwinds[elem][ph][nod]++;
     204     6760902 :         else if (_proto_flux[ph][nod] < 0)
     205     6266114 :           _num_downwinds[elem][ph][nod]++;
     206             :       }
     207             :     }
     208             :   }
     209             : 
     210             :   // based on _num_upwinds and _num_downwinds, calculate the maximum number
     211             :   // of upwind-downwind swaps that have been encountered in this timestep
     212             :   // for this element
     213    11764864 :   std::vector<unsigned> max_swaps(_num_phases, 0);
     214    26121565 :   for (unsigned ph = 0; ph < _num_phases; ++ph)
     215             :   {
     216    64081777 :     for (unsigned nod = 0; nod < num_nodes; ++nod)
     217    49725076 :       max_swaps[ph] = std::max(
     218    49725076 :           max_swaps[ph], std::min(_num_upwinds[elem][ph][nod], _num_downwinds[elem][ph][nod]));
     219             :   }
     220             : 
     221             :   // size the _jacobian correctly and calculate it for the case residual = _proto_flux
     222    11764864 :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     223             :   {
     224    14463114 :     for (unsigned ph = 0; ph < _num_phases; ++ph)
     225             :     {
     226     8028871 :       _jacobian[ph].resize(_local_ke.m());
     227    35726889 :       for (_i = 0; _i < _test.size(); _i++)
     228             :       {
     229    27698018 :         _jacobian[ph][_i].assign(_local_ke.n(), 0.0);
     230   149756918 :         for (_j = 0; _j < _phi.size(); _j++)
     231  1070917276 :           for (_qp = 0; _qp < _qrule->n_points(); _qp++)
     232   948858376 :             _jacobian[ph][_i][_j] += _JxW[_qp] * _coord[_qp] * darcyQpJacobian(jvar, ph);
     233             :       }
     234             :     }
     235             :   }
     236             : 
     237             :   // Loop over all the phases, computing the mass flux, which
     238             :   // gets placed into _proto_flux, and the derivative of this
     239             :   // which gets placed into _jacobian
     240    26121565 :   for (unsigned int ph = 0; ph < _num_phases; ++ph)
     241             :   {
     242    14356701 :     if (max_swaps[ph] < _full_upwind_threshold)
     243    14355105 :       fullyUpwind(res_or_jac, ph, pvar);
     244             :     else
     245             :     {
     246        1596 :       switch (_fallback_scheme)
     247             :       {
     248         596 :         case FallbackEnum::QUICK:
     249         596 :           quickUpwind(res_or_jac, ph, pvar);
     250             :           break;
     251        1000 :         case FallbackEnum::HARMONIC:
     252        1000 :           harmonicMean(res_or_jac, ph, pvar);
     253             :           break;
     254             :       }
     255             :     }
     256             :   }
     257             : 
     258             :   // Add results to the Residual or Jacobian
     259    11764864 :   if (res_or_jac == JacRes::CALCULATE_RESIDUAL)
     260             :   {
     261     5330621 :     prepareVectorTag(_assembly, _var.number());
     262    24550651 :     for (_i = 0; _i < _test.size(); _i++)
     263    41247088 :       for (unsigned int ph = 0; ph < _num_phases; ++ph)
     264    22027058 :         _local_re(_i) += _proto_flux[ph][_i];
     265     5330621 :     accumulateTaggedLocalResidual();
     266             : 
     267     5330621 :     if (_has_save_in)
     268           0 :       for (unsigned int i = 0; i < _save_in.size(); i++)
     269           0 :         _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
     270             :   }
     271             : 
     272    11764864 :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     273             :   {
     274    29851853 :     for (_i = 0; _i < _test.size(); _i++)
     275   132547630 :       for (_j = 0; _j < _phi.size(); _j++)
     276   231188920 :         for (unsigned int ph = 0; ph < _num_phases; ++ph)
     277   122058900 :           _local_ke(_i, _j) += _jacobian[ph][_i][_j];
     278             : 
     279     6434243 :     accumulateTaggedLocalMatrix();
     280             : 
     281     6434243 :     if (_has_diag_save_in && jvar == _var.number())
     282             :     {
     283             :       unsigned int rows = _local_ke.m();
     284           0 :       DenseVector<Number> diag(rows);
     285           0 :       for (unsigned int i = 0; i < rows; i++)
     286           0 :         diag(i) = _local_ke(i, i);
     287             : 
     288           0 :       for (unsigned int i = 0; i < _diag_save_in.size(); i++)
     289           0 :         _diag_save_in[i]->sys().solution().add_vector(diag, _diag_save_in[i]->dofIndices());
     290             :     }
     291             :   }
     292    11764864 : }
     293             : 
     294             : void
     295    14355105 : PorousFlowDarcyBase::fullyUpwind(JacRes res_or_jac, unsigned int ph, unsigned int pvar)
     296             : {
     297             :   /**
     298             :    * Perform the full upwinding by multiplying the residuals at the upstream nodes by their
     299             :    * mobilities.
     300             :    * Mobility is different for each phase, and in each situation:
     301             :    *   mobility = density / viscosity    for single-component Darcy flow
     302             :    *   mobility = mass_fraction * density * relative_perm / viscosity    for multi-component,
     303             :    *multiphase flow
     304             :    *   mobility = enthalpy * density * relative_perm / viscosity    for heat convection
     305             :    *
     306             :    * The residual for the kernel is the sum over Darcy fluxes for each phase.
     307             :    * The Darcy flux for a particular phase is
     308             :    * R_i = int{mobility*flux_no_mob} = int{mobility*grad(pot)*permeability*grad(test_i)}
     309             :    * for node i.  where int is the integral over the element.
     310             :    * However, in fully-upwind, the first step is to take the mobility outside the integral,
     311             :    * which was done in the _proto_flux calculation above.
     312             :    *
     313             :    * NOTE: Physically _proto_flux[i][ph] is a measure of fluid of phase ph flowing out of node i.
     314             :    * If we had left in mobility, it would be exactly the component mass flux flowing out of node
     315             :    *i.
     316             :    *
     317             :    * This leads to the definition of upwinding:
     318             :    *
     319             :    * If _proto_flux(i)[ph] is positive then we use mobility_i.  That is we use the upwind value of
     320             :    * mobility.
     321             :    *
     322             :    * The final subtle thing is we must also conserve fluid mass: the total component mass flowing
     323             :    * out of node i must be the sum of the masses flowing into the other nodes.
     324             :    **/
     325             : 
     326             :   // The number of nodes in the element
     327    14355105 :   const unsigned int num_nodes = _test.size();
     328             : 
     329             :   Real mob;
     330             :   Real dmob;
     331             :   // Define variables used to ensure mass conservation
     332             :   Real total_mass_out = 0.0;
     333             :   Real total_in = 0.0;
     334             : 
     335             :   // The following holds derivatives of these
     336             :   std::vector<Real> dtotal_mass_out;
     337             :   std::vector<Real> dtotal_in;
     338    14355105 :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     339             :   {
     340     8028643 :     dtotal_mass_out.assign(num_nodes, 0.0);
     341     8028643 :     dtotal_in.assign(num_nodes, 0.0);
     342             :   }
     343             : 
     344             :   // Perform the upwinding using the mobility
     345    14355105 :   std::vector<bool> upwind_node(num_nodes);
     346    64073989 :   for (unsigned int n = 0; n < num_nodes; ++n)
     347             :   {
     348    49718884 :     if (_proto_flux[ph][n] >= 0.0) // upstream node
     349             :     {
     350             :       upwind_node[n] = true;
     351             :       // The mobility at the upstream node
     352    25781776 :       mob = mobility(n, ph);
     353    25781776 :       if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     354             :       {
     355             :         // The derivative of the mobility wrt the PorousFlow variable
     356    14410196 :         dmob = dmobility(n, ph, pvar);
     357             : 
     358    78133518 :         for (_j = 0; _j < _phi.size(); _j++)
     359    63723322 :           _jacobian[ph][n][_j] *= mob;
     360             : 
     361    14410196 :         if (_test.size() == _phi.size())
     362             :           /* mobility at node=n depends only on the variables at node=n, by construction.  For
     363             :            * linear-lagrange variables, this means that Jacobian entries involving the derivative
     364             :            * of mobility will only be nonzero for derivatives wrt variables at node=n.  Hence the
     365             :            * [n][n] in the line below.  However, for other variable types (eg constant monomials)
     366             :            * I cannot tell what variable number contributes to the derivative.  However, in all
     367             :            * cases I can possibly imagine, the derivative is zero anyway, since in the full
     368             :            * upwinding scheme, mobility shouldn't depend on these other sorts of variables.
     369             :            */
     370    12574731 :           _jacobian[ph][n][n] += dmob * _proto_flux[ph][n];
     371             : 
     372    78133518 :         for (_j = 0; _j < _phi.size(); _j++)
     373    63723322 :           dtotal_mass_out[_j] += _jacobian[ph][n][_j];
     374             :       }
     375    25781776 :       _proto_flux[ph][n] *= mob;
     376    25781776 :       total_mass_out += _proto_flux[ph][n];
     377             :     }
     378             :     else
     379             :     {
     380             :       upwind_node[n] = false;
     381    23937108 :       total_in -= _proto_flux[ph][n]; /// note the -= means the result is positive
     382    23937108 :       if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     383    71619512 :         for (_j = 0; _j < _phi.size(); _j++)
     384    58332506 :           dtotal_in[_j] -= _jacobian[ph][n][_j];
     385             :     }
     386             :   }
     387             : 
     388             :   // Conserve mass over all phases by proportioning the total_mass_out mass to the inflow nodes,
     389             :   // weighted by their proto_flux values
     390    64073989 :   for (unsigned int n = 0; n < num_nodes; ++n)
     391             :   {
     392    49718884 :     if (!upwind_node[n]) // downstream node
     393             :     {
     394    23937108 :       if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     395    71619512 :         for (_j = 0; _j < _phi.size(); _j++)
     396             :         {
     397    58332506 :           _jacobian[ph][n][_j] *= total_mass_out / total_in;
     398    58332506 :           _jacobian[ph][n][_j] +=
     399    58332506 :               _proto_flux[ph][n] * (dtotal_mass_out[_j] / total_in -
     400    58332506 :                                     dtotal_in[_j] * total_mass_out / total_in / total_in);
     401             :         }
     402    23937108 :       _proto_flux[ph][n] *= total_mass_out / total_in;
     403             :     }
     404             :   }
     405    14355105 : }
     406             : 
     407             : void
     408         596 : PorousFlowDarcyBase::quickUpwind(JacRes res_or_jac, unsigned int ph, unsigned int pvar)
     409             : {
     410             :   // The number of nodes in the element
     411         596 :   const unsigned int num_nodes = _test.size();
     412             : 
     413             :   Real mob;
     414             :   Real dmob;
     415             : 
     416             :   // Use the raw nodal mobility
     417        2788 :   for (unsigned int n = 0; n < num_nodes; ++n)
     418             :   {
     419             :     // The mobility at the node
     420        2192 :     mob = mobility(n, ph);
     421        2192 :     if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     422             :     {
     423             :       // The derivative of the mobility wrt the PorousFlow variable
     424         336 :       dmob = dmobility(n, ph, pvar);
     425             : 
     426        1488 :       for (_j = 0; _j < _phi.size(); _j++)
     427        1152 :         _jacobian[ph][n][_j] *= mob;
     428             : 
     429         336 :       if (_test.size() == _phi.size())
     430             :         /* mobility at node=n depends only on the variables at node=n, by construction.  For
     431             :          * linear-lagrange variables, this means that Jacobian entries involving the derivative
     432             :          * of mobility will only be nonzero for derivatives wrt variables at node=n.  Hence the
     433             :          * [n][n] in the line below.  However, for other variable types (eg constant monomials)
     434             :          * I cannot tell what variable number contributes to the derivative.  However, in all
     435             :          * cases I can possibly imagine, the derivative is zero anyway, since in the full
     436             :          * upwinding scheme, mobility shouldn't depend on these other sorts of variables.
     437             :          */
     438         336 :         _jacobian[ph][n][n] += dmob * _proto_flux[ph][n];
     439             :     }
     440        2192 :     _proto_flux[ph][n] *= mob;
     441             :   }
     442         596 : }
     443             : 
     444             : void
     445        1000 : PorousFlowDarcyBase::harmonicMean(JacRes res_or_jac, unsigned int ph, unsigned int pvar)
     446             : {
     447             :   // The number of nodes in the element
     448        1000 :   const unsigned int num_nodes = _test.size();
     449             : 
     450        1000 :   std::vector<Real> mob(num_nodes);
     451             :   unsigned num_zero = 0;
     452             :   unsigned zero_mobility_node = std::numeric_limits<unsigned>::max();
     453             :   Real harmonic_mob = 0;
     454        5000 :   for (unsigned n = 0; n < num_nodes; ++n)
     455             :   {
     456        4000 :     mob[n] = mobility(n, ph);
     457        4000 :     if (mob[n] == 0.0)
     458             :     {
     459             :       zero_mobility_node = n;
     460           0 :       num_zero++;
     461             :     }
     462             :     else
     463        4000 :       harmonic_mob += 1.0 / mob[n];
     464             :   }
     465        1000 :   if (num_zero > 0)
     466             :     harmonic_mob = 0.0;
     467             :   else
     468        1000 :     harmonic_mob = (1.0 * num_nodes) / harmonic_mob;
     469             : 
     470             :   // d(harmonic_mob)/d(PorousFlow variable at node n)
     471        1000 :   std::vector<Real> dharmonic_mob(num_nodes, 0.0);
     472        1000 :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     473             :   {
     474         120 :     const Real harm2 = std::pow(harmonic_mob, 2) / (1.0 * num_nodes);
     475         120 :     if (num_zero == 0)
     476         600 :       for (unsigned n = 0; n < num_nodes; ++n)
     477         480 :         dharmonic_mob[n] = dmobility(n, ph, pvar) * harm2 / std::pow(mob[n], 2);
     478           0 :     else if (num_zero == 1)
     479           0 :       dharmonic_mob[zero_mobility_node] =
     480           0 :           num_nodes * dmobility(zero_mobility_node, ph, pvar); // other derivs are zero
     481             :     // if num_zero > 1 then all dharmonic_mob = 0.0
     482             :   }
     483             : 
     484             :   if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
     485         600 :     for (unsigned n = 0; n < num_nodes; ++n)
     486        2400 :       for (_j = 0; _j < _phi.size(); _j++)
     487             :       {
     488        1920 :         _jacobian[ph][n][_j] *= harmonic_mob;
     489        1920 :         if (_test.size() == _phi.size())
     490        1920 :           _jacobian[ph][n][_j] += dharmonic_mob[_j] * _proto_flux[ph][n];
     491             :       }
     492             : 
     493        1000 :   if (res_or_jac == JacRes::CALCULATE_RESIDUAL)
     494        4400 :     for (unsigned n = 0; n < num_nodes; ++n)
     495        3520 :       _proto_flux[ph][n] *= harmonic_mob;
     496        1000 : }
     497             : 
     498             : Real
     499           0 : PorousFlowDarcyBase::mobility(unsigned nodenum, unsigned phase) const
     500             : {
     501           0 :   return _fluid_density_node[nodenum][phase] / _fluid_viscosity[nodenum][phase];
     502             : }
     503             : 
     504             : Real
     505           0 : PorousFlowDarcyBase::dmobility(unsigned nodenum, unsigned phase, unsigned pvar) const
     506             : {
     507           0 :   Real dm = _dfluid_density_node_dvar[nodenum][phase][pvar] / _fluid_viscosity[nodenum][phase];
     508           0 :   dm -= _fluid_density_node[nodenum][phase] * _dfluid_viscosity_dvar[nodenum][phase][pvar] /
     509             :         std::pow(_fluid_viscosity[nodenum][phase], 2);
     510           0 :   return dm;
     511             : }

Generated by: LCOV version 1.14