LCOV - code coverage report
Current view: top level - src/userobjects - NekSpatialBinUserObject.C (source / functions) Hit Total Coverage
Test: neams-th-coe/cardinal: faa471 Lines: 145 148 98.0 %
Date: 2026-08-05 13:00:46 Functions: 15 16 93.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /********************************************************************/
       2             : /*                  SOFTWARE COPYRIGHT NOTIFICATION                 */
       3             : /*                             Cardinal                             */
       4             : /*                                                                  */
       5             : /*                  (c) 2021 UChicago Argonne, LLC                  */
       6             : /*                        ALL RIGHTS RESERVED                       */
       7             : /*                                                                  */
       8             : /*                 Prepared by UChicago Argonne, LLC                */
       9             : /*               Under Contract No. DE-AC02-06CH11357               */
      10             : /*                With the U. S. Department of Energy               */
      11             : /*                                                                  */
      12             : /*             Prepared by Battelle Energy Alliance, LLC            */
      13             : /*               Under Contract No. DE-AC07-05ID14517               */
      14             : /*                With the U. S. Department of Energy               */
      15             : /*                                                                  */
      16             : /*                 See LICENSE for full restrictions                */
      17             : /********************************************************************/
      18             : 
      19             : #ifdef ENABLE_NEK_COUPLING
      20             : 
      21             : #include "NekSpatialBinUserObject.h"
      22             : #include "NekInterface.h"
      23             : #include "CardinalUtils.h"
      24             : 
      25             : InputParameters
      26         432 : NekSpatialBinUserObject::validParams()
      27             : {
      28         432 :   InputParameters params = GeneralUserObject::validParams();
      29         432 :   params += NekBase::validParams();
      30         432 :   params += NekFieldInterface::validParams();
      31         864 :   params.addRequiredParam<std::vector<UserObjectName>>(
      32             :       "bins", "Userobjects providing a spatial bin given a point");
      33         864 :   params.addParam<unsigned int>(
      34             :       "interval",
      35         864 :       1,
      36             :       "Frequency (in number of time steps) with which to execute this user object; user objects "
      37             :       "can be expensive and not necessary to evaluate on every single time step. NOTE: you "
      38             :       "probably want to match with 'time_step_interval' in the Output");
      39         864 :   params.addParam<bool>(
      40             :       "map_space_by_qp",
      41         864 :       false,
      42             :       "Whether to map the NekRS spatial domain to a bin according to the element centroids (true) "
      43             :       "or quadrature point locations (false).");
      44         864 :   params.addParam<bool>(
      45             :       "check_zero_contributions",
      46         864 :       true,
      47             :       "Whether to throw an error if no GLL points/element centroids in the NekRS mesh map to a "
      48             :       "spatial bin; this "
      49             :       "can be used to ensure that the bins are sufficiently big to get at least one contributing "
      50             :       "point from the NekRS mesh.");
      51         432 :   return params;
      52           0 : }
      53             : 
      54         219 : NekSpatialBinUserObject::NekSpatialBinUserObject(const InputParameters & parameters)
      55             :   : GeneralUserObject(parameters),
      56             :     NekBase(this, parameters),
      57             :     NekFieldInterface(this, parameters, true /* allow normal */),
      58         218 :     _interval(getParam<unsigned int>("interval")),
      59         436 :     _bin_names(getParam<std::vector<UserObjectName>>("bins")),
      60         436 :     _map_space_by_qp(getParam<bool>("map_space_by_qp")),
      61         436 :     _check_zero_contributions(getParam<bool>("check_zero_contributions")),
      62         218 :     _bin_values(nullptr),
      63         218 :     _bin_values_x(nullptr),
      64         218 :     _bin_values_y(nullptr),
      65         218 :     _bin_values_z(nullptr),
      66         218 :     _bin_volumes(nullptr),
      67         218 :     _bin_counts(nullptr),
      68         218 :     _bin_partial_values(nullptr),
      69         219 :     _bin_partial_counts(nullptr)
      70             : {
      71         218 :   _fixed_mesh = !nekrs::hasMovingMesh();
      72             : 
      73         218 :   if (_bin_names.size() == 0)
      74           0 :     paramError("bins", "Length of vector must be greater than zero!");
      75             : 
      76         569 :   for (auto & b : _bin_names)
      77             :   {
      78             :     // check that the user object exists and that it's the right type
      79         352 :     if (!hasUserObjectByName<SpatialBinUserObject>(b))
      80           2 :       mooseError("Bin user object with name '" + b +
      81             :                  "' either does not exist or is of the wrong type. This user object must inherit "
      82             :                  "from SpatialBinUserObject.\n\n"
      83             :                  "Volume options: HexagonalSubchannelBin, LayeredBin, RadialBin\n"
      84             :                  "Side options: HexagonalSubchannelGapBin, LayeredGapBin");
      85             : 
      86         351 :     _bins.push_back(&getUserObjectByName<SpatialBinUserObject>(b));
      87             :   }
      88             : 
      89         217 :   _n_bins = num_bins();
      90             : 
      91         217 :   _bin_values = (double *)calloc(_n_bins, sizeof(double));
      92         217 :   _bin_volumes = (double *)calloc(_n_bins, sizeof(double));
      93         217 :   _bin_partial_values = (double *)calloc(_n_bins, sizeof(double));
      94         217 :   _bin_counts = (int *)calloc(_n_bins, sizeof(int));
      95         217 :   _bin_partial_counts = (int *)calloc(_n_bins, sizeof(int));
      96             : 
      97         217 :   if (_field == field::velocity_component)
      98             :   {
      99          17 :     _bin_values_x = (double *)calloc(_n_bins, sizeof(double));
     100          17 :     _bin_values_y = (double *)calloc(_n_bins, sizeof(double));
     101          17 :     _bin_values_z = (double *)calloc(_n_bins, sizeof(double));
     102             :   }
     103             : 
     104         217 :   _has_direction = {false, false, false};
     105         217 :   _bin_providing_direction.resize(3);
     106         566 :   for (unsigned int b = 0; b < _bins.size(); ++b)
     107             :   {
     108             :     const auto & bin = _bins[b];
     109             : 
     110             :     // directions provided by this bin
     111         351 :     auto bin_directions = bin->directions();
     112             : 
     113         820 :     for (const auto & d : bin_directions)
     114             :     {
     115         471 :       if (_has_direction[d])
     116             :       {
     117           2 :         const auto & bin_providing_d = _bins[_bin_providing_direction[d]];
     118           2 :         mooseError("Cannot combine multiple distributions in the same coordinate direction!\n"
     119           2 :                    "Bin '" +
     120           4 :                    bin->name() + "' conflicts with bin '" + bin_providing_d->name() + "'.");
     121             :       }
     122             :       else
     123             :       {
     124             :         _has_direction[d] = true;
     125         469 :         _bin_providing_direction[d] = b;
     126             :       }
     127             :     }
     128         349 :   }
     129             : 
     130             :   // initialize all points to (0, 0, 0)
     131       44857 :   for (unsigned int i = 0; i < _n_bins; ++i)
     132       44642 :     _points.push_back(Point(0.0, 0.0, 0.0));
     133             : 
     134             :   // we will at most have 3 separate distributions
     135         215 :   if (_bins.size() == 1)
     136         102 :     computePoints1D();
     137         113 :   else if (_bins.size() == 2)
     138          96 :     computePoints2D();
     139             :   else
     140          17 :     computePoints3D();
     141             : 
     142             :   // with a user-specified direction, the direction for each bin is the same
     143         215 :   if (_field == field::velocity_component && _velocity_component == component::user)
     144        1016 :     for (unsigned int i = 0; i < _n_bins; ++i)
     145        1008 :       _velocity_bin_directions.push_back(_velocity_direction);
     146         215 : }
     147             : 
     148         208 : NekSpatialBinUserObject::~NekSpatialBinUserObject()
     149             : {
     150         208 :   freePointer(_bin_values);
     151         208 :   freePointer(_bin_volumes);
     152         208 :   freePointer(_bin_counts);
     153         208 :   freePointer(_bin_partial_values);
     154         208 :   freePointer(_bin_partial_counts);
     155             : 
     156         208 :   freePointer(_bin_values_x);
     157         208 :   freePointer(_bin_values_y);
     158         208 :   freePointer(_bin_values_z);
     159         416 : }
     160             : 
     161             : void
     162        1432 : NekSpatialBinUserObject::execute()
     163             : {
     164        1432 :   if (_fe_problem.timeStep() % _interval == 0)
     165         216 :     executeUserObject();
     166        1432 : }
     167             : 
     168             : Point
     169   126587040 : NekSpatialBinUserObject::nekPoint(const int & local_elem_id, const int & local_node_id) const
     170             : {
     171   126587040 :   if (_map_space_by_qp)
     172    99589536 :     return nekrs::gllPoint(local_elem_id, local_node_id);
     173             :   else
     174    26997504 :     return nekrs::centroid(local_elem_id);
     175             : }
     176             : 
     177             : void
     178         459 : NekSpatialBinUserObject::resetPartialStorage()
     179             : {
     180       59638 :   for (unsigned int i = 0; i < _n_bins; ++i)
     181             :   {
     182       59179 :     _bin_partial_values[i] = 0.0;
     183       59179 :     _bin_partial_counts[i] = 0;
     184             :   }
     185         459 : }
     186             : 
     187             : void
     188         211 : NekSpatialBinUserObject::computeBinVolumes()
     189             : {
     190         211 :   getBinVolumes();
     191             : 
     192         211 :   if (_check_zero_contributions)
     193             :   {
     194       11327 :     for (unsigned int i = 0; i < _n_bins; ++i)
     195             :     {
     196       11142 :       if (_bin_counts[i] == 0)
     197             :       {
     198           3 :         std::string map = _map_space_by_qp ? "GLL points" : "element centroids";
     199           4 :         mooseError(
     200           4 :             "Failed to map any " + map + " to bin " + Moose::stringify(i) +
     201             :             "!\n\n"
     202             :             "This can happen if the bins are much finer than the NekRS mesh or if the bins are "
     203             :             "defined in a way that results in bins entirely outside the NekRS domain. You can turn "
     204             :             "this error off by setting 'check_zero_contributions = false' (at your own risk!).");
     205             :       }
     206             :     }
     207             :   }
     208         209 : }
     209             : 
     210             : Real
     211     1671078 : NekSpatialBinUserObject::spatialValue(const Point & p) const
     212             : {
     213     1671078 :   return _bin_values[bin(p)];
     214             : }
     215             : 
     216             : const std::vector<unsigned int>
     217       97088 : NekSpatialBinUserObject::unrolledBin(const unsigned int & total_bin_index) const
     218             : {
     219             :   std::vector<unsigned int> local_bins;
     220       97088 :   local_bins.resize(_bins.size());
     221             : 
     222       97088 :   int running_index = total_bin_index;
     223      269800 :   for (int i = _bins.size() - 1; i >= 0; --i)
     224             :   {
     225      172712 :     local_bins[i] = running_index % _bins[i]->num_bins();
     226      172712 :     running_index -= local_bins[i];
     227      172712 :     running_index /= _bins[i]->num_bins();
     228             :   }
     229             : 
     230       97088 :   return local_bins;
     231           0 : }
     232             : 
     233             : const unsigned int
     234    59791362 : NekSpatialBinUserObject::bin(const Point & p) const
     235             : {
     236             :   // get the indices into each of the individual bin objects
     237             :   std::vector<unsigned int> indices;
     238   169139690 :   for (const auto & b : _bins)
     239   109348328 :     indices.push_back(b->bin(p));
     240             : 
     241             :   // convert to a total index into the multidimensional bin union
     242    59791362 :   unsigned int index = indices[0];
     243   109348328 :   for (unsigned int i = 1; i < _bins.size(); ++i)
     244    49556966 :     index = index * _bins[i]->num_bins() + indices[i];
     245             : 
     246    59791362 :   return index;
     247    59791362 : }
     248             : 
     249             : const unsigned int
     250        8361 : NekSpatialBinUserObject::num_bins() const
     251             : {
     252             :   unsigned int num_bins = 1;
     253       25020 :   for (const auto & b : _bins)
     254       16659 :     num_bins *= b->num_bins();
     255             : 
     256        8361 :   return num_bins;
     257             : }
     258             : 
     259             : void
     260         102 : NekSpatialBinUserObject::computePoints1D()
     261             : {
     262         574 :   for (unsigned int i = 0; i < _bins[0]->num_bins(); ++i)
     263             :   {
     264         472 :     std::vector<unsigned int> indices = {i};
     265         472 :     fillCoordinates(indices, _points[i]);
     266         472 :   }
     267         102 : }
     268             : 
     269             : void
     270          96 : NekSpatialBinUserObject::computePoints2D()
     271             : {
     272             :   int p = 0;
     273        1778 :   for (unsigned int i = 0; i < _bins[0]->num_bins(); ++i)
     274             :   {
     275       35796 :     for (unsigned int j = 0; j < _bins[1]->num_bins(); ++j, ++p)
     276             :     {
     277       34114 :       std::vector<unsigned int> indices = {i, j};
     278       34114 :       fillCoordinates(indices, _points[p]);
     279       34114 :     }
     280             :   }
     281          96 : }
     282             : 
     283             : void
     284          17 : NekSpatialBinUserObject::computePoints3D()
     285             : {
     286             :   int p = 0;
     287          64 :   for (unsigned int i = 0; i < _bins[0]->num_bins(); ++i)
     288             :   {
     289         180 :     for (unsigned int j = 0; j < _bins[1]->num_bins(); ++j)
     290             :     {
     291       10189 :       for (unsigned int k = 0; k < _bins[2]->num_bins(); ++k, ++p)
     292             :       {
     293       10056 :         std::vector<unsigned int> indices = {i, j, k};
     294       10056 :         fillCoordinates(indices, _points[p]);
     295       10056 :       }
     296             :     }
     297             :   }
     298          17 : }
     299             : 
     300             : void
     301       44642 : NekSpatialBinUserObject::fillCoordinates(const std::vector<unsigned int> & indices, Point & p) const
     302             : {
     303      143510 :   for (unsigned int b = 0; b < _bins.size(); ++b)
     304             :   {
     305             :     const auto & bin = _bins[b];
     306       98868 :     const auto & centers = bin->getBinCenters();
     307             : 
     308      232118 :     for (const auto & d : bin->directions())
     309      232118 :       p(d) = centers[indices[b]](d);
     310             :   }
     311       44642 : }
     312             : 
     313             : #endif

Generated by: LCOV version 1.14