LCOV - code coverage report
Current view: top level - src/dirackernels - RichardsBorehole.C (source / functions) Hit Total Coverage
Test: idaholab/moose richards: #31405 (292dce) with base fef103 Lines: 138 139 99.3 %
Date: 2025-09-04 07:56:35 Functions: 9 9 100.0 %
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 "RichardsBorehole.h"
      11             : #include "RotationMatrix.h"
      12             : 
      13             : registerMooseObject("RichardsApp", RichardsBorehole);
      14             : 
      15             : InputParameters
      16          84 : RichardsBorehole::validParams()
      17             : {
      18          84 :   InputParameters params = PeacemanBorehole::validParams();
      19         168 :   params.addRequiredParam<UserObjectName>(
      20             :       "richardsVarNames_UO", "The UserObject that holds the list of Richards variable names.");
      21         168 :   params.addParam<std::vector<UserObjectName>>("relperm_UO",
      22             :                                                "List of names of user objects that "
      23             :                                                "define relative permeability.  Only "
      24             :                                                "needed if fully_upwind is used");
      25         168 :   params.addParam<std::vector<UserObjectName>>(
      26             :       "seff_UO",
      27             :       "List of name of user objects that define effective saturation as a function of "
      28             :       "pressure list.  Only needed if fully_upwind is used");
      29         168 :   params.addParam<std::vector<UserObjectName>>("density_UO",
      30             :                                                "List of names of user objects that "
      31             :                                                "define the fluid density.  Only "
      32             :                                                "needed if fully_upwind is used");
      33         168 :   params.addParam<bool>("fully_upwind", false, "Fully upwind the flux");
      34          84 :   params.addClassDescription("Approximates a borehole in the mesh with given bottomhole pressure, "
      35             :                              "and radii using a number of point sinks whose positions are read "
      36             :                              "from a file");
      37          84 :   return params;
      38           0 : }
      39             : 
      40          43 : RichardsBorehole::RichardsBorehole(const InputParameters & parameters)
      41             :   : PeacemanBorehole(parameters),
      42          41 :     _fully_upwind(getParam<bool>("fully_upwind")),
      43          41 :     _richards_name_UO(getUserObject<RichardsVarNames>("richardsVarNames_UO")),
      44          41 :     _num_p(_richards_name_UO.num_v()),
      45          41 :     _pvar(_richards_name_UO.richards_var_num(_var.number())),
      46             : 
      47             :     // in the following, getUserObjectByName returns a reference (an alias) to a RichardsBLAH user
      48             :     // object, and the & turns it into a pointer
      49          56 :     _density_UO(_fully_upwind ? &getUserObjectByName<RichardsDensity>(
      50          56 :                                     getParam<std::vector<UserObjectName>>("density_UO")[_pvar])
      51             :                               : NULL),
      52          56 :     _seff_UO(_fully_upwind ? &getUserObjectByName<RichardsSeff>(
      53          56 :                                  getParam<std::vector<UserObjectName>>("seff_UO")[_pvar])
      54             :                            : NULL),
      55          56 :     _relperm_UO(_fully_upwind ? &getUserObjectByName<RichardsRelPerm>(
      56          56 :                                     getParam<std::vector<UserObjectName>>("relperm_UO")[_pvar])
      57             :                               : NULL),
      58             : 
      59          41 :     _num_nodes(0),
      60          41 :     _mobility(0),
      61          41 :     _dmobility_dv(0),
      62          82 :     _pp(getMaterialProperty<std::vector<Real>>("porepressure")),
      63          82 :     _dpp_dv(getMaterialProperty<std::vector<std::vector<Real>>>("dporepressure_dv")),
      64          82 :     _viscosity(getMaterialProperty<std::vector<Real>>("viscosity")),
      65          82 :     _permeability(getMaterialProperty<RealTensorValue>("permeability")),
      66          82 :     _dseff_dv(getMaterialProperty<std::vector<std::vector<Real>>>("ds_eff_dv")),
      67          82 :     _rel_perm(getMaterialProperty<std::vector<Real>>("rel_perm")),
      68          82 :     _drel_perm_dv(getMaterialProperty<std::vector<std::vector<Real>>>("drel_perm_dv")),
      69          82 :     _density(getMaterialProperty<std::vector<Real>>("density")),
      70         125 :     _ddensity_dv(getMaterialProperty<std::vector<std::vector<Real>>>("ddensity_dv"))
      71             : {
      72          41 :   _ps_at_nodes.resize(_num_p);
      73          86 :   for (unsigned int pnum = 0; pnum < _num_p; ++pnum)
      74          45 :     _ps_at_nodes[pnum] = _richards_name_UO.nodal_var(pnum);
      75             : 
      76             :   // To correctly compute the Jacobian terms,
      77             :   // tell MOOSE that this DiracKernel depends on all the Richards Vars
      78          41 :   const std::vector<MooseVariableFEBase *> & coupled_vars = _richards_name_UO.getCoupledMooseVars();
      79          86 :   for (unsigned int i = 0; i < coupled_vars.size(); i++)
      80          90 :     addMooseVariableDependency(coupled_vars[i]);
      81          41 : }
      82             : 
      83             : void
      84        2399 : RichardsBorehole::prepareNodalValues()
      85             : {
      86             :   // NOTE: i'm assuming that all the richards variables are pressure values
      87             : 
      88        2399 :   _num_nodes = (*_ps_at_nodes[_pvar]).size();
      89             : 
      90             :   Real p;
      91             :   Real density;
      92             :   Real ddensity_dp;
      93             :   Real seff;
      94             :   std::vector<Real> dseff_dp;
      95             :   Real relperm;
      96             :   Real drelperm_ds;
      97        2399 :   _mobility.resize(_num_nodes);
      98        2399 :   _dmobility_dv.resize(_num_nodes);
      99        2399 :   dseff_dp.resize(_num_p);
     100       21591 :   for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
     101             :   {
     102             :     // retrieve and calculate basic things at the node
     103       19192 :     p = (*_ps_at_nodes[_pvar])[nodenum];    // pressure of fluid _pvar at node nodenum
     104       19192 :     density = _density_UO->density(p);      // density of fluid _pvar at node nodenum
     105       19192 :     ddensity_dp = _density_UO->ddensity(p); // d(density)/dP
     106       19192 :     seff = _seff_UO->seff(_ps_at_nodes,
     107             :                           nodenum); // effective saturation of fluid _pvar at node nodenum
     108       19192 :     _seff_UO->dseff(
     109             :         _ps_at_nodes, nodenum, dseff_dp); // d(seff)/d(P_ph), for ph = 0, ..., _num_p - 1
     110       19192 :     relperm = _relperm_UO->relperm(seff); // relative permeability of fluid _pvar at node nodenum
     111       19192 :     drelperm_ds = _relperm_UO->drelperm(seff); // d(relperm)/dseff
     112             : 
     113             :     // calculate the mobility and its derivatives wrt (variable_ph = porepressure_ph)
     114       19192 :     _mobility[nodenum] =
     115       19192 :         density * relperm / _viscosity[0][_pvar]; // assume viscosity is constant throughout element
     116       19192 :     _dmobility_dv[nodenum].resize(_num_p);
     117       42752 :     for (unsigned int ph = 0; ph < _num_p; ++ph)
     118       23560 :       _dmobility_dv[nodenum][ph] = density * drelperm_ds * dseff_dp[ph] / _viscosity[0][_pvar];
     119       19192 :     _dmobility_dv[nodenum][_pvar] += ddensity_dp * relperm / _viscosity[0][_pvar];
     120             :   }
     121        2399 : }
     122             : 
     123             : void
     124        4714 : RichardsBorehole::computeResidual()
     125             : {
     126        4714 :   if (_fully_upwind)
     127        1603 :     prepareNodalValues();
     128        4714 :   DiracKernel::computeResidual();
     129        4713 : }
     130             : 
     131             : Real
     132       75249 : RichardsBorehole::computeQpResidual()
     133             : {
     134       75249 :   const Real character = _character.value(_t, _q_point[_qp]);
     135       75249 :   if (character == 0.0)
     136             :     return 0.0;
     137             : 
     138             :   const Real bh_pressure =
     139       75249 :       _p_bot +
     140             :       _unit_weight *
     141       75249 :           (_q_point[_qp] -
     142       75249 :            _bottom_point); // really want to use _q_point instaed of _current_point, i think?!
     143             : 
     144             :   Real pp;
     145             :   Real mob;
     146       75249 :   if (!_fully_upwind)
     147             :   {
     148       49761 :     pp = _pp[_qp][_pvar];
     149       49761 :     mob = _rel_perm[_qp][_pvar] * _density[_qp][_pvar] / _viscosity[_qp][_pvar];
     150             :   }
     151             :   else
     152             :   {
     153       25488 :     pp = (*_ps_at_nodes[_pvar])[_i];
     154       25488 :     mob = _mobility[_i];
     155             :   }
     156             : 
     157             :   // Get the ID we initially assigned to this point
     158       75249 :   const unsigned current_dirac_ptid = currentPointCachedID();
     159             : 
     160             :   // If getting the ID failed, fall back to the old bodge!
     161             :   // if (current_dirac_ptid == libMesh::invalid_uint)
     162             :   //  current_dirac_ptid = (_zs.size() > 2) ? 1 : 0;
     163             : 
     164             :   Real outflow(0.0); // this is the flow rate from porespace out of the system
     165             : 
     166             :   Real wc(0.0);
     167       75249 :   if (current_dirac_ptid > 0)
     168             :   // contribution from half-segment "behind" this point (must have >1 point for
     169             :   // current_dirac_ptid>0)
     170             :   {
     171       37544 :     wc = wellConstant(_permeability[_qp],
     172             :                       _rot_matrix[current_dirac_ptid - 1],
     173       37544 :                       _half_seg_len[current_dirac_ptid - 1],
     174       37544 :                       _current_elem,
     175       37544 :                       _rs[current_dirac_ptid]);
     176       37544 :     if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
     177             :       // injection, so outflow<0 || // production, so outflow>0
     178       37114 :       outflow += _test[_i][_qp] * std::abs(character) * wc * mob * (pp - bh_pressure);
     179             :   }
     180             : 
     181       75249 :   if (current_dirac_ptid + 1 < _zs.size() || _zs.size() == 1)
     182             :   // contribution from half-segment "ahead of" this point, or we only have one point
     183             :   {
     184       37705 :     wc = wellConstant(_permeability[_qp],
     185             :                       _rot_matrix[current_dirac_ptid],
     186             :                       _half_seg_len[current_dirac_ptid],
     187       37705 :                       _current_elem,
     188       37705 :                       _rs[current_dirac_ptid]);
     189       37704 :     if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
     190             :       // injection, so outflow<0 || // production, so outflow>0
     191       37274 :       outflow += _test[_i][_qp] * std::abs(character) * wc * mob * (pp - bh_pressure);
     192             :   }
     193             : 
     194       75248 :   _total_outflow_mass.add(
     195       75248 :       outflow * _dt); // this is not thread safe, but DiracKernel's aren't currently threaded
     196       75248 :   return outflow;
     197             : }
     198             : 
     199             : void
     200        2594 : RichardsBorehole::computeJacobian()
     201             : {
     202        2594 :   if (_fully_upwind)
     203         796 :     prepareNodalValues();
     204        2594 :   DiracKernel::computeJacobian();
     205        2594 : }
     206             : 
     207             : Real
     208      331072 : RichardsBorehole::computeQpJacobian()
     209             : {
     210      331072 :   const Real character = _character.value(_t, _q_point[_qp]);
     211      331072 :   if (character == 0.0)
     212             :     return 0.0;
     213      331072 :   return jac(_pvar);
     214             : }
     215             : 
     216             : Real
     217        6656 : RichardsBorehole::computeQpOffDiagJacobian(unsigned int jvar)
     218             : {
     219        6656 :   if (_richards_name_UO.not_richards_var(jvar))
     220             :     return 0.0;
     221        6656 :   const unsigned int dvar = _richards_name_UO.richards_var_num(jvar);
     222        6656 :   return jac(dvar);
     223             : }
     224             : 
     225             : Real
     226      337728 : RichardsBorehole::jac(unsigned int wrt_num)
     227             : {
     228      337728 :   const Real character = _character.value(_t, _q_point[_qp]);
     229      337728 :   if (character == 0.0)
     230             :     return 0.0;
     231             : 
     232             :   const Real bh_pressure =
     233      337728 :       _p_bot +
     234             :       _unit_weight *
     235      337728 :           (_q_point[_qp] -
     236      337728 :            _bottom_point); // really want to use _q_point instaed of _current_point, i think?!
     237             : 
     238             :   Real pp;
     239             :   Real dpp_dv;
     240             :   Real mob;
     241             :   Real dmob_dv;
     242             :   Real phi;
     243      337728 :   if (!_fully_upwind)
     244             :   {
     245      233472 :     pp = _pp[_qp][_pvar];
     246      233472 :     dpp_dv = _dpp_dv[_qp][_pvar][wrt_num];
     247      233472 :     mob = _rel_perm[_qp][_pvar] * _density[_qp][_pvar] / _viscosity[_qp][_pvar];
     248      233472 :     dmob_dv = (_drel_perm_dv[_qp][_pvar][wrt_num] * _density[_qp][_pvar] +
     249      233472 :                _rel_perm[_qp][_pvar] * _ddensity_dv[_qp][_pvar][wrt_num]) /
     250             :               _viscosity[_qp][_pvar];
     251      233472 :     phi = _phi[_j][_qp];
     252             :   }
     253             :   else
     254             :   {
     255      104256 :     if (_i != _j)
     256             :       return 0.0; // residual at node _i only depends on variables at that node
     257       13032 :     pp = (*_ps_at_nodes[_pvar])[_i];
     258       13032 :     dpp_dv =
     259             :         (_pvar == wrt_num ? 1 : 0); // NOTE: i'm assuming that the variables are pressure variables
     260       13032 :     mob = _mobility[_i];
     261       13032 :     dmob_dv = _dmobility_dv[_i][wrt_num];
     262             :     phi = 1;
     263             :   }
     264             : 
     265             :   // Get the ID we initially assigned to this point
     266      246504 :   const unsigned current_dirac_ptid = currentPointCachedID();
     267             : 
     268             :   // If getting the ID failed, fall back to the old bodge!
     269             :   // if (current_dirac_ptid == libMesh::invalid_uint)
     270             :   //  current_dirac_ptid = (_zs.size() > 2) ? 1 : 0;
     271             : 
     272             :   Real outflowp(0.0);
     273             : 
     274             :   Real wc(0.0);
     275      246504 :   if (current_dirac_ptid > 0)
     276             :   // contribution from half-segment "behind" this point
     277             :   {
     278      123192 :     wc = wellConstant(_permeability[_qp],
     279             :                       _rot_matrix[current_dirac_ptid - 1],
     280      123192 :                       _half_seg_len[current_dirac_ptid - 1],
     281      123192 :                       _current_elem,
     282      123192 :                       _rs[current_dirac_ptid]);
     283      123192 :     if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
     284             :       // injection, so outflow<0 || // production, so outflow>0
     285      123192 :       outflowp += _test[_i][_qp] * std::abs(character) * wc *
     286      123192 :                   (mob * phi * dpp_dv + dmob_dv * phi * (pp - bh_pressure));
     287             :   }
     288             : 
     289      246504 :   if (current_dirac_ptid < _zs.size() - 1 || _zs.size() == 1)
     290             :   // contribution from half-segment "ahead of" this point
     291             :   {
     292      123312 :     wc = wellConstant(_permeability[_qp],
     293             :                       _rot_matrix[current_dirac_ptid],
     294             :                       _half_seg_len[current_dirac_ptid],
     295      123312 :                       _current_elem,
     296             :                       _rs[current_dirac_ptid]);
     297      123312 :     if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
     298             :       // injection, so outflow<0 || // production, so outflow>0
     299      123312 :       outflowp += _test[_i][_qp] * std::abs(character) * wc *
     300      123312 :                   (mob * phi * dpp_dv + dmob_dv * phi * (pp - bh_pressure));
     301             :   }
     302             : 
     303             :   return outflowp;
     304             : }

Generated by: LCOV version 1.14