LCOV - code coverage report
Current view: top level - src/userobjects - AdvectiveFluxCalculatorBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose porous_flow: #31706 (f8ed4a) with base bb0a08 Lines: 479 483 99.2 %
Date: 2025-11-03 17:27:11 Functions: 25 25 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 "AdvectiveFluxCalculatorBase.h"
      11             : #include "Conversion.h" // for stringify
      12             : 
      13             : #include "libmesh/string_to_enum.h"
      14             : #include "libmesh/mesh_tools.h"
      15             : #include "libmesh/parallel_sync.h"
      16             : 
      17             : InputParameters
      18        3701 : AdvectiveFluxCalculatorBase::validParams()
      19             : {
      20        3701 :   InputParameters params = ElementUserObject::validParams();
      21        3701 :   params.addClassDescription(
      22             :       "Base class to compute K_ij (a measure of advective flux from node i to node j) "
      23             :       "and R+ and R- (which quantify amount of antidiffusion to add) in the "
      24             :       "Kuzmin-Turek FEM-TVD multidimensional scheme");
      25        7402 :   MooseEnum flux_limiter_type("MinMod VanLeer MC superbee None", "VanLeer");
      26        7402 :   params.addParam<MooseEnum>("flux_limiter_type",
      27             :                              flux_limiter_type,
      28             :                              "Type of flux limiter to use.  'None' means that no antidiffusion "
      29             :                              "will be added in the Kuzmin-Turek scheme");
      30       11103 :   params.addRangeCheckedParam<Real>(
      31             :       "allowable_MB_wastage",
      32        7402 :       5.0,
      33             :       "allowable_MB_wastage > 0.0",
      34             :       "This object will issue a memory warning if the internal node-numbering data structure "
      35             :       "wastes more than allowable_MB_wastage megabytes.  This data structure uses sequential "
      36             :       "node-numbering which is optimized for speed rather than memory efficiency");
      37             : 
      38        7402 :   params.addRelationshipManager("ElementPointNeighborLayers",
      39             :                                 Moose::RelationshipManagerType::GEOMETRIC |
      40             :                                     Moose::RelationshipManagerType::ALGEBRAIC |
      41             :                                     Moose::RelationshipManagerType::COUPLING,
      42        3675 :                                 [](const InputParameters &, InputParameters & rm_params)
      43        3675 :                                 { rm_params.set<unsigned short>("layers") = 2; });
      44             : 
      45       11103 :   params.set<ExecFlagEnum>("execute_on", true) = {EXEC_LINEAR};
      46        3701 :   return params;
      47        7402 : }
      48             : 
      49        1664 : AdvectiveFluxCalculatorBase::AdvectiveFluxCalculatorBase(const InputParameters & parameters)
      50             :   : ElementUserObject(parameters),
      51        1664 :     _resizing_needed(true),
      52        1664 :     _flux_limiter_type(getParam<MooseEnum>("flux_limiter_type").getEnum<FluxLimiterTypeEnum>()),
      53             :     _kij(),
      54             :     _flux_out(),
      55             :     _dflux_out_du(),
      56             :     _dflux_out_dKjk(),
      57             :     _valence(),
      58             :     _u_nodal(),
      59             :     _u_nodal_computed_by_thread(),
      60        1664 :     _connections(),
      61        1664 :     _number_of_nodes(0),
      62        1664 :     _my_pid(processor_id()),
      63             :     _nodes_to_receive(),
      64             :     _nodes_to_send(),
      65             :     _pairs_to_receive(),
      66             :     _pairs_to_send(),
      67        4992 :     _allowable_MB_wastage(getParam<Real>("allowable_MB_wastage"))
      68             : {
      69        1664 :   if (!_execute_enum.isValueSet(EXEC_LINEAR))
      70           4 :     paramError(
      71             :         "execute_on",
      72           2 :         "The AdvectiveFluxCalculator UserObject " + name() +
      73             :             " execute_on parameter must include, at least, 'linear'.  This is to ensure that "
      74             :             "this UserObject computes all necessary quantities just before the Kernels evaluate "
      75             :             "their Residuals");
      76        1662 : }
      77             : 
      78             : void
      79       58342 : AdvectiveFluxCalculatorBase::timestepSetup()
      80             : {
      81             :   // If needed, size and initialize quantities appropriately, and compute _valence
      82       58342 :   if (_resizing_needed)
      83             :   {
      84             :     /*
      85             :      * Populate _connections for all nodes that can be seen by this processor and on relevant
      86             :      * blocks
      87             :      *
      88             :      * MULTIPROC NOTE: this must loop over local elements and 2 layers of ghosted elements.
      89             :      * The Kernel will only loop over local elements, so will only use _kij, etc, for
      90             :      * linked node-node pairs that appear in the local elements.  Nevertheless, we
      91             :      * need to build _kij, etc, for the nodes in the ghosted elements in order to simplify
      92             :      * Jacobian computations
      93             :      */
      94        2802 :     _connections.clear();
      95       52679 :     for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
      96       49877 :       if (this->hasBlocks(elem->subdomain_id()))
      97      172745 :         for (unsigned i = 0; i < elem->n_nodes(); ++i)
      98      126564 :           _connections.addGlobalNode(elem->node_id(i));
      99        2802 :     _connections.finalizeAddingGlobalNodes();
     100       52679 :     for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
     101       49877 :       if (this->hasBlocks(elem->subdomain_id()))
     102      172745 :         for (unsigned i = 0; i < elem->n_nodes(); ++i)
     103      532604 :           for (unsigned j = 0; j < elem->n_nodes(); ++j)
     104      406040 :             _connections.addConnection(elem->node_id(i), elem->node_id(j));
     105        2802 :     _connections.finalizeAddingConnections();
     106             : 
     107        2802 :     _number_of_nodes = _connections.numNodes();
     108             : 
     109        2802 :     Real mb_wasted = (_connections.sizeSequential() - _number_of_nodes) * 4.0 / 1048576;
     110             :     gatherMax(mb_wasted);
     111        2802 :     if (mb_wasted > _allowable_MB_wastage)
     112           0 :       mooseWarning(
     113             :           "In at least one processor, the sequential node-numbering internal data structure used "
     114           0 :           "in " +
     115           0 :           name() + " is using memory inefficiently.\nThe memory wasted is " +
     116           0 :           Moose::stringify(mb_wasted) +
     117             :           " megabytes.\n  The node-numbering data structure has been optimized for speed at the "
     118             :           "expense of memory, but that may not be an appropriate optimization for your case, "
     119             :           "because the node numbering is not close to sequential in your case.\n");
     120             : 
     121             :     // initialize _kij
     122        2802 :     _kij.resize(_number_of_nodes);
     123       59095 :     for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     124             :       _kij[sequential_i].assign(
     125      112586 :           _connections.sequentialConnectionsToSequentialID(sequential_i).size(), 0.0);
     126             : 
     127             :     /*
     128             :      * Build _valence[i], which is the number of times the sequential node i is encountered when
     129             :      * looping over the entire mesh (and on relevant blocks)
     130             :      *
     131             :      * MULTIPROC NOTE: this must loop over local elements and >=1 layer of ghosted elements.
     132             :      * The Kernel will only loop over local elements, so will only use _valence for
     133             :      * linked node-node pairs that appear in the local elements.  But other processors will
     134             :      * loop over neighboring elements, so avoid multiple counting of the residual and Jacobian
     135             :      * this processor must record how many times each node-node link of its local elements
     136             :      * appears in the local elements and >=1 layer, and pass that info to the Kernel
     137             :      */
     138        2802 :     _valence.assign(_number_of_nodes, 0);
     139       52679 :     for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
     140       49877 :       if (this->hasBlocks(elem->subdomain_id()))
     141      172745 :         for (unsigned i = 0; i < elem->n_nodes(); ++i)
     142             :         {
     143      126564 :           const dof_id_type node_i = elem->node_id(i);
     144      126564 :           const dof_id_type sequential_i = _connections.sequentialID(node_i);
     145      126564 :           _valence[sequential_i] += 1;
     146             :         }
     147             : 
     148        2802 :     _u_nodal.assign(_number_of_nodes, 0.0);
     149        2802 :     _u_nodal_computed_by_thread.assign(_number_of_nodes, false);
     150        2802 :     _flux_out.assign(_number_of_nodes, 0.0);
     151        2802 :     _dflux_out_du.assign(_number_of_nodes, std::map<dof_id_type, Real>());
     152       59095 :     for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     153       56293 :       zeroedConnection(_dflux_out_du[sequential_i], _connections.globalID(sequential_i));
     154        2802 :     _dflux_out_dKjk.resize(_number_of_nodes);
     155       59095 :     for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     156             :     {
     157             :       const std::vector<dof_id_type> con_i =
     158       56293 :           _connections.sequentialConnectionsToSequentialID(sequential_i);
     159             :       const std::size_t num_con_i = con_i.size();
     160       56293 :       _dflux_out_dKjk[sequential_i].resize(num_con_i);
     161      333018 :       for (std::size_t j = 0; j < con_i.size(); ++j)
     162             :       {
     163      276725 :         const dof_id_type sequential_j = con_i[j];
     164             :         const std::size_t num_con_j =
     165      276725 :             _connections.sequentialConnectionsToSequentialID(sequential_j).size();
     166      276725 :         _dflux_out_dKjk[sequential_i][j].assign(num_con_j, 0.0);
     167             :       }
     168       56293 :     }
     169             : 
     170        2802 :     if (_app.n_processors() > 1)
     171        2028 :       buildCommLists();
     172             : 
     173        2802 :     _resizing_needed = false;
     174             : 
     175             :     // Clear and size all member vectors
     176        2802 :     _dij.assign(_number_of_nodes, {});
     177        2802 :     _dDij_dKij.assign(_number_of_nodes, {});
     178        2802 :     _dDij_dKji.assign(_number_of_nodes, {});
     179        2802 :     _dDii_dKij.assign(_number_of_nodes, {});
     180        2802 :     _dDii_dKji.assign(_number_of_nodes, {});
     181        2802 :     _lij.assign(_number_of_nodes, {});
     182        2802 :     _rP.assign(_number_of_nodes, 0.0);
     183        2802 :     _rM.assign(_number_of_nodes, 0.0);
     184        2802 :     _drP.assign(_number_of_nodes, {});
     185        2802 :     _drM.assign(_number_of_nodes, {});
     186        2802 :     _drP_dk.assign(_number_of_nodes, {});
     187        2802 :     _drM_dk.assign(_number_of_nodes, {});
     188        2802 :     _fa.assign(_number_of_nodes, {});
     189        2802 :     _dfa.assign(_number_of_nodes, {});
     190        2802 :     _dFij_dKik.assign(_number_of_nodes, {});
     191        2802 :     _dFij_dKjk.assign(_number_of_nodes, {});
     192             :   }
     193       58342 : }
     194             : 
     195             : void
     196        1329 : AdvectiveFluxCalculatorBase::meshChanged()
     197             : {
     198             :   ElementUserObject::meshChanged();
     199             : 
     200             :   // Signal that _kij, _valence, etc need to be rebuilt
     201        1329 :   _resizing_needed = true;
     202        1329 : }
     203             : 
     204             : void
     205      195735 : AdvectiveFluxCalculatorBase::initialize()
     206             : {
     207             :   // Zero _kij and falsify _u_nodal_computed_by_thread ready for building in execute() and
     208             :   // finalize()
     209      195735 :   _u_nodal_computed_by_thread.assign(_number_of_nodes, false);
     210     5253089 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     211     5057354 :     _kij[sequential_i].assign(_connections.sequentialConnectionsToSequentialID(sequential_i).size(),
     212    10114708 :                               0.0);
     213      195735 : }
     214             : 
     215             : void
     216     2250990 : AdvectiveFluxCalculatorBase::execute()
     217             : {
     218             :   // compute _kij contributions from this element that is local to this processor
     219             :   // and record _u_nodal
     220     7823330 :   for (unsigned i = 0; i < _current_elem->n_nodes(); ++i)
     221             :   {
     222     5572340 :     const dof_id_type node_i = _current_elem->node_id(i);
     223     5572340 :     const dof_id_type sequential_i = _connections.sequentialID(node_i);
     224     5572340 :     if (!_u_nodal_computed_by_thread[sequential_i])
     225             :     {
     226     2802812 :       _u_nodal[sequential_i] = computeU(i);
     227             :       _u_nodal_computed_by_thread[sequential_i] = true;
     228             :     }
     229    21209660 :     for (unsigned j = 0; j < _current_elem->n_nodes(); ++j)
     230             :     {
     231    15637320 :       const dof_id_type node_j = _current_elem->node_id(j);
     232    66572120 :       for (unsigned qp = 0; qp < _qrule->n_points(); ++qp)
     233    50934800 :         executeOnElement(node_i, node_j, i, j, qp);
     234             :     }
     235             :   }
     236     2250990 : }
     237             : 
     238             : void
     239    50934800 : AdvectiveFluxCalculatorBase::executeOnElement(
     240             :     dof_id_type global_i, dof_id_type global_j, unsigned local_i, unsigned local_j, unsigned qp)
     241             : {
     242             :   // KT Eqn (20)
     243    50934800 :   const dof_id_type sequential_i = _connections.sequentialID(global_i);
     244    50934800 :   const unsigned index_i_to_j = _connections.indexOfGlobalConnection(global_i, global_j);
     245    50934800 :   _kij[sequential_i][index_i_to_j] += _JxW[qp] * _coord[qp] * computeVelocity(local_i, local_j, qp);
     246    50934800 : }
     247             : 
     248             : void
     249       88061 : AdvectiveFluxCalculatorBase::threadJoin(const UserObject & uo)
     250             : {
     251             :   const auto & afc = static_cast<const AdvectiveFluxCalculatorBase &>(uo);
     252             :   // add the values of _kij computed by different threads
     253     2240085 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     254             :   {
     255             :     const std::size_t num_con_i =
     256     2152024 :         _connections.sequentialConnectionsToSequentialID(sequential_i).size();
     257    12084202 :     for (std::size_t j = 0; j < num_con_i; ++j)
     258     9932178 :       _kij[sequential_i][j] += afc._kij[sequential_i][j];
     259             :   }
     260             : 
     261             :   // gather the values of _u_nodal computed by different threads
     262     2240085 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     263     2152024 :     if (!_u_nodal_computed_by_thread[sequential_i] && afc._u_nodal_computed_by_thread[sequential_i])
     264      724615 :       _u_nodal[sequential_i] = afc._u_nodal[sequential_i];
     265       88061 : }
     266             : 
     267             : void
     268      107674 : AdvectiveFluxCalculatorBase::finalize()
     269             : {
     270             :   // Overall: ensure _kij is fully built, then compute Kuzmin-Turek D, L, f^a and
     271             :   // relevant Jacobian information, and then the relevant quantities into _flux_out and
     272             :   // _dflux_out_du, _dflux_out_dKjk
     273             : 
     274      107674 :   if (_app.n_processors() > 1)
     275       77990 :     exchangeGhostedInfo();
     276             : 
     277             :   // Calculate KuzminTurek D matrix
     278             :   // See Eqn (32)
     279             :   // This adds artificial diffusion, which eliminates any spurious oscillations
     280             :   // The idea is that D will remove all negative off-diagonal elements when it is added to K
     281             :   // This is identical to full upwinding
     282     3013004 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     283             :   {
     284             :     const std::vector<dof_id_type> & con_i =
     285     2905330 :         _connections.sequentialConnectionsToSequentialID(sequential_i);
     286             :     const std::size_t num_con_i = con_i.size();
     287     2905330 :     _dij[sequential_i].assign(num_con_i, 0.0);
     288     2905330 :     _dDij_dKij[sequential_i].assign(num_con_i, 0.0);
     289     2905330 :     _dDij_dKji[sequential_i].assign(num_con_i, 0.0);
     290     2905330 :     _dDii_dKij[sequential_i].assign(num_con_i, 0.0);
     291     2905330 :     _dDii_dKji[sequential_i].assign(num_con_i, 0.0);
     292             :     const unsigned index_i_to_i =
     293     2905330 :         _connections.indexOfSequentialConnection(sequential_i, sequential_i);
     294    15638200 :     for (std::size_t j = 0; j < num_con_i; ++j)
     295             :     {
     296    12732870 :       const dof_id_type sequential_j = con_i[j];
     297    12732870 :       if (sequential_i == sequential_j)
     298     2905330 :         continue;
     299             :       const unsigned index_j_to_i =
     300     9827540 :           _connections.indexOfSequentialConnection(sequential_j, sequential_i);
     301     9827540 :       const Real kij = _kij[sequential_i][j];
     302     9827540 :       const Real kji = _kij[sequential_j][index_j_to_i];
     303     9827540 :       if ((kij <= kji) && (kij < 0.0))
     304             :       {
     305     4773317 :         _dij[sequential_i][j] = -kij;
     306     4773317 :         _dDij_dKij[sequential_i][j] = -1.0;
     307     4773317 :         _dDii_dKij[sequential_i][j] += 1.0;
     308             :       }
     309     5054223 :       else if ((kji <= kij) && (kji < 0.0))
     310             :       {
     311     4414455 :         _dij[sequential_i][j] = -kji;
     312     4414455 :         _dDij_dKji[sequential_i][j] = -1.0;
     313     4414455 :         _dDii_dKji[sequential_i][j] += 1.0;
     314             :       }
     315             :       else
     316      639768 :         _dij[sequential_i][j] = 0.0;
     317     9827540 :       _dij[sequential_i][index_i_to_i] -= _dij[sequential_i][j];
     318             :     }
     319             :   }
     320             : 
     321             :   // Calculate KuzminTurek L matrix
     322             :   // See Fig 2: L = K + D
     323     3013004 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     324             :   {
     325             :     const std::vector<dof_id_type> & con_i =
     326     2905330 :         _connections.sequentialConnectionsToSequentialID(sequential_i);
     327             :     const std::size_t num_con_i = con_i.size();
     328     2905330 :     _lij[sequential_i].assign(num_con_i, 0.0);
     329    15638200 :     for (std::size_t j = 0; j < num_con_i; ++j)
     330    12732870 :       _lij[sequential_i][j] = _kij[sequential_i][j] + _dij[sequential_i][j];
     331             :   }
     332             : 
     333             :   // Compute KuzminTurek R matrices
     334             :   // See Eqns (49) and (12)
     335     3013004 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     336             :   {
     337     2905330 :     _rP[sequential_i] = rPlus(sequential_i, _drP[sequential_i], _drP_dk[sequential_i]);
     338     2905330 :     _rM[sequential_i] = rMinus(sequential_i, _drM[sequential_i], _drM_dk[sequential_i]);
     339             :   }
     340             : 
     341             :   // Calculate KuzminTurek f^{a} matrix
     342             :   // This is the antidiffusive flux
     343             :   // See Eqn (50)
     344     3013004 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     345             :   {
     346             :     const std::vector<dof_id_type> & con_i =
     347     2905330 :         _connections.globalConnectionsToSequentialID(sequential_i);
     348             :     const unsigned num_con_i = con_i.size();
     349     2905330 :     _fa[sequential_i].assign(num_con_i, 0.0);
     350     2905330 :     _dfa[sequential_i].resize(num_con_i);
     351     2905330 :     _dFij_dKik[sequential_i].resize(num_con_i);
     352     2905330 :     _dFij_dKjk[sequential_i].resize(num_con_i);
     353    15638200 :     for (std::size_t j = 0; j < num_con_i; ++j)
     354             :     {
     355    85782400 :       for (const auto & global_k : con_i)
     356    73049530 :         _dfa[sequential_i][j][global_k] = 0;
     357    12732870 :       const dof_id_type global_j = con_i[j];
     358    12732870 :       const std::vector<dof_id_type> & con_j = _connections.globalConnectionsToGlobalID(global_j);
     359             :       const unsigned num_con_j = con_j.size();
     360    85782400 :       for (const auto & global_k : con_j)
     361    73049530 :         _dfa[sequential_i][j][global_k] = 0;
     362    12732870 :       _dFij_dKik[sequential_i][j].assign(num_con_i, 0.0);
     363    12732870 :       _dFij_dKjk[sequential_i][j].assign(num_con_j, 0.0);
     364             :     }
     365             :   }
     366             : 
     367     3013004 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     368             :   {
     369     2905330 :     const dof_id_type global_i = _connections.globalID(sequential_i);
     370     2905330 :     const Real u_i = _u_nodal[sequential_i];
     371             :     const std::vector<dof_id_type> & con_i =
     372     2905330 :         _connections.sequentialConnectionsToSequentialID(sequential_i);
     373             :     const std::size_t num_con_i = con_i.size();
     374    15638200 :     for (std::size_t j = 0; j < num_con_i; ++j)
     375             :     {
     376    12732870 :       const dof_id_type sequential_j = con_i[j];
     377    12732870 :       if (sequential_i == sequential_j)
     378     2905330 :         continue;
     379             :       const unsigned index_j_to_i =
     380     9827540 :           _connections.indexOfSequentialConnection(sequential_j, sequential_i);
     381     9827540 :       const Real Lij = _lij[sequential_i][j];
     382     9827540 :       const Real Lji = _lij[sequential_j][index_j_to_i];
     383     9827540 :       if (Lji >= Lij) // node i is upwind of node j.
     384             :       {
     385     5340023 :         const Real Dij = _dij[sequential_i][j];
     386     5340023 :         const dof_id_type global_j = _connections.globalID(sequential_j);
     387     5340023 :         const Real u_j = _u_nodal[sequential_j];
     388             :         Real prefactor = 0.0;
     389             :         std::vector<Real> dprefactor_du(num_con_i,
     390     5340023 :                                         0.0); // dprefactor_du[j] = d(prefactor)/du[sequential_j]
     391             :         std::vector<Real> dprefactor_dKij(
     392     5340023 :             num_con_i, 0.0);      // dprefactor_dKij[j] = d(prefactor)/dKij[sequential_i][j].
     393             :         Real dprefactor_dKji = 0; // dprefactor_dKji = d(prefactor)/dKij[sequential_j][index_j_to_i]
     394     5340023 :         if (u_i >= u_j)
     395             :         {
     396     3199436 :           if (Lji <= _rP[sequential_i] * Dij)
     397             :           {
     398             :             prefactor = Lji;
     399      717277 :             dprefactor_dKij[j] += _dDij_dKji[sequential_j][index_j_to_i];
     400      717277 :             dprefactor_dKji += 1.0 + _dDij_dKij[sequential_j][index_j_to_i];
     401             :           }
     402             :           else
     403             :           {
     404             :             prefactor = _rP[sequential_i] * Dij;
     405    16133645 :             for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
     406    13651486 :               dprefactor_du[ind_j] = _drP[sequential_i][ind_j] * Dij;
     407     2482159 :             dprefactor_dKij[j] += _rP[sequential_i] * _dDij_dKij[sequential_i][j];
     408     2482159 :             dprefactor_dKji += _rP[sequential_i] * _dDij_dKji[sequential_i][j];
     409    16133645 :             for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
     410    13651486 :               dprefactor_dKij[ind_j] += _drP_dk[sequential_i][ind_j] * Dij;
     411             :           }
     412             :         }
     413             :         else
     414             :         {
     415     2140587 :           if (Lji <= _rM[sequential_i] * Dij)
     416             :           {
     417             :             prefactor = Lji;
     418      484272 :             dprefactor_dKij[j] += _dDij_dKji[sequential_j][index_j_to_i];
     419      484272 :             dprefactor_dKji += 1.0 + _dDij_dKij[sequential_j][index_j_to_i];
     420             :           }
     421             :           else
     422             :           {
     423             :             prefactor = _rM[sequential_i] * Dij;
     424    12642380 :             for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
     425    10986065 :               dprefactor_du[ind_j] = _drM[sequential_i][ind_j] * Dij;
     426     1656315 :             dprefactor_dKij[j] += _rM[sequential_i] * _dDij_dKij[sequential_i][j];
     427     1656315 :             dprefactor_dKji += _rM[sequential_i] * _dDij_dKji[sequential_i][j];
     428    12642380 :             for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
     429    10986065 :               dprefactor_dKij[ind_j] += _drM_dk[sequential_i][ind_j] * Dij;
     430             :           }
     431             :         }
     432     5340023 :         _fa[sequential_i][j] = prefactor * (u_i - u_j);
     433     5340023 :         _dfa[sequential_i][j][global_i] = prefactor;
     434     5340023 :         _dfa[sequential_i][j][global_j] = -prefactor;
     435    38544235 :         for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
     436             :         {
     437    33204212 :           _dfa[sequential_i][j][_connections.globalID(con_i[ind_j])] +=
     438    33204212 :               dprefactor_du[ind_j] * (u_i - u_j);
     439    33204212 :           _dFij_dKik[sequential_i][j][ind_j] += dprefactor_dKij[ind_j] * (u_i - u_j);
     440             :         }
     441     5340023 :         _dFij_dKjk[sequential_i][j][index_j_to_i] += dprefactor_dKji * (u_i - u_j);
     442     5340023 :       }
     443             :     }
     444             :   }
     445             : 
     446     3013004 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     447             :   {
     448             :     const std::vector<dof_id_type> & con_i =
     449     2905330 :         _connections.sequentialConnectionsToSequentialID(sequential_i);
     450             :     const std::size_t num_con_i = con_i.size();
     451    15638200 :     for (std::size_t j = 0; j < num_con_i; ++j)
     452             :     {
     453    12732870 :       const dof_id_type sequential_j = con_i[j];
     454    12732870 :       if (sequential_i == sequential_j)
     455     2905330 :         continue;
     456             :       const unsigned index_j_to_i =
     457     9827540 :           _connections.indexOfSequentialConnection(sequential_j, sequential_i);
     458     9827540 :       if (_lij[sequential_j][index_j_to_i] < _lij[sequential_i][j]) // node_i is downwind of node_j.
     459             :       {
     460     4487517 :         _fa[sequential_i][j] = -_fa[sequential_j][index_j_to_i];
     461    41600605 :         for (const auto & dof_deriv : _dfa[sequential_j][index_j_to_i])
     462    37113088 :           _dfa[sequential_i][j][dof_deriv.first] = -dof_deriv.second;
     463    31599965 :         for (std::size_t k = 0; k < num_con_i; ++k)
     464    27112448 :           _dFij_dKik[sequential_i][j][k] = -_dFij_dKjk[sequential_j][index_j_to_i][k];
     465             :         const std::size_t num_con_j =
     466     4487517 :             _connections.sequentialConnectionsToSequentialID(sequential_j).size();
     467    31592067 :         for (std::size_t k = 0; k < num_con_j; ++k)
     468    27104550 :           _dFij_dKjk[sequential_i][j][k] = -_dFij_dKik[sequential_j][index_j_to_i][k];
     469             :       }
     470             :     }
     471             :   }
     472             : 
     473             :   // zero _flux_out and its derivatives
     474      107674 :   _flux_out.assign(_number_of_nodes, 0.0);
     475             :   // The derivatives are a bit complicated.
     476             :   // If i is upwind of a node "j" then _flux_out[i] depends on all nodes connected to i.
     477             :   // But if i is downwind of a node "j" then _flux_out depends on all nodes connected with node
     478             :   // j.
     479      107674 :   _dflux_out_du.assign(_number_of_nodes, std::map<dof_id_type, Real>());
     480     3013004 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     481    15638200 :     for (const auto & j : _connections.globalConnectionsToSequentialID(sequential_i))
     482             :     {
     483    12732870 :       _dflux_out_du[sequential_i][j] = 0.0;
     484    85782400 :       for (const auto & neighbors_j : _connections.globalConnectionsToGlobalID(j))
     485    73049530 :         _dflux_out_du[sequential_i][neighbors_j] = 0.0;
     486             :     }
     487      107674 :   _dflux_out_dKjk.resize(_number_of_nodes);
     488     3013004 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     489             :   {
     490             :     const std::vector<dof_id_type> & con_i =
     491     2905330 :         _connections.sequentialConnectionsToSequentialID(sequential_i);
     492             :     const std::size_t num_con_i = con_i.size();
     493     2905330 :     _dflux_out_dKjk[sequential_i].resize(num_con_i);
     494    15638200 :     for (std::size_t j = 0; j < num_con_i; ++j)
     495             :     {
     496    12732870 :       const dof_id_type sequential_j = con_i[j];
     497             :       const std::vector<dof_id_type> & con_j =
     498    12732870 :           _connections.sequentialConnectionsToSequentialID(sequential_j);
     499    12732870 :       _dflux_out_dKjk[sequential_i][j].assign(con_j.size(), 0.0);
     500             :     }
     501             :   }
     502             : 
     503             :   // Add everything together
     504             :   // See step 3 in Fig 2, noting Eqn (36)
     505     3013004 :   for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
     506             :   {
     507             :     const std::vector<dof_id_type> & con_i =
     508     2905330 :         _connections.sequentialConnectionsToSequentialID(sequential_i);
     509             :     const size_t num_con_i = con_i.size();
     510             :     const dof_id_type index_i_to_i =
     511     2905330 :         _connections.indexOfSequentialConnection(sequential_i, sequential_i);
     512    15638200 :     for (std::size_t j = 0; j < num_con_i; ++j)
     513             :     {
     514    12732870 :       const dof_id_type sequential_j = con_i[j];
     515    12732870 :       const dof_id_type global_j = _connections.globalID(sequential_j);
     516    12732870 :       const Real u_j = _u_nodal[sequential_j];
     517             : 
     518             :       // negative sign because residual = -Lu (KT equation (19))
     519    12732870 :       _flux_out[sequential_i] -= _lij[sequential_i][j] * u_j + _fa[sequential_i][j];
     520             : 
     521    12732870 :       _dflux_out_du[sequential_i][global_j] -= _lij[sequential_i][j];
     522   107197996 :       for (const auto & dof_deriv : _dfa[sequential_i][j])
     523    94465126 :         _dflux_out_du[sequential_i][dof_deriv.first] -= dof_deriv.second;
     524             : 
     525    12732870 :       _dflux_out_dKjk[sequential_i][index_i_to_i][j] -= 1.0 * u_j; // from the K in L = K + D
     526             : 
     527    12732870 :       if (sequential_j == sequential_i)
     528    15638200 :         for (dof_id_type k = 0; k < num_con_i; ++k)
     529    12732870 :           _dflux_out_dKjk[sequential_i][index_i_to_i][k] -= _dDii_dKij[sequential_i][k] * u_j;
     530             :       else
     531     9827540 :         _dflux_out_dKjk[sequential_i][index_i_to_i][j] -= _dDij_dKij[sequential_i][j] * u_j;
     532    85782400 :       for (dof_id_type k = 0; k < num_con_i; ++k)
     533    73049530 :         _dflux_out_dKjk[sequential_i][index_i_to_i][k] -= _dFij_dKik[sequential_i][j][k];
     534             : 
     535    12732870 :       if (sequential_j == sequential_i)
     536    15638200 :         for (unsigned k = 0; k < con_i.size(); ++k)
     537             :         {
     538             :           const unsigned index_k_to_i =
     539    12732870 :               _connections.indexOfSequentialConnection(con_i[k], sequential_i);
     540    12732870 :           _dflux_out_dKjk[sequential_i][k][index_k_to_i] -= _dDii_dKji[sequential_i][k] * u_j;
     541             :         }
     542             :       else
     543             :       {
     544             :         const unsigned index_j_to_i =
     545     9827540 :             _connections.indexOfSequentialConnection(sequential_j, sequential_i);
     546     9827540 :         _dflux_out_dKjk[sequential_i][j][index_j_to_i] -= _dDij_dKji[sequential_i][j] * u_j;
     547             :       }
     548    73049530 :       for (unsigned k = 0;
     549    85782400 :            k < _connections.sequentialConnectionsToSequentialID(sequential_j).size();
     550             :            ++k)
     551    73049530 :         _dflux_out_dKjk[sequential_i][j][k] -= _dFij_dKjk[sequential_i][j][k];
     552             :     }
     553             :   }
     554      107674 : }
     555             : 
     556             : Real
     557     2905330 : AdvectiveFluxCalculatorBase::rPlus(dof_id_type sequential_i,
     558             :                                    std::vector<Real> & dlimited_du,
     559             :                                    std::vector<Real> & dlimited_dk) const
     560             : {
     561     2905330 :   const std::size_t num_con = _connections.sequentialConnectionsToSequentialID(sequential_i).size();
     562     2905330 :   dlimited_du.assign(num_con, 0.0);
     563     2905330 :   dlimited_dk.assign(num_con, 0.0);
     564     2905330 :   if (_flux_limiter_type == FluxLimiterTypeEnum::None)
     565             :     return 0.0;
     566             :   std::vector<Real> dp_du;
     567             :   std::vector<Real> dp_dk;
     568     2866480 :   const Real p = PQPlusMinus(sequential_i, PQPlusMinusEnum::PPlus, dp_du, dp_dk);
     569     2866480 :   if (p == 0.0)
     570             :     // Comment after Eqn (49): if P=0 then there's no antidiffusion, so no need to remove it
     571             :     return 1.0;
     572             :   std::vector<Real> dq_du;
     573             :   std::vector<Real> dq_dk;
     574     1831282 :   const Real q = PQPlusMinus(sequential_i, PQPlusMinusEnum::QPlus, dq_du, dq_dk);
     575             : 
     576     1831282 :   const Real r = q / p;
     577             :   Real limited;
     578             :   Real dlimited_dr;
     579     1831282 :   limitFlux(1.0, r, limited, dlimited_dr);
     580             : 
     581             :   const Real p2 = std::pow(p, 2);
     582     9790953 :   for (std::size_t j = 0; j < num_con; ++j)
     583             :   {
     584     7959671 :     const Real dr_du = dq_du[j] / p - q * dp_du[j] / p2;
     585     7959671 :     const Real dr_dk = dq_dk[j] / p - q * dp_dk[j] / p2;
     586     7959671 :     dlimited_du[j] = dlimited_dr * dr_du;
     587     7959671 :     dlimited_dk[j] = dlimited_dr * dr_dk;
     588             :   }
     589     1831282 :   return limited;
     590     2866480 : }
     591             : 
     592             : Real
     593     2905330 : AdvectiveFluxCalculatorBase::rMinus(dof_id_type sequential_i,
     594             :                                     std::vector<Real> & dlimited_du,
     595             :                                     std::vector<Real> & dlimited_dk) const
     596             : {
     597     2905330 :   const std::size_t num_con = _connections.sequentialConnectionsToSequentialID(sequential_i).size();
     598     2905330 :   dlimited_du.assign(num_con, 0.0);
     599     2905330 :   dlimited_dk.assign(num_con, 0.0);
     600     2905330 :   if (_flux_limiter_type == FluxLimiterTypeEnum::None)
     601             :     return 0.0;
     602             :   std::vector<Real> dp_du;
     603             :   std::vector<Real> dp_dk;
     604     2866480 :   const Real p = PQPlusMinus(sequential_i, PQPlusMinusEnum::PMinus, dp_du, dp_dk);
     605     2866480 :   if (p == 0.0)
     606             :     // Comment after Eqn (49): if P=0 then there's no antidiffusion, so no need to remove it
     607             :     return 1.0;
     608             :   std::vector<Real> dq_du;
     609             :   std::vector<Real> dq_dk;
     610     1037157 :   const Real q = PQPlusMinus(sequential_i, PQPlusMinusEnum::QMinus, dq_du, dq_dk);
     611             : 
     612     1037157 :   const Real r = q / p;
     613             :   Real limited;
     614             :   Real dlimited_dr;
     615     1037157 :   limitFlux(1.0, r, limited, dlimited_dr);
     616             : 
     617             :   const Real p2 = std::pow(p, 2);
     618     6765130 :   for (std::size_t j = 0; j < num_con; ++j)
     619             :   {
     620     5727973 :     const Real dr_du = dq_du[j] / p - q * dp_du[j] / p2;
     621     5727973 :     const Real dr_dk = dq_dk[j] / p - q * dp_dk[j] / p2;
     622     5727973 :     dlimited_du[j] = dlimited_dr * dr_du;
     623     5727973 :     dlimited_dk[j] = dlimited_dr * dr_dk;
     624             :   }
     625     1037157 :   return limited;
     626     2866480 : }
     627             : 
     628             : void
     629     2868439 : AdvectiveFluxCalculatorBase::limitFlux(Real a, Real b, Real & limited, Real & dlimited_db) const
     630             : {
     631     2868439 :   limited = 0.0;
     632     2868439 :   dlimited_db = 0.0;
     633     2868439 :   if (_flux_limiter_type == FluxLimiterTypeEnum::None)
     634             :     return;
     635             : 
     636     2868439 :   if ((a >= 0.0 && b <= 0.0) || (a <= 0.0 && b >= 0.0))
     637             :     return;
     638     2326101 :   const Real s = (a > 0.0 ? 1.0 : -1.0);
     639             : 
     640     2326101 :   const Real lal = std::abs(a);
     641     2326101 :   const Real lbl = std::abs(b);
     642     2326101 :   const Real dlbl = (b >= 0.0 ? 1.0 : -1.0); // d(lbl)/db
     643     2326101 :   switch (_flux_limiter_type)
     644             :   {
     645       15947 :     case FluxLimiterTypeEnum::MinMod:
     646             :     {
     647       15947 :       if (lal <= lbl)
     648             :       {
     649        8330 :         limited = s * lal;
     650        8330 :         dlimited_db = 0.0;
     651             :       }
     652             :       else
     653             :       {
     654        7617 :         limited = s * lbl;
     655        7617 :         dlimited_db = s * dlbl;
     656             :       }
     657             :       return;
     658             :     }
     659       23550 :     case FluxLimiterTypeEnum::VanLeer:
     660             :     {
     661       23550 :       limited = s * 2 * lal * lbl / (lal + lbl);
     662       23550 :       dlimited_db = s * 2 * lal * (dlbl / (lal + lbl) - lbl * dlbl / std::pow(lal + lbl, 2));
     663       23550 :       return;
     664             :     }
     665       11174 :     case FluxLimiterTypeEnum::MC:
     666             :     {
     667       11174 :       const Real av = 0.5 * std::abs(a + b);
     668       11174 :       if (2 * lal <= av && lal <= lbl)
     669             :       {
     670             :         // 2 * lal is the smallest
     671        4812 :         limited = s * 2.0 * lal;
     672        4812 :         dlimited_db = 0.0;
     673             :       }
     674        6362 :       else if (2 * lbl <= av && lbl <= lal)
     675             :       {
     676             :         // 2 * lbl is the smallest
     677        2912 :         limited = s * 2.0 * lbl;
     678        2912 :         dlimited_db = s * 2.0 * dlbl;
     679             :       }
     680             :       else
     681             :       {
     682             :         // av is the smallest
     683        3450 :         limited = s * av;
     684             :         // if (a>0 and b>0) then d(av)/db = 0.5 = 0.5 * dlbl
     685             :         // if (a<0 and b<0) then d(av)/db = -0.5 = 0.5 * dlbl
     686             :         // if a and b have different sign then limited=0, above
     687        3450 :         dlimited_db = s * 0.5 * dlbl;
     688             :       }
     689             :       return;
     690             :     }
     691     2275430 :     case FluxLimiterTypeEnum::superbee:
     692             :     {
     693     2275430 :       const Real term1 = std::min(2.0 * lal, lbl);
     694     2275430 :       const Real term2 = std::min(lal, 2.0 * lbl);
     695     2275430 :       if (term1 >= term2)
     696             :       {
     697      759696 :         if (2.0 * lal <= lbl)
     698             :         {
     699      407338 :           limited = s * 2 * lal;
     700      407338 :           dlimited_db = 0.0;
     701             :         }
     702             :         else
     703             :         {
     704      352358 :           limited = s * lbl;
     705      352358 :           dlimited_db = s * dlbl;
     706             :         }
     707             :       }
     708             :       else
     709             :       {
     710     1515734 :         if (lal <= 2.0 * lbl)
     711             :         {
     712      947492 :           limited = s * lal;
     713      947492 :           dlimited_db = 0.0;
     714             :         }
     715             :         else
     716             :         {
     717      568242 :           limited = s * 2.0 * lbl;
     718      568242 :           dlimited_db = s * 2.0 * dlbl;
     719             :         }
     720             :       }
     721             :       return;
     722             :     }
     723             :     default:
     724             :       return;
     725             :   }
     726             : }
     727             : 
     728             : const std::map<dof_id_type, Real> &
     729     3370504 : AdvectiveFluxCalculatorBase::getdFluxOutdu(dof_id_type node_i) const
     730             : {
     731     3370504 :   return _dflux_out_du[_connections.sequentialID(node_i)];
     732             : }
     733             : 
     734             : const std::vector<std::vector<Real>> &
     735     1409956 : AdvectiveFluxCalculatorBase::getdFluxOutdKjk(dof_id_type node_i) const
     736             : {
     737     1409956 :   return _dflux_out_dKjk[_connections.sequentialID(node_i)];
     738             : }
     739             : 
     740             : Real
     741     5572340 : AdvectiveFluxCalculatorBase::getFluxOut(dof_id_type node_i) const
     742             : {
     743     5572340 :   return _flux_out[_connections.sequentialID(node_i)];
     744             : }
     745             : 
     746             : unsigned
     747     9411532 : AdvectiveFluxCalculatorBase::getValence(dof_id_type node_i) const
     748             : {
     749     9411532 :   return _valence[_connections.sequentialID(node_i)];
     750             : }
     751             : 
     752             : void
     753       56293 : AdvectiveFluxCalculatorBase::zeroedConnection(std::map<dof_id_type, Real> & the_map,
     754             :                                               dof_id_type node_i) const
     755             : {
     756             :   the_map.clear();
     757      333018 :   for (const auto & node_j : _connections.globalConnectionsToGlobalID(node_i))
     758      276725 :     the_map[node_j] = 0.0;
     759       56293 : }
     760             : 
     761             : Real
     762     8601399 : AdvectiveFluxCalculatorBase::PQPlusMinus(dof_id_type sequential_i,
     763             :                                          const PQPlusMinusEnum pq_plus_minus,
     764             :                                          std::vector<Real> & derivs,
     765             :                                          std::vector<Real> & dpqdk) const
     766             : {
     767             :   // Find the value of u at sequential_i
     768     8601399 :   const Real u_i = _u_nodal[sequential_i];
     769             : 
     770             :   // Connections to sequential_i
     771             :   const std::vector<dof_id_type> con_i =
     772     8601399 :       _connections.sequentialConnectionsToSequentialID(sequential_i);
     773             :   const std::size_t num_con = con_i.size();
     774             :   // The neighbor number of sequential_i to sequential_i
     775     8601399 :   const unsigned i_index_i = _connections.indexOfSequentialConnection(sequential_i, sequential_i);
     776             : 
     777             :   // Initialize the results
     778             :   Real result = 0.0;
     779     8601399 :   derivs.assign(num_con, 0.0);
     780     8601399 :   dpqdk.assign(num_con, 0.0);
     781             : 
     782             :   // Sum over all nodes connected with node_i.
     783    47243259 :   for (std::size_t j = 0; j < num_con; ++j)
     784             :   {
     785    38641860 :     const dof_id_type sequential_j = con_i[j];
     786    38641860 :     if (sequential_j == sequential_i)
     787     8601399 :       continue;
     788    30040461 :     const Real kentry = _kij[sequential_i][j];
     789             : 
     790             :     // Find the value of u at node_j
     791    30040461 :     const Real u_j = _u_nodal[sequential_j];
     792    30040461 :     const Real ujminusi = u_j - u_i;
     793             : 
     794             :     // Evaluate the i-j contribution to the result
     795    30040461 :     switch (pq_plus_minus)
     796             :     {
     797     9610628 :       case PQPlusMinusEnum::PPlus:
     798             :       {
     799     9610628 :         if (ujminusi < 0.0 && kentry < 0.0)
     800             :         {
     801     2600811 :           result += kentry * ujminusi;
     802     2600811 :           derivs[j] += kentry;
     803     2600811 :           derivs[i_index_i] -= kentry;
     804     2600811 :           dpqdk[j] += ujminusi;
     805             :         }
     806             :         break;
     807             :       }
     808     9610628 :       case PQPlusMinusEnum::PMinus:
     809             :       {
     810     9610628 :         if (ujminusi > 0.0 && kentry < 0.0)
     811             :         {
     812     1911807 :           result += kentry * ujminusi;
     813     1911807 :           derivs[j] += kentry;
     814     1911807 :           derivs[i_index_i] -= kentry;
     815     1911807 :           dpqdk[j] += ujminusi;
     816             :         }
     817             :         break;
     818             :       }
     819     6128389 :       case PQPlusMinusEnum::QPlus:
     820             :       {
     821     6128389 :         if (ujminusi > 0.0 && kentry > 0.0)
     822             :         {
     823     2054407 :           result += kentry * ujminusi;
     824     2054407 :           derivs[j] += kentry;
     825     2054407 :           derivs[i_index_i] -= kentry;
     826     2054407 :           dpqdk[j] += ujminusi;
     827             :         }
     828             :         break;
     829             :       }
     830     4690816 :       case PQPlusMinusEnum::QMinus:
     831             :       {
     832     4690816 :         if (ujminusi < 0.0 && kentry > 0.0)
     833             :         {
     834     1381557 :           result += kentry * ujminusi;
     835     1381557 :           derivs[j] += kentry;
     836     1381557 :           derivs[i_index_i] -= kentry;
     837     1381557 :           dpqdk[j] += ujminusi;
     838             :         }
     839             :         break;
     840             :       }
     841             :     }
     842             :   }
     843             : 
     844     8601399 :   return result;
     845     8601399 : }
     846             : 
     847             : void
     848        2028 : AdvectiveFluxCalculatorBase::buildCommLists()
     849             : {
     850             :   /**
     851             :    * Build the multi-processor communication lists.
     852             :    *
     853             :    * (A) We will have to send _u_nodal information to other processors.
     854             :    * This is because although we can Evaluate Variables at all elements in
     855             :    * _fe_problem.getNonlinearEvaluableElementRange(), in the PorousFlow setting
     856             :    * _u_nodal could depend on Material Properties within the elements, and
     857             :    * we can't access those Properties within the ghosted elements.
     858             :    *
     859             :    * (B) In a similar way, we need to send _kij information to other processors.
     860             :    * A similar strategy is followed
     861             :    */
     862             : 
     863             :   _nodes_to_receive.clear();
     864       33142 :   for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
     865       31114 :     if (this->hasBlocks(elem->subdomain_id()))
     866             :     {
     867       28678 :       const processor_id_type elem_pid = elem->processor_id();
     868       28678 :       if (elem_pid != _my_pid)
     869             :       {
     870        6292 :         if (_nodes_to_receive.find(elem_pid) == _nodes_to_receive.end())
     871        3992 :           _nodes_to_receive[elem_pid] = std::vector<dof_id_type>();
     872       26068 :         for (unsigned i = 0; i < elem->n_nodes(); ++i)
     873       19776 :           if (std::find(_nodes_to_receive[elem_pid].begin(),
     874       19776 :                         _nodes_to_receive[elem_pid].end(),
     875       39552 :                         elem->node_id(i)) == _nodes_to_receive[elem_pid].end())
     876       11238 :             _nodes_to_receive[elem_pid].push_back(elem->node_id(i));
     877             :       }
     878             :     }
     879             : 
     880             :   // exchange this info with other processors, building _nodes_to_send at the same time
     881             :   _nodes_to_send.clear();
     882        1996 :   auto nodes_action_functor = [this](processor_id_type pid, const std::vector<dof_id_type> & nts)
     883        1996 :   { _nodes_to_send[pid] = nts; };
     884        2028 :   Parallel::push_parallel_vector_data(this->comm(), _nodes_to_receive, nodes_action_functor);
     885             : 
     886             :   // At the moment,  _nodes_to_send and _nodes_to_receive contain global node numbers
     887             :   // It is slightly more efficient to convert to sequential node numbers
     888             :   // so that we don't have to keep doing things like
     889             :   // _u_nodal[_connections.sequentialID(_nodes_to_send[pid][i])
     890             :   // every time we send/receive
     891        4024 :   for (auto & kv : _nodes_to_send)
     892             :   {
     893        1996 :     const processor_id_type pid = kv.first;
     894             :     const std::size_t num_nodes = kv.second.size();
     895       13234 :     for (unsigned i = 0; i < num_nodes; ++i)
     896       11238 :       _nodes_to_send[pid][i] = _connections.sequentialID(_nodes_to_send[pid][i]);
     897             :   }
     898        4024 :   for (auto & kv : _nodes_to_receive)
     899             :   {
     900        1996 :     const processor_id_type pid = kv.first;
     901             :     const std::size_t num_nodes = kv.second.size();
     902       13234 :     for (unsigned i = 0; i < num_nodes; ++i)
     903       11238 :       _nodes_to_receive[pid][i] = _connections.sequentialID(_nodes_to_receive[pid][i]);
     904             :   }
     905             : 
     906             :   // Build pairs_to_receive
     907             :   _pairs_to_receive.clear();
     908       33142 :   for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
     909       31114 :     if (this->hasBlocks(elem->subdomain_id()))
     910             :     {
     911       28678 :       const processor_id_type elem_pid = elem->processor_id();
     912       28678 :       if (elem_pid != _my_pid)
     913             :       {
     914        6292 :         if (_pairs_to_receive.find(elem_pid) == _pairs_to_receive.end())
     915        3992 :           _pairs_to_receive[elem_pid] = std::vector<std::pair<dof_id_type, dof_id_type>>();
     916       26068 :         for (unsigned i = 0; i < elem->n_nodes(); ++i)
     917       93760 :           for (unsigned j = 0; j < elem->n_nodes(); ++j)
     918             :           {
     919       73984 :             std::pair<dof_id_type, dof_id_type> the_pair(elem->node_id(i), elem->node_id(j));
     920       73984 :             if (std::find(_pairs_to_receive[elem_pid].begin(),
     921       73984 :                           _pairs_to_receive[elem_pid].end(),
     922       73984 :                           the_pair) == _pairs_to_receive[elem_pid].end())
     923       55690 :               _pairs_to_receive[elem_pid].push_back(the_pair);
     924             :           }
     925             :       }
     926             :     }
     927             : 
     928             :   _pairs_to_send.clear();
     929             :   auto pairs_action_functor =
     930        1996 :       [this](processor_id_type pid, const std::vector<std::pair<dof_id_type, dof_id_type>> & pts)
     931        1996 :   { _pairs_to_send[pid] = pts; };
     932        2028 :   Parallel::push_parallel_vector_data(this->comm(), _pairs_to_receive, pairs_action_functor);
     933             : 
     934             :   // _pairs_to_send and _pairs_to_receive have been built using global node IDs
     935             :   // since all processors know about that.  However, using global IDs means
     936             :   // that every time we send/receive, we keep having to do things like
     937             :   // _kij[_connections.sequentialID(_pairs_to_send[pid][i].first)][_connections.indexOfGlobalConnection(_pairs_to_send[pid][i].first,
     938             :   // _pairs_to_send[pid][i].second)] which is quite inefficient.  So:
     939        4024 :   for (auto & kv : _pairs_to_send)
     940             :   {
     941        1996 :     const processor_id_type pid = kv.first;
     942             :     const std::size_t num_pairs = kv.second.size();
     943       57686 :     for (unsigned i = 0; i < num_pairs; ++i)
     944             :     {
     945       55690 :       _pairs_to_send[pid][i].second = _connections.indexOfGlobalConnection(
     946       55690 :           _pairs_to_send[pid][i].first, _pairs_to_send[pid][i].second);
     947       55690 :       _pairs_to_send[pid][i].first = _connections.sequentialID(_pairs_to_send[pid][i].first);
     948             :     }
     949             :   }
     950        4024 :   for (auto & kv : _pairs_to_receive)
     951             :   {
     952        1996 :     const processor_id_type pid = kv.first;
     953             :     const std::size_t num_pairs = kv.second.size();
     954       57686 :     for (unsigned i = 0; i < num_pairs; ++i)
     955             :     {
     956       55690 :       _pairs_to_receive[pid][i].second = _connections.indexOfGlobalConnection(
     957       55690 :           _pairs_to_receive[pid][i].first, _pairs_to_receive[pid][i].second);
     958       55690 :       _pairs_to_receive[pid][i].first = _connections.sequentialID(_pairs_to_receive[pid][i].first);
     959             :     }
     960             :   }
     961        2028 : }
     962             : 
     963             : void
     964       77990 : AdvectiveFluxCalculatorBase::exchangeGhostedInfo()
     965             : {
     966             :   // Exchange ghosted _u_nodal information with other processors
     967             :   std::map<processor_id_type, std::vector<Real>> unodal_to_send;
     968      155480 :   for (const auto & kv : _nodes_to_send)
     969             :   {
     970       77490 :     const processor_id_type pid = kv.first;
     971      154980 :     unodal_to_send[pid] = std::vector<Real>();
     972      505176 :     for (const auto & nd : kv.second)
     973      427686 :       unodal_to_send[pid].push_back(_u_nodal[nd]);
     974             :   }
     975             : 
     976             :   auto unodal_action_functor =
     977       77490 :       [this](processor_id_type pid, const std::vector<Real> & unodal_received)
     978             :   {
     979             :     const std::size_t msg_size = unodal_received.size();
     980             :     mooseAssert(msg_size == _nodes_to_receive[pid].size(),
     981             :                 "Message size, " << msg_size
     982             :                                  << ", incompatible with nodes_to_receive, which has size "
     983             :                                  << _nodes_to_receive[pid].size());
     984      505176 :     for (unsigned i = 0; i < msg_size; ++i)
     985      427686 :       _u_nodal[_nodes_to_receive[pid][i]] = unodal_received[i];
     986       77990 :   };
     987       77990 :   Parallel::push_parallel_vector_data(this->comm(), unodal_to_send, unodal_action_functor);
     988             : 
     989             :   // Exchange _kij information with other processors
     990             :   std::map<processor_id_type, std::vector<Real>> kij_to_send;
     991      155480 :   for (const auto & kv : _pairs_to_send)
     992             :   {
     993       77490 :     const processor_id_type pid = kv.first;
     994      154980 :     kij_to_send[pid] = std::vector<Real>();
     995     2091984 :     for (const auto & pr : kv.second)
     996     2014494 :       kij_to_send[pid].push_back(_kij[pr.first][pr.second]);
     997             :   }
     998             : 
     999       77490 :   auto kij_action_functor = [this](processor_id_type pid, const std::vector<Real> & kij_received)
    1000             :   {
    1001             :     const std::size_t msg_size = kij_received.size();
    1002             :     mooseAssert(msg_size == _pairs_to_receive[pid].size(),
    1003             :                 "Message size, " << msg_size
    1004             :                                  << ", incompatible with pairs_to_receive, which has size "
    1005             :                                  << _pairs_to_receive[pid].size());
    1006     2091984 :     for (unsigned i = 0; i < msg_size; ++i)
    1007     2014494 :       _kij[_pairs_to_receive[pid][i].first][_pairs_to_receive[pid][i].second] += kij_received[i];
    1008       77990 :   };
    1009       77990 :   Parallel::push_parallel_vector_data(this->comm(), kij_to_send, kij_action_functor);
    1010       77990 : }

Generated by: LCOV version 1.14