LCOV - code coverage report
Current view: top level - src/problems - QuadSubChannel1PhaseProblem.C (source / functions) Hit Total Coverage
Test: idaholab/moose subchannel: #33187 (5aa0b2) with base d7c4bd Lines: 286 311 92.0 %
Date: 2026-06-30 12:24:57 Functions: 5 6 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "QuadSubChannel1PhaseProblem.h"
      11             : #include "AuxiliarySystem.h"
      12             : #include "SCM.h"
      13             : #include "SinglePhaseFluidProperties.h"
      14             : 
      15             : registerMooseObject("SubChannelApp", QuadSubChannel1PhaseProblem);
      16             : 
      17             : InputParameters
      18         274 : QuadSubChannel1PhaseProblem::validParams()
      19             : {
      20         274 :   InputParameters params = SubChannel1PhaseProblem::validParams();
      21         274 :   params.addClassDescription(
      22             :       "Solver class for subchannels in a square lattice assembly and bare fuel pins");
      23         274 :   return params;
      24           0 : }
      25             : 
      26         137 : QuadSubChannel1PhaseProblem::QuadSubChannel1PhaseProblem(const InputParameters & params)
      27         137 :   : SubChannel1PhaseProblem(params), _subchannel_mesh(SCM::getMesh<QuadSubChannelMesh>(_mesh))
      28             : {
      29         137 : }
      30             : 
      31             : void
      32         196 : QuadSubChannel1PhaseProblem::initializeSolution()
      33             : {
      34         196 :   detectDeformation();
      35             : 
      36             :   /// update surface area, wetted perimeter based on: Dpin, displacement
      37         196 :   if (_deformation)
      38             :   {
      39           5 :     _console << " =========== DEFORMATION RECALCULATION ACTIVATED ============== " << std::endl;
      40             :     Real standard_area, additional_area, wetted_perimeter, displaced_area;
      41           5 :     auto pitch = _subchannel_mesh.getPitch();
      42           5 :     auto side_gap = _subchannel_mesh.getSideGap();
      43           5 :     auto z_blockage = _subchannel_mesh.getZBlockage();
      44           5 :     auto index_blockage = _subchannel_mesh.getIndexBlockage();
      45           5 :     auto reduction_blockage = _subchannel_mesh.getReductionBlockage();
      46          35 :     for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
      47             :     {
      48         300 :       for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
      49             :       {
      50         270 :         auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
      51         270 :         auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
      52         270 :         auto Z = _z_grid[iz];
      53             :         Real rod_area = 0.0;
      54             :         Real rod_perimeter = 0.0;
      55         750 :         for (auto i_pin : _subchannel_mesh.getChannelPins(i_ch))
      56             :         {
      57         480 :           auto * pin_node = _subchannel_mesh.getPinNode(i_pin, iz);
      58         480 :           rod_area += 0.25 * 0.25 * M_PI * (*_Dpin_soln)(pin_node) * (*_Dpin_soln)(pin_node);
      59         480 :           rod_perimeter += 0.25 * M_PI * (*_Dpin_soln)(pin_node);
      60             :         }
      61             : 
      62         270 :         if (subch_type == EChannelType::CORNER)
      63             :         {
      64         120 :           standard_area = 0.25 * pitch * pitch;
      65         120 :           displaced_area = (2 * side_gap + pitch) * (*_displacement_soln)(node) / sqrt(2) +
      66         120 :                            (*_displacement_soln)(node) * (*_displacement_soln)(node) / 2;
      67         120 :           additional_area = pitch * side_gap + side_gap * side_gap;
      68         120 :           wetted_perimeter =
      69         120 :               rod_perimeter + pitch + 2 * side_gap + 2 * (*_displacement_soln)(node) / sqrt(2);
      70             :         }
      71         150 :         else if (subch_type == EChannelType::EDGE)
      72             :         {
      73         120 :           standard_area = 0.5 * pitch * pitch;
      74         120 :           additional_area = pitch * side_gap;
      75         120 :           displaced_area = pitch * (*_displacement_soln)(node);
      76         120 :           wetted_perimeter = rod_perimeter + pitch;
      77             :         }
      78             :         else
      79             :         {
      80          30 :           standard_area = pitch * pitch;
      81             :           displaced_area = 0.0;
      82             :           additional_area = 0.0;
      83             :           wetted_perimeter = rod_perimeter;
      84             :         }
      85             : 
      86             :         /// Calculate subchannel area
      87         270 :         auto subchannel_area = displaced_area + standard_area + additional_area - rod_area;
      88             : 
      89             :         /// Correct subchannel area and wetted perimeter in case of overlapping pins
      90             :         auto overlapping_pin_area = 0.0;
      91             :         auto overlapping_wetted_perimeter = 0.0;
      92         990 :         for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
      93             :         {
      94         720 :           auto gap_pins = _subchannel_mesh.getGapPins(i_gap);
      95             :           auto pin_1 = gap_pins.first;
      96             :           auto pin_2 = gap_pins.second;
      97         720 :           auto * pin_node_1 = _subchannel_mesh.getPinNode(pin_1, iz);
      98         720 :           auto * pin_node_2 = _subchannel_mesh.getPinNode(pin_2, iz);
      99         720 :           auto Diameter1 = (*_Dpin_soln)(pin_node_1);
     100         720 :           auto Radius1 = Diameter1 / 2.0;
     101         720 :           auto Diameter2 = (*_Dpin_soln)(pin_node_2);
     102         720 :           auto Radius2 = Diameter2 / 2.0;
     103         720 :           auto pitch = _subchannel_mesh.getPitch();
     104             : 
     105         720 :           if (pitch < (Radius1 + Radius2)) // overlapping pins
     106             :           {
     107           0 :             mooseWarning(" The gap of index : '", i_gap, " at axial cell ", iz, " ' is blocked.");
     108           0 :             auto cos1 =
     109           0 :                 (pitch * pitch + Radius1 * Radius1 - Radius2 * Radius2) / (2 * pitch * Radius1);
     110           0 :             auto cos2 =
     111           0 :                 (pitch * pitch + Radius2 * Radius2 - Radius1 * Radius1) / (2 * pitch * Radius2);
     112           0 :             auto angle1 = 2.0 * acos(cos1);
     113           0 :             auto angle2 = 2.0 * acos(cos2);
     114             :             // half of the intersecting arc-length
     115           0 :             overlapping_wetted_perimeter += 0.5 * angle1 * Radius1 + 0.5 * angle2 * Radius2;
     116             :             // Half of the overlapping area
     117           0 :             overlapping_pin_area +=
     118           0 :                 0.5 * Radius1 * Radius1 * acos(cos1) + 0.5 * Radius2 * Radius2 * acos(cos2) -
     119           0 :                 0.25 * sqrt((-pitch + Radius1 + Radius2) * (pitch + Radius1 - Radius2) *
     120           0 :                             (pitch - Radius1 + Radius2) * (pitch + Radius1 + Radius2));
     121             :           }
     122             :         }
     123         270 :         subchannel_area += overlapping_pin_area;           // correct surface area
     124         270 :         wetted_perimeter += -overlapping_wetted_perimeter; // correct wetted perimeter
     125             : 
     126             :         /// Apply area reduction on subchannels affected by blockage
     127             :         auto index = 0;
     128         540 :         for (const auto & i_blockage : index_blockage)
     129             :         {
     130         270 :           if (i_ch == i_blockage && (Z >= z_blockage.front() && Z <= z_blockage.back()))
     131             :           {
     132           5 :             subchannel_area *= reduction_blockage[index];
     133             :           }
     134         270 :           index++;
     135             :         }
     136             : 
     137         270 :         _S_flow_soln->set(node, subchannel_area);
     138         270 :         _w_perim_soln->set(node, wetted_perimeter);
     139             :       }
     140             :     }
     141             :     /// update map of gap between pins (gij) based on: Dpin, displacement
     142          35 :     for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
     143             :     {
     144         390 :       for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
     145             :       {
     146         360 :         auto gap_pins = _subchannel_mesh.getGapPins(i_gap);
     147             :         auto pin_1 = gap_pins.first;
     148             :         auto pin_2 = gap_pins.second;
     149         360 :         auto * pin_node_1 = _subchannel_mesh.getPinNode(pin_1, iz);
     150         360 :         auto * pin_node_2 = _subchannel_mesh.getPinNode(pin_2, iz);
     151         360 :         if (pin_1 == pin_2) // Corner or edge gap
     152             :         {
     153             :           auto displacement = 0.0;
     154             :           auto counter = 0.0;
     155        1200 :           for (auto i_ch : _subchannel_mesh.getPinChannels(pin_1))
     156             :           {
     157         960 :             auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
     158         960 :             auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
     159         960 :             if (subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER)
     160             :             {
     161         720 :               displacement += (*_displacement_soln)(node);
     162         720 :               counter += 1.0;
     163             :             }
     164             :           }
     165         240 :           displacement = displacement / counter;
     166         240 :           _subchannel_mesh.setGapWidth(
     167         240 :               iz, i_gap, (pitch - (*_Dpin_soln)(pin_node_1)) / 2.0 + side_gap + displacement);
     168             :         }
     169             :         else // center gap
     170             :         {
     171         120 :           _subchannel_mesh.setGapWidth(
     172         120 :               iz, i_gap, pitch - (*_Dpin_soln)(pin_node_1) / 2.0 - (*_Dpin_soln)(pin_node_2) / 2.0);
     173             :         }
     174             :         // if pins come in contact, the gap is zero
     175         360 :         if (_subchannel_mesh.getGapWidth(iz, i_gap) <= 0.0)
     176           0 :           _subchannel_mesh.setGapWidth(iz, i_gap, 0.0);
     177             :       }
     178             :     }
     179           5 :   }
     180             : 
     181        4813 :   for (unsigned int iz = 1; iz < _n_cells + 1; iz++)
     182             :   {
     183       77354 :     for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
     184             :     {
     185       72737 :       auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
     186       72737 :       auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
     187       72737 :       _mdot_soln->set(node_out, (*_mdot_soln)(node_in));
     188             :     }
     189             :   }
     190             : 
     191             :   // We must do a global assembly to make sure data is parallel consistent before we do things
     192             :   // like compute L2 norms
     193         196 :   _aux->solution().close();
     194         196 : }
     195             : 
     196             : Real
     197      191130 : QuadSubChannel1PhaseProblem::computeAddedHeatPin(unsigned int i_ch, unsigned int iz) const
     198             : {
     199      191130 :   if (!_pin_mesh_exist)
     200             :     return 0.0;
     201             : 
     202             :   // Compute axial location of nodes.
     203      185730 :   auto z2 = _z_grid[iz];
     204      185730 :   auto z1 = _z_grid[iz - 1];
     205      185730 :   auto heated_length = _subchannel_mesh.getHeatedLength();
     206      185730 :   auto unheated_length_entry = _subchannel_mesh.getHeatedLengthEntry();
     207      185730 :   if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
     208      183885 :       MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
     209             :   {
     210             :     // Compute the height of this element.
     211      182040 :     auto dz = z2 - z1;
     212             :     auto heat_rate_in = 0.0;
     213             :     auto heat_rate_out = 0.0;
     214      613960 :     for (auto i_pin : _subchannel_mesh.getChannelPins(i_ch))
     215             :     {
     216      431920 :       auto * node_in = _subchannel_mesh.getPinNode(i_pin, iz - 1);
     217      431920 :       auto * node_out = _subchannel_mesh.getPinNode(i_pin, iz);
     218      431920 :       heat_rate_out += 0.25 * (*_q_prime_soln)(node_out);
     219      431920 :       heat_rate_in += 0.25 * (*_q_prime_soln)(node_in);
     220             :     }
     221      182040 :     return (heat_rate_in + heat_rate_out) * dz / 2.0;
     222             :   }
     223             :   else
     224             :     return 0.0;
     225             : }
     226             : 
     227             : Real
     228           0 : QuadSubChannel1PhaseProblem::getSubChannelPeripheralDuctWidth(unsigned int i_ch) const
     229             : {
     230           0 :   auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
     231           0 :   if (subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER)
     232             :   {
     233           0 :     auto width = _subchannel_mesh.getPitch();
     234           0 :     if (subch_type == EChannelType::CORNER)
     235           0 :       width += 2.0 * _subchannel_mesh.getSideGap();
     236           0 :     return width;
     237             :   }
     238             :   else
     239           0 :     mooseError("Channel is not a perimetric subchannel ");
     240             : }
     241             : 
     242             : void
     243         402 : QuadSubChannel1PhaseProblem::computeh(int iblock)
     244             : {
     245         402 :   unsigned int last_node = (iblock + 1) * _block_size;
     246         402 :   unsigned int first_node = iblock * _block_size + 1;
     247         402 :   if (iblock == 0)
     248             :   {
     249        7842 :     for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
     250             :     {
     251        7440 :       auto * node = _subchannel_mesh.getChannelNode(i_ch, 0);
     252        7440 :       auto h_out = _fp->h_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
     253        7440 :       if (h_out < 0)
     254             :       {
     255           0 :         mooseError(
     256           0 :             name(), " : Calculation of negative Enthalpy h_out = : ", h_out, " Axial Level= : ", 0);
     257             :       }
     258        7440 :       _h_soln->set(node, h_out);
     259             :     }
     260             :   }
     261             : 
     262         402 :   if (!_implicit_bool)
     263             :   {
     264        4994 :     for (unsigned int iz = first_node; iz < last_node + 1; iz++)
     265             :     {
     266        4810 :       auto dz = _z_grid[iz] - _z_grid[iz - 1];
     267       34300 :       for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
     268             :       {
     269       29490 :         auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
     270       29490 :         auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
     271       29490 :         auto mdot_in = (*_mdot_soln)(node_in);
     272       29490 :         auto h_in = (*_h_soln)(node_in); // J/kg
     273       29490 :         auto volume = dz * (*_S_flow_soln)(node_in);
     274       29490 :         auto mdot_out = (*_mdot_soln)(node_out);
     275       29490 :         auto h_out = 0.0;
     276             :         Real sumWijh = 0.0;
     277             :         Real sumWijPrimeDhij = 0.0;
     278       29490 :         Real added_enthalpy = computeAddedHeatPin(i_ch, iz);
     279             :         // Calculate sum of crossflow into channel i from channels j around i
     280             :         unsigned int counter = 0;
     281      105690 :         for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
     282             :         {
     283       76200 :           auto chans = _subchannel_mesh.getGapChannels(i_gap);
     284             :           unsigned int ii_ch = chans.first;
     285             :           // i is always the smallest and first index in the mapping
     286             :           unsigned int jj_ch = chans.second;
     287       76200 :           auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
     288       76200 :           auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
     289             :           // Define donor enthalpy
     290             :           Real h_star = 0.0;
     291       76200 :           if (_Wij(i_gap, iz) > 0.0)
     292       28008 :             h_star = (*_h_soln)(node_in_i);
     293       48192 :           else if (_Wij(i_gap, iz) < 0.0)
     294       24912 :             h_star = (*_h_soln)(node_in_j);
     295             :           // take care of the sign by applying the map, use donor cell
     296       76200 :           sumWijh += _subchannel_mesh.getCrossflowSign(i_ch, counter) * _Wij(i_gap, iz) * h_star;
     297       76200 :           sumWijPrimeDhij += _WijPrime(i_gap, iz) * (2 * (*_h_soln)(node_in) -
     298       76200 :                                                      (*_h_soln)(node_in_j) - (*_h_soln)(node_in_i));
     299       76200 :           counter++;
     300             :         }
     301       88470 :         h_out = (mdot_in * h_in - sumWijh - sumWijPrimeDhij + added_enthalpy +
     302       29490 :                  _TR * _rho_soln->old(node_out) * _h_soln->old(node_out) * volume / _dt) /
     303       29490 :                 (mdot_out + _TR * (*_rho_soln)(node_out)*volume / _dt);
     304       29490 :         if (h_out < 0)
     305             :         {
     306           0 :           mooseError(name(),
     307             :                      " : Calculation of negative Enthalpy h_out = : ",
     308             :                      h_out,
     309             :                      " Axial Level= : ",
     310             :                      iz);
     311             :         }
     312       29490 :         _h_soln->set(node_out, h_out); // J/kg
     313             :       }
     314             :     }
     315             :   }
     316             :   else
     317             :   {
     318         218 :     LibmeshPetscCall(MatZeroEntries(_hc_time_derivative_mat));
     319         218 :     LibmeshPetscCall(MatZeroEntries(_hc_advective_derivative_mat));
     320         218 :     LibmeshPetscCall(MatZeroEntries(_hc_cross_derivative_mat));
     321         218 :     LibmeshPetscCall(VecZeroEntries(_hc_time_derivative_rhs));
     322         218 :     LibmeshPetscCall(VecZeroEntries(_hc_advective_derivative_rhs));
     323         218 :     LibmeshPetscCall(VecZeroEntries(_hc_cross_derivative_rhs));
     324         218 :     LibmeshPetscCall(VecZeroEntries(_hc_added_heat_rhs));
     325         218 :     LibmeshPetscCall(MatZeroEntries(_hc_sys_h_mat));
     326         218 :     LibmeshPetscCall(VecZeroEntries(_hc_sys_h_rhs));
     327        7368 :     for (unsigned int iz = first_node; iz < last_node + 1; iz++)
     328             :     {
     329        7150 :       auto dz = _z_grid[iz] - _z_grid[iz - 1];
     330        7150 :       auto iz_ind = iz - first_node;
     331      167620 :       for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
     332             :       {
     333      160470 :         auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
     334      160470 :         auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
     335      160470 :         auto S_in = (*_S_flow_soln)(node_in);
     336      160470 :         auto S_out = (*_S_flow_soln)(node_out);
     337      160470 :         auto S_interp = computeInterpolatedValue(S_out, S_in, 0.5);
     338      160470 :         auto volume = dz * S_interp;
     339             : 
     340             :         // interpolation weight coefficient
     341      160470 :         auto alpha = computeInterpolationCoefficients(0.5);
     342             : 
     343             :         /// Time derivative term
     344      160470 :         if (iz == first_node)
     345             :         {
     346             :           PetscScalar value_vec_tt =
     347        5238 :               -1.0 * _TR * alpha * (*_rho_soln)(node_in) * (*_h_soln)(node_in)*volume / _dt;
     348        5238 :           PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
     349        5238 :           LibmeshPetscCall(
     350             :               VecSetValues(_hc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
     351             :         }
     352             :         else
     353             :         {
     354      155232 :           PetscInt row_tt = i_ch + _n_channels * iz_ind;
     355      155232 :           PetscInt col_tt = i_ch + _n_channels * (iz_ind - 1);
     356      155232 :           PetscScalar value_tt = _TR * alpha * (*_rho_soln)(node_in)*volume / _dt;
     357      155232 :           LibmeshPetscCall(MatSetValues(
     358             :               _hc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
     359             :         }
     360             : 
     361             :         // Adding diagonal elements
     362      160470 :         PetscInt row_tt = i_ch + _n_channels * iz_ind;
     363      160470 :         PetscInt col_tt = i_ch + _n_channels * iz_ind;
     364      160470 :         PetscScalar value_tt = _TR * (1.0 - alpha) * (*_rho_soln)(node_out)*volume / _dt;
     365      160470 :         LibmeshPetscCall(MatSetValues(
     366             :             _hc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
     367             : 
     368             :         // Adding RHS elements
     369             :         PetscScalar rho_old_interp =
     370      160470 :             computeInterpolatedValue(_rho_soln->old(node_out), _rho_soln->old(node_in), 0.5);
     371             :         PetscScalar h_old_interp =
     372      160470 :             computeInterpolatedValue(_h_soln->old(node_out), _h_soln->old(node_in), 0.5);
     373      160470 :         PetscScalar value_vec_tt = _TR * rho_old_interp * h_old_interp * volume / _dt;
     374      160470 :         PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
     375      160470 :         LibmeshPetscCall(
     376             :             VecSetValues(_hc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
     377             : 
     378             :         /// Advective derivative term
     379      160470 :         if (iz == first_node)
     380             :         {
     381        5238 :           PetscScalar value_vec_at = (*_mdot_soln)(node_in) * (*_h_soln)(node_in);
     382        5238 :           PetscInt row_vec_at = i_ch + _n_channels * iz_ind;
     383        5238 :           LibmeshPetscCall(VecSetValues(
     384             :               _hc_advective_derivative_rhs, 1, &row_vec_at, &value_vec_at, ADD_VALUES));
     385             :         }
     386             :         else
     387             :         {
     388      155232 :           PetscInt row_at = i_ch + _n_channels * iz_ind;
     389      155232 :           PetscInt col_at = i_ch + _n_channels * (iz_ind - 1);
     390      155232 :           PetscScalar value_at = -1.0 * (*_mdot_soln)(node_in);
     391      155232 :           LibmeshPetscCall(MatSetValues(
     392             :               _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
     393             :         }
     394             : 
     395             :         // Adding diagonal elements
     396      160470 :         PetscInt row_at = i_ch + _n_channels * iz_ind;
     397      160470 :         PetscInt col_at = i_ch + _n_channels * iz_ind;
     398      160470 :         PetscScalar value_at = (*_mdot_soln)(node_out);
     399      160470 :         LibmeshPetscCall(MatSetValues(
     400             :             _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
     401             : 
     402             :         /// Cross derivative term
     403             :         unsigned int counter = 0;
     404             :         unsigned int cross_index = iz; // iz-1;
     405      658470 :         for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
     406             :         {
     407      498000 :           auto chans = _subchannel_mesh.getGapChannels(i_gap);
     408             :           unsigned int ii_ch = chans.first;
     409             :           unsigned int jj_ch = chans.second;
     410      498000 :           auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
     411      498000 :           auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
     412             :           PetscScalar h_star;
     413             :           // figure out donor axial velocity
     414      498000 :           if (_Wij(i_gap, cross_index) > 0.0)
     415             :           {
     416      313174 :             if (iz == first_node)
     417             :             {
     418        9124 :               h_star = (*_h_soln)(node_in_i);
     419       18248 :               PetscScalar value_vec_ct = -1.0 * alpha *
     420        9124 :                                          _subchannel_mesh.getCrossflowSign(i_ch, counter) *
     421        9124 :                                          _Wij(i_gap, cross_index) * h_star;
     422        9124 :               PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
     423        9124 :               LibmeshPetscCall(VecSetValues(
     424             :                   _hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
     425             :             }
     426             :             else
     427             :             {
     428      304050 :               PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
     429      304050 :                                      _Wij(i_gap, cross_index);
     430      304050 :               PetscInt row_ct = i_ch + _n_channels * iz_ind;
     431      304050 :               PetscInt col_ct = ii_ch + _n_channels * (iz_ind - 1);
     432      304050 :               LibmeshPetscCall(MatSetValues(
     433             :                   _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
     434             :             }
     435      313174 :             PetscScalar value_ct = (1.0 - alpha) *
     436      313174 :                                    _subchannel_mesh.getCrossflowSign(i_ch, counter) *
     437      313174 :                                    _Wij(i_gap, cross_index);
     438      313174 :             PetscInt row_ct = i_ch + _n_channels * iz_ind;
     439      313174 :             PetscInt col_ct = ii_ch + _n_channels * iz_ind;
     440      313174 :             LibmeshPetscCall(MatSetValues(
     441             :                 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
     442             :           }
     443      184826 :           else if (_Wij(i_gap, cross_index) < 0.0) // _Wij=0 operations not necessary
     444             :           {
     445      170426 :             if (iz == first_node)
     446             :             {
     447        5996 :               h_star = (*_h_soln)(node_in_j);
     448       11992 :               PetscScalar value_vec_ct = -1.0 * alpha *
     449        5996 :                                          _subchannel_mesh.getCrossflowSign(i_ch, counter) *
     450        5996 :                                          _Wij(i_gap, cross_index) * h_star;
     451        5996 :               PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
     452        5996 :               LibmeshPetscCall(VecSetValues(
     453             :                   _hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
     454             :             }
     455             :             else
     456             :             {
     457      164430 :               PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
     458      164430 :                                      _Wij(i_gap, cross_index);
     459      164430 :               PetscInt row_ct = i_ch + _n_channels * iz_ind;
     460      164430 :               PetscInt col_ct = jj_ch + _n_channels * (iz_ind - 1);
     461      164430 :               LibmeshPetscCall(MatSetValues(
     462             :                   _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
     463             :             }
     464      170426 :             PetscScalar value_ct = (1.0 - alpha) *
     465      170426 :                                    _subchannel_mesh.getCrossflowSign(i_ch, counter) *
     466      170426 :                                    _Wij(i_gap, cross_index);
     467      170426 :             PetscInt row_ct = i_ch + _n_channels * iz_ind;
     468      170426 :             PetscInt col_ct = jj_ch + _n_channels * iz_ind;
     469      170426 :             LibmeshPetscCall(MatSetValues(
     470             :                 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
     471             :           }
     472             : 
     473             :           // Turbulent cross flows
     474      498000 :           if (iz == first_node)
     475             :           {
     476             :             PetscScalar value_vec_ct =
     477       16560 :                 -2.0 * alpha * (*_h_soln)(node_in)*_WijPrime(i_gap, cross_index);
     478       16560 :             value_vec_ct += alpha * (*_h_soln)(node_in_j)*_WijPrime(i_gap, cross_index);
     479       16560 :             value_vec_ct += alpha * (*_h_soln)(node_in_i)*_WijPrime(i_gap, cross_index);
     480       16560 :             PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
     481       16560 :             LibmeshPetscCall(
     482             :                 VecSetValues(_hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
     483             :           }
     484             :           else
     485             :           {
     486      481440 :             PetscScalar value_center_ct = 2.0 * alpha * _WijPrime(i_gap, cross_index);
     487      481440 :             PetscInt row_ct = i_ch + _n_channels * iz_ind;
     488      481440 :             PetscInt col_ct = i_ch + _n_channels * (iz_ind - 1);
     489      481440 :             LibmeshPetscCall(MatSetValues(
     490             :                 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
     491             : 
     492      481440 :             PetscScalar value_left_ct = -1.0 * alpha * _WijPrime(i_gap, cross_index);
     493      481440 :             row_ct = i_ch + _n_channels * iz_ind;
     494      481440 :             col_ct = jj_ch + _n_channels * (iz_ind - 1);
     495      481440 :             LibmeshPetscCall(MatSetValues(
     496             :                 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
     497             : 
     498      481440 :             PetscScalar value_right_ct = -1.0 * alpha * _WijPrime(i_gap, cross_index);
     499      481440 :             row_ct = i_ch + _n_channels * iz_ind;
     500      481440 :             col_ct = ii_ch + _n_channels * (iz_ind - 1);
     501      481440 :             LibmeshPetscCall(MatSetValues(
     502             :                 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
     503             :           }
     504      498000 :           PetscScalar value_center_ct = 2.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
     505      498000 :           PetscInt row_ct = i_ch + _n_channels * iz_ind;
     506      498000 :           PetscInt col_ct = i_ch + _n_channels * iz_ind;
     507      498000 :           LibmeshPetscCall(MatSetValues(
     508             :               _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
     509             : 
     510      498000 :           PetscScalar value_left_ct = -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
     511      498000 :           row_ct = i_ch + _n_channels * iz_ind;
     512      498000 :           col_ct = jj_ch + _n_channels * iz_ind;
     513      498000 :           LibmeshPetscCall(MatSetValues(
     514             :               _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
     515             : 
     516      498000 :           PetscScalar value_right_ct = -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
     517      498000 :           row_ct = i_ch + _n_channels * iz_ind;
     518      498000 :           col_ct = ii_ch + _n_channels * iz_ind;
     519      498000 :           LibmeshPetscCall(MatSetValues(
     520             :               _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
     521      498000 :           counter++;
     522             :         }
     523             : 
     524             :         /// Added heat enthalpy
     525      160470 :         PetscScalar added_enthalpy = computeAddedHeatPin(i_ch, iz);
     526      160470 :         PetscInt row_vec_ht = i_ch + _n_channels * iz_ind;
     527      160470 :         LibmeshPetscCall(
     528             :             VecSetValues(_hc_added_heat_rhs, 1, &row_vec_ht, &added_enthalpy, ADD_VALUES));
     529             :       }
     530             :     }
     531             :     /// Assembling system
     532         218 :     LibmeshPetscCall(MatAssemblyBegin(_hc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
     533         218 :     LibmeshPetscCall(MatAssemblyEnd(_hc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
     534         218 :     LibmeshPetscCall(MatAssemblyBegin(_hc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
     535         218 :     LibmeshPetscCall(MatAssemblyEnd(_hc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
     536         218 :     LibmeshPetscCall(MatAssemblyBegin(_hc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
     537         218 :     LibmeshPetscCall(MatAssemblyEnd(_hc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
     538         218 :     LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     539         218 :     LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     540             :     // Matrix
     541             : #if !PETSC_VERSION_LESS_THAN(3, 15, 0)
     542         218 :     LibmeshPetscCall(MatAXPY(_hc_sys_h_mat, 1.0, _hc_time_derivative_mat, UNKNOWN_NONZERO_PATTERN));
     543         218 :     LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     544         218 :     LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     545         218 :     LibmeshPetscCall(
     546             :         MatAXPY(_hc_sys_h_mat, 1.0, _hc_advective_derivative_mat, UNKNOWN_NONZERO_PATTERN));
     547         218 :     LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     548         218 :     LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     549         218 :     LibmeshPetscCall(
     550             :         MatAXPY(_hc_sys_h_mat, 1.0, _hc_cross_derivative_mat, UNKNOWN_NONZERO_PATTERN));
     551             : #else
     552             :     LibmeshPetscCall(
     553             :         MatAXPY(_hc_sys_h_mat, 1.0, _hc_time_derivative_mat, DIFFERENT_NONZERO_PATTERN));
     554             :     LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     555             :     LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     556             :     LibmeshPetscCall(
     557             :         MatAXPY(_hc_sys_h_mat, 1.0, _hc_advective_derivative_mat, DIFFERENT_NONZERO_PATTERN));
     558             :     LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     559             :     LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     560             :     LibmeshPetscCall(
     561             :         MatAXPY(_hc_sys_h_mat, 1.0, _hc_cross_derivative_mat, DIFFERENT_NONZERO_PATTERN));
     562             : #endif
     563         218 :     LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     564         218 :     LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
     565         218 :     if (_verbose_subchannel)
     566          98 :       _console << "Block: " << iblock << " - Enthalpy conservation matrix assembled" << std::endl;
     567             :     // RHS
     568         218 :     LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_time_derivative_rhs));
     569         218 :     LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_advective_derivative_rhs));
     570         218 :     LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_cross_derivative_rhs));
     571         218 :     LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_added_heat_rhs));
     572             : 
     573             :     // Use system to solve for and populate enthalpy
     574         218 :     LibmeshPetscCall(this->solveAndPopulateEnthalpy(
     575             :         _hc_sys_h_mat, _hc_sys_h_rhs, first_node, last_node, "h_sys_"));
     576             :   }
     577         402 : }

Generated by: LCOV version 1.14