LCOV - code coverage report
Current view: top level - src/bcs - RichardsPiecewiseLinearSink.C (source / functions) Hit Total Coverage
Test: idaholab/moose richards: #31405 (292dce) with base fef103 Lines: 145 146 99.3 %
Date: 2025-09-04 07:56:35 Functions: 10 10 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 "RichardsPiecewiseLinearSink.h"
      11             : 
      12             : // MOOSE includes
      13             : #include "MooseVariable.h"
      14             : 
      15             : // C++ includes
      16             : #include <iostream>
      17             : 
      18             : registerMooseObject("RichardsApp", RichardsPiecewiseLinearSink);
      19             : 
      20             : InputParameters
      21         110 : RichardsPiecewiseLinearSink::validParams()
      22             : {
      23         110 :   InputParameters params = IntegratedBC::validParams();
      24         220 :   params.addRequiredParam<bool>(
      25             :       "use_mobility",
      26             :       "If true, then fluxes are multiplied by (density*permeability_nn/viscosity), "
      27             :       "where the '_nn' indicates the component normal to the boundary.  In this "
      28             :       "case bare_flux is measured in Pa.s^-1.  This can be used in conjunction "
      29             :       "with use_relperm.");
      30         220 :   params.addRequiredParam<bool>("use_relperm",
      31             :                                 "If true, then fluxes are multiplied by relative "
      32             :                                 "permeability.  This can be used in conjunction "
      33             :                                 "with use_mobility");
      34         220 :   params.addParam<std::vector<UserObjectName>>("relperm_UO",
      35             :                                                "List of names of user objects that "
      36             :                                                "define relative permeability.  Only "
      37             :                                                "needed if fully_upwind is used");
      38         220 :   params.addParam<std::vector<UserObjectName>>(
      39             :       "seff_UO",
      40             :       "List of name of user objects that define effective saturation as a function of "
      41             :       "pressure list.  Only needed if fully_upwind is used");
      42         220 :   params.addParam<std::vector<UserObjectName>>("density_UO",
      43             :                                                "List of names of user objects that "
      44             :                                                "define the fluid density.  Only "
      45             :                                                "needed if fully_upwind is used");
      46         220 :   params.addRequiredParam<std::vector<Real>>(
      47             :       "pressures", "Tuple of pressure values.  Must be monotonically increasing.");
      48         220 :   params.addRequiredParam<std::vector<Real>>(
      49             :       "bare_fluxes",
      50             :       "Tuple of flux values (measured in kg.m^-2.s^-1 for use_mobility=false, and "
      51             :       "in Pa.s^-1 if use_mobility=true).  This flux is OUT of the medium: hence "
      52             :       "positive values of flux means this will be a SINK, while negative values "
      53             :       "indicate this flux will be a SOURCE.  A piecewise-linear fit is performed to "
      54             :       "the (pressure,bare_fluxes) pairs to obtain the flux at any arbitrary "
      55             :       "pressure, and the first or last bare_flux values are used if the quad-point "
      56             :       "pressure falls outside this range.");
      57         220 :   params.addParam<FunctionName>("multiplying_fcn",
      58         220 :                                 1.0,
      59             :                                 "If this function is provided, the flux "
      60             :                                 "will be multiplied by this function.  "
      61             :                                 "This is useful for spatially or "
      62             :                                 "temporally varying sinks");
      63         220 :   params.addRequiredParam<UserObjectName>(
      64             :       "richardsVarNames_UO", "The UserObject that holds the list of Richards variable names.");
      65         220 :   params.addParam<bool>("fully_upwind", false, "Use full upwinding");
      66         220 :   params.addParam<PostprocessorName>(
      67             :       "area_pp",
      68         220 :       1,
      69             :       "An area postprocessor.  If given, the bare_fluxes will be divided by this "
      70             :       "quantity.  This means the bare fluxes are measured in kg.s^-1.  This is "
      71             :       "useful for the case when you wish to provide the *total* flux, and let MOOSE "
      72             :       "proportion it uniformly across the boundary.  In that case you would have "
      73             :       "use_mobility=false=use_relperm, and only one bare flux should be specified");
      74         110 :   return params;
      75           0 : }
      76             : 
      77          52 : RichardsPiecewiseLinearSink::RichardsPiecewiseLinearSink(const InputParameters & parameters)
      78             :   : IntegratedBC(parameters),
      79          52 :     _use_mobility(getParam<bool>("use_mobility")),
      80         104 :     _use_relperm(getParam<bool>("use_relperm")),
      81         104 :     _fully_upwind(getParam<bool>("fully_upwind")),
      82             : 
      83         208 :     _sink_func(getParam<std::vector<Real>>("pressures"),
      84             :                getParam<std::vector<Real>>("bare_fluxes")),
      85             : 
      86          52 :     _m_func(getFunction("multiplying_fcn")),
      87             : 
      88          52 :     _richards_name_UO(getUserObject<RichardsVarNames>("richardsVarNames_UO")),
      89          52 :     _num_p(_richards_name_UO.num_v()),
      90          52 :     _pvar(_richards_name_UO.richards_var_num(_var.number())),
      91             : 
      92             :     // in the following, getUserObjectByName returns a reference (an alias) to a RichardsBLAH user
      93             :     // object, and the & turns it into a pointer
      94          75 :     _density_UO(_fully_upwind ? &getUserObjectByName<RichardsDensity>(
      95          75 :                                     getParam<std::vector<UserObjectName>>("density_UO")[_pvar])
      96             :                               : NULL),
      97          75 :     _seff_UO(_fully_upwind ? &getUserObjectByName<RichardsSeff>(
      98          75 :                                  getParam<std::vector<UserObjectName>>("seff_UO")[_pvar])
      99             :                            : NULL),
     100          75 :     _relperm_UO(_fully_upwind ? &getUserObjectByName<RichardsRelPerm>(
     101          75 :                                     getParam<std::vector<UserObjectName>>("relperm_UO")[_pvar])
     102             :                               : NULL),
     103             : 
     104          52 :     _area_pp(getPostprocessorValue("area_pp")),
     105             : 
     106          52 :     _num_nodes(0),
     107          52 :     _nodal_density(0),
     108          52 :     _dnodal_density_dv(0),
     109          52 :     _nodal_relperm(0),
     110          52 :     _dnodal_relperm_dv(0),
     111             : 
     112         104 :     _pp(getMaterialProperty<std::vector<Real>>("porepressure")),
     113         104 :     _dpp_dv(getMaterialProperty<std::vector<std::vector<Real>>>("dporepressure_dv")),
     114             : 
     115         104 :     _viscosity(getMaterialProperty<std::vector<Real>>("viscosity")),
     116         104 :     _permeability(getMaterialProperty<RealTensorValue>("permeability")),
     117             : 
     118         104 :     _dseff_dv(getMaterialProperty<std::vector<std::vector<Real>>>("ds_eff_dv")),
     119             : 
     120         104 :     _rel_perm(getMaterialProperty<std::vector<Real>>("rel_perm")),
     121         104 :     _drel_perm_dv(getMaterialProperty<std::vector<std::vector<Real>>>("drel_perm_dv")),
     122             : 
     123         104 :     _density(getMaterialProperty<std::vector<Real>>("density")),
     124         156 :     _ddensity_dv(getMaterialProperty<std::vector<std::vector<Real>>>("ddensity_dv"))
     125             : {
     126          52 :   _ps_at_nodes.resize(_num_p);
     127         110 :   for (unsigned int pnum = 0; pnum < _num_p; ++pnum)
     128          58 :     _ps_at_nodes[pnum] = _richards_name_UO.nodal_var(pnum);
     129          52 : }
     130             : 
     131             : void
     132       16110 : RichardsPiecewiseLinearSink::prepareNodalValues()
     133             : {
     134             :   // NOTE: i'm assuming that all the richards variables are pressure values
     135             : 
     136       16110 :   _num_nodes = (*_ps_at_nodes[_pvar]).size();
     137             : 
     138             :   Real p;
     139             :   Real seff;
     140             :   std::vector<Real> dseff_dp;
     141             :   Real drelperm_ds;
     142             : 
     143       16110 :   _nodal_density.resize(_num_nodes);
     144       16110 :   _dnodal_density_dv.resize(_num_nodes);
     145       16110 :   _nodal_relperm.resize(_num_nodes);
     146       16110 :   _dnodal_relperm_dv.resize(_num_nodes);
     147       16110 :   dseff_dp.resize(_num_p);
     148       80958 :   for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
     149             :   {
     150             :     // retrieve and calculate basic things at the node
     151       64848 :     p = (*_ps_at_nodes[_pvar])[nodenum]; // pressure of fluid _pvar at node nodenum
     152             : 
     153       64848 :     _nodal_density[nodenum] = _density_UO->density(p); // density of fluid _pvar at node nodenum
     154       64848 :     _dnodal_density_dv[nodenum].resize(_num_p);
     155      130064 :     for (unsigned int ph = 0; ph < _num_p; ++ph)
     156       65216 :       _dnodal_density_dv[nodenum][ph] = 0;
     157       64848 :     _dnodal_density_dv[nodenum][_pvar] = _density_UO->ddensity(p); // d(density)/dP
     158             : 
     159       64848 :     seff = _seff_UO->seff(_ps_at_nodes,
     160             :                           nodenum); // effective saturation of fluid _pvar at node nodenum
     161       64848 :     _seff_UO->dseff(
     162             :         _ps_at_nodes, nodenum, dseff_dp); // d(seff)/d(P_ph), for ph = 0, ..., _num_p - 1
     163             : 
     164       64848 :     _nodal_relperm[nodenum] =
     165       64848 :         _relperm_UO->relperm(seff); // relative permeability of fluid _pvar at node nodenum
     166       64848 :     drelperm_ds = _relperm_UO->drelperm(seff); // d(relperm)/dseff
     167             : 
     168       64848 :     _dnodal_relperm_dv[nodenum].resize(_num_p);
     169      130064 :     for (unsigned int ph = 0; ph < _num_p; ++ph)
     170       65216 :       _dnodal_relperm_dv[nodenum][ph] = drelperm_ds * dseff_dp[ph];
     171             :   }
     172       16110 : }
     173             : 
     174             : void
     175       15350 : RichardsPiecewiseLinearSink::computeResidual()
     176             : {
     177       15350 :   if (_fully_upwind)
     178        8430 :     prepareNodalValues();
     179       15350 :   IntegratedBC::computeResidual();
     180       15349 : }
     181             : 
     182             : Real
     183      127017 : RichardsPiecewiseLinearSink::computeQpResidual()
     184             : {
     185             :   Real flux = 0;
     186             :   Real k = 0;
     187             : 
     188      127017 :   if (!_fully_upwind)
     189             :   {
     190       57472 :     flux = _test[_i][_qp] * _sink_func.sample(_pp[_qp][_pvar]);
     191       57472 :     if (_use_mobility)
     192             :     {
     193       15488 :       k = (_permeability[_qp] * _normals[_qp]) * _normals[_qp];
     194       15488 :       flux *= _density[_qp][_pvar] * k / _viscosity[_qp][_pvar];
     195             :     }
     196       57472 :     if (_use_relperm)
     197       15488 :       flux *= _rel_perm[_qp][_pvar];
     198             :   }
     199             :   else
     200             :   {
     201       69545 :     flux = _test[_i][_qp] * _sink_func.sample((*_ps_at_nodes[_pvar])[_i]);
     202       69545 :     if (_use_mobility)
     203             :     {
     204       15488 :       k = (_permeability[0] * _normals[_qp]) * _normals[_qp]; // assume that _permeability is
     205             :                                                               // constant throughout element so
     206             :                                                               // doesn't need to be upwinded
     207       15488 :       flux *= _nodal_density[_i] * k /
     208       15488 :               _viscosity[0][_pvar]; // assume that viscosity is constant throughout element
     209             :     }
     210       69545 :     if (_use_relperm)
     211       15488 :       flux *= _nodal_relperm[_i];
     212             :   }
     213             : 
     214      127017 :   flux *= _m_func.value(_t, _q_point[_qp]);
     215             : 
     216      127017 :   if (_area_pp == 0.0)
     217             :   {
     218          25 :     if (flux != 0)
     219           1 :       mooseError("RichardsPiecewiseLinearSink: flux is nonzero, but area is zero!\n");
     220             :     // if flux == 0, then leave it as zero.
     221             :   }
     222             :   else
     223      126992 :     flux /= _area_pp;
     224             : 
     225      127016 :   return flux;
     226             : }
     227             : 
     228             : void
     229        9620 : RichardsPiecewiseLinearSink::computeJacobian()
     230             : {
     231        9620 :   if (_fully_upwind)
     232        4466 :     prepareNodalValues();
     233        9620 :   IntegratedBC::computeJacobian();
     234        9619 : }
     235             : 
     236             : Real
     237      310497 : RichardsPiecewiseLinearSink::computeQpJacobian()
     238             : {
     239      310497 :   return jac(_pvar);
     240             : }
     241             : 
     242             : void
     243        8624 : RichardsPiecewiseLinearSink::computeOffDiagJacobian(const unsigned int jvar)
     244             : {
     245        8624 :   if (_fully_upwind)
     246        3214 :     prepareNodalValues();
     247        8624 :   IntegratedBC::computeOffDiagJacobian(jvar);
     248        8623 : }
     249             : 
     250             : Real
     251        9152 : RichardsPiecewiseLinearSink::computeQpOffDiagJacobian(unsigned int jvar)
     252             : {
     253        9152 :   if (_richards_name_UO.not_richards_var(jvar))
     254             :     return 0.0;
     255        9152 :   unsigned int dvar = _richards_name_UO.richards_var_num(jvar);
     256        9152 :   return jac(dvar);
     257             : }
     258             : 
     259             : Real
     260      319649 : RichardsPiecewiseLinearSink::jac(unsigned int wrt_num)
     261             : {
     262             :   Real flux = 0;
     263             :   Real deriv = 0;
     264             :   Real k = 0;
     265             :   Real mob = 0;
     266             :   Real mobp = 0;
     267             :   Real phi = 0;
     268             : 
     269      319649 :   if (!_fully_upwind)
     270             :   {
     271      174912 :     flux = _sink_func.sample(_pp[_qp][_pvar]);
     272      174912 :     deriv = _sink_func.sampleDerivative(_pp[_qp][_pvar]) * _dpp_dv[_qp][_pvar][wrt_num];
     273      174912 :     phi = _phi[_j][_qp];
     274      174912 :     if (_use_mobility)
     275             :     {
     276       42432 :       k = (_permeability[_qp] * _normals[_qp]) * _normals[_qp];
     277       42432 :       mob = _density[_qp][_pvar] * k / _viscosity[_qp][_pvar];
     278       42432 :       mobp = _ddensity_dv[_qp][_pvar][wrt_num] * k / _viscosity[_qp][_pvar];
     279       42432 :       deriv = mob * deriv + mobp * flux;
     280       42432 :       flux *= mob;
     281             :     }
     282      174912 :     if (_use_relperm)
     283       42432 :       deriv = _rel_perm[_qp][_pvar] * deriv + _drel_perm_dv[_qp][_pvar][wrt_num] * flux;
     284             :   }
     285             :   else
     286             :   {
     287      144737 :     if (_i != _j)
     288             :       return 0.0; // residual at node _i only depends on variables at that node
     289       35929 :     flux = _sink_func.sample((*_ps_at_nodes[_pvar])[_i]);
     290       35929 :     deriv = (_pvar == wrt_num ? _sink_func.sampleDerivative((*_ps_at_nodes[_pvar])[_i])
     291             :                               : 0); // NOTE: i'm assuming that the variables are pressure variables
     292             :     phi = 1;
     293       35929 :     if (_use_mobility)
     294             :     {
     295       10416 :       k = (_permeability[0] * _normals[_qp]) * _normals[_qp];
     296       10416 :       mob = _nodal_density[_i] * k / _viscosity[0][_pvar];
     297       10416 :       mobp = _dnodal_density_dv[_i][wrt_num] * k / _viscosity[0][_pvar];
     298       10416 :       deriv = mob * deriv + mobp * flux;
     299       10416 :       flux *= mob;
     300             :     }
     301       35929 :     if (_use_relperm)
     302       10416 :       deriv = _nodal_relperm[_i] * deriv + _dnodal_relperm_dv[_i][wrt_num] * flux;
     303             :   }
     304             : 
     305      210841 :   deriv *= _m_func.value(_t, _q_point[_qp]);
     306             : 
     307      210841 :   if (_area_pp == 0.0)
     308             :   {
     309           1 :     if (deriv != 0)
     310           1 :       mooseError("RichardsPiecewiseLinearSink: deriv is nonzero, but area is zero!\n");
     311             :     // if deriv == 0, then leave it as zero.
     312             :   }
     313             :   else
     314      210840 :     deriv /= _area_pp;
     315             : 
     316      210840 :   return _test[_i][_qp] * deriv * phi;
     317             : }

Generated by: LCOV version 1.14