LCOV - code coverage report
Current view: top level - src/userobjects - NekSpatialBinUserObject.C (source / functions) Hit Total Coverage
Test: neams-th-coe/cardinal: be601f Lines: 143 146 97.9 %
Date: 2025-07-15 20:50:38 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         489 : NekSpatialBinUserObject::validParams()
      27             : {
      28         489 :   InputParameters params = GeneralUserObject::validParams();
      29         489 :   params += NekBase::validParams();
      30         489 :   params += NekFieldInterface::validParams();
      31         978 :   params.addRequiredParam<std::vector<UserObjectName>>(
      32             :       "bins", "Userobjects providing a spatial bin given a point");
      33         978 :   params.addParam<unsigned int>(
      34             :       "interval",
      35         978 :       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         978 :   params.addParam<bool>(
      40             :       "map_space_by_qp",
      41         978 :       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         978 :   params.addParam<bool>(
      45             :       "check_zero_contributions",
      46         978 :       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         489 :   return params;
      52           0 : }
      53             : 
      54         248 : NekSpatialBinUserObject::NekSpatialBinUserObject(const InputParameters & parameters)
      55             :   : GeneralUserObject(parameters),
      56             :     NekBase(this, parameters),
      57             :     NekFieldInterface(this, parameters, true /* allow normal */),
      58         247 :     _interval(getParam<unsigned int>("interval")),
      59         494 :     _bin_names(getParam<std::vector<UserObjectName>>("bins")),
      60         494 :     _map_space_by_qp(getParam<bool>("map_space_by_qp")),
      61         494 :     _check_zero_contributions(getParam<bool>("check_zero_contributions")),
      62         247 :     _bin_values(nullptr),
      63         247 :     _bin_values_x(nullptr),
      64         247 :     _bin_values_y(nullptr),
      65         247 :     _bin_values_z(nullptr),
      66         247 :     _bin_volumes(nullptr),
      67         247 :     _bin_counts(nullptr),
      68         247 :     _bin_partial_values(nullptr),
      69         495 :     _bin_partial_counts(nullptr)
      70             : {
      71         247 :   _fixed_mesh = !nekrs::hasMovingMesh();
      72             : 
      73         247 :   if (_bin_names.size() == 0)
      74           0 :     paramError("bins", "Length of vector must be greater than zero!");
      75             : 
      76         652 :   for (auto & b : _bin_names)
      77             :   {
      78             :     // first check that the user object exists
      79         407 :     if (!hasUserObjectByName<UserObject>(b))
      80           2 :       mooseError("Bin user object with name '" + b +
      81             :                  "' not found in problem. The user objects "
      82           1 :                  "in 'bins' must be listed before the '" +
      83           0 :                  name() + "' user object.");
      84             : 
      85             :     // then check that it's the right type
      86         406 :     if (!hasUserObjectByName<SpatialBinUserObject>(b))
      87           2 :       mooseError("Bin user object with name '" + b +
      88             :                  "' must inherit from SpatialBinUserObject.\n\n"
      89             :                  "Volume options: HexagonalSubchannelBin, LayeredBin, RadialBin\n"
      90             :                  "Side options: HexagonalSubchannelGapBin, LayeredGapBin");
      91             : 
      92         405 :     _bins.push_back(&getUserObjectByName<SpatialBinUserObject>(b));
      93             :   }
      94             : 
      95         245 :   _n_bins = num_bins();
      96             : 
      97         245 :   _bin_values = (double *)calloc(_n_bins, sizeof(double));
      98         245 :   _bin_volumes = (double *)calloc(_n_bins, sizeof(double));
      99         245 :   _bin_partial_values = (double *)calloc(_n_bins, sizeof(double));
     100         245 :   _bin_counts = (int *)calloc(_n_bins, sizeof(int));
     101         245 :   _bin_partial_counts = (int *)calloc(_n_bins, sizeof(int));
     102             : 
     103         245 :   if (_field == field::velocity_component)
     104             :   {
     105          25 :     _bin_values_x = (double *)calloc(_n_bins, sizeof(double));
     106          25 :     _bin_values_y = (double *)calloc(_n_bins, sizeof(double));
     107          25 :     _bin_values_z = (double *)calloc(_n_bins, sizeof(double));
     108             :   }
     109             : 
     110         245 :   _has_direction = {false, false, false};
     111         245 :   _bin_providing_direction.resize(3);
     112         648 :   for (unsigned int b = 0; b < _bins.size(); ++b)
     113             :   {
     114             :     const auto & bin = _bins[b];
     115             : 
     116             :     // directions provided by this bin
     117         405 :     auto bin_directions = bin->directions();
     118             : 
     119         948 :     for (const auto & d : bin_directions)
     120             :     {
     121         545 :       if (_has_direction[d])
     122             :       {
     123           2 :         const auto & bin_providing_d = _bins[_bin_providing_direction[d]];
     124           2 :         mooseError("Cannot combine multiple distributions in the same coordinate direction!\n"
     125           2 :                    "Bin '" +
     126           4 :                    bin->name() + "' conflicts with bin '" + bin_providing_d->name() + "'.");
     127             :       }
     128             :       else
     129             :       {
     130             :         _has_direction[d] = true;
     131         543 :         _bin_providing_direction[d] = b;
     132             :       }
     133             :     }
     134             :   }
     135             : 
     136             :   // initialize all points to (0, 0, 0)
     137       50361 :   for (unsigned int i = 0; i < _n_bins; ++i)
     138       50118 :     _points.push_back(Point(0.0, 0.0, 0.0));
     139             : 
     140             :   // we will at most have 3 separate distributions
     141         243 :   if (_bins.size() == 1)
     142         104 :     computePoints1D();
     143         139 :   else if (_bins.size() == 2)
     144         122 :     computePoints2D();
     145             :   else
     146          17 :     computePoints3D();
     147             : 
     148             :   // with a user-specified direction, the direction for each bin is the same
     149         243 :   if (_field == field::velocity_component && _velocity_component == component::user)
     150        1016 :     for (unsigned int i = 0; i < _n_bins; ++i)
     151        1008 :       _velocity_bin_directions.push_back(_velocity_direction);
     152         243 : }
     153             : 
     154         236 : NekSpatialBinUserObject::~NekSpatialBinUserObject()
     155             : {
     156         236 :   freePointer(_bin_values);
     157         236 :   freePointer(_bin_volumes);
     158         236 :   freePointer(_bin_counts);
     159         236 :   freePointer(_bin_partial_values);
     160         236 :   freePointer(_bin_partial_counts);
     161             : 
     162         236 :   freePointer(_bin_values_x);
     163         236 :   freePointer(_bin_values_y);
     164         236 :   freePointer(_bin_values_z);
     165         236 : }
     166             : 
     167             : void
     168        1964 : NekSpatialBinUserObject::execute()
     169             : {
     170        1964 :   if (_fe_problem.timeStep() % _interval == 0)
     171         348 :     executeUserObject();
     172        1964 : }
     173             : 
     174             : Point
     175   245866656 : NekSpatialBinUserObject::nekPoint(const int & local_elem_id, const int & local_node_id) const
     176             : {
     177   245866656 :   if (_map_space_by_qp)
     178   218869152 :     return nekrs::gllPoint(local_elem_id, local_node_id);
     179             :   else
     180    26997504 :     return nekrs::centroid(local_elem_id);
     181             : }
     182             : 
     183             : void
     184         635 : NekSpatialBinUserObject::resetPartialStorage()
     185             : {
     186       90894 :   for (unsigned int i = 0; i < _n_bins; ++i)
     187             :   {
     188       90259 :     _bin_partial_values[i] = 0.0;
     189       90259 :     _bin_partial_counts[i] = 0;
     190             :   }
     191         635 : }
     192             : 
     193             : void
     194         239 : NekSpatialBinUserObject::computeBinVolumes()
     195             : {
     196         239 :   getBinVolumes();
     197             : 
     198         239 :   if (_check_zero_contributions)
     199             :   {
     200       17023 :     for (unsigned int i = 0; i < _n_bins; ++i)
     201             :     {
     202       16798 :       if (_bin_counts[i] == 0)
     203             :       {
     204           3 :         std::string map = _map_space_by_qp ? "GLL points" : "element centroids";
     205           4 :         mooseError(
     206           4 :             "Failed to map any " + map + " to bin " + Moose::stringify(i) +
     207             :             "!\n\n"
     208             :             "This can happen if the bins are much finer than the NekRS mesh or if the bins are "
     209             :             "defined in a way that results in bins entirely outside the NekRS domain. You can turn "
     210             :             "this error off by setting 'check_zero_contributions = false' (at your own risk!).");
     211             :       }
     212             :     }
     213             :   }
     214         237 : }
     215             : 
     216             : Real
     217     2136608 : NekSpatialBinUserObject::spatialValue(const Point & p) const
     218             : {
     219     2136608 :   return _bin_values[bin(p)];
     220             : }
     221             : 
     222             : const std::vector<unsigned int>
     223      127448 : NekSpatialBinUserObject::unrolledBin(const unsigned int & total_bin_index) const
     224             : {
     225             :   std::vector<unsigned int> local_bins;
     226      127448 :   local_bins.resize(_bins.size());
     227             : 
     228      127448 :   int running_index = total_bin_index;
     229      360880 :   for (int i = _bins.size() - 1; i >= 0; --i)
     230             :   {
     231      233432 :     local_bins[i] = running_index % _bins[i]->num_bins();
     232      233432 :     running_index -= local_bins[i];
     233      233432 :     running_index /= _bins[i]->num_bins();
     234             :   }
     235             : 
     236      127448 :   return local_bins;
     237             : }
     238             : 
     239             : const unsigned int
     240   159219476 : NekSpatialBinUserObject::bin(const Point & p) const
     241             : {
     242             :   // get the indices into each of the individual bin objects
     243             :   std::vector<unsigned int> indices;
     244   465941188 :   for (const auto & b : _bins)
     245   306721712 :     indices.push_back(b->bin(p));
     246             : 
     247             :   // convert to a total index into the multidimensional bin union
     248   159219476 :   unsigned int index = indices[0];
     249   306721712 :   for (unsigned int i = 1; i < _bins.size(); ++i)
     250   147502236 :     index = index * _bins[i]->num_bins() + indices[i];
     251             : 
     252   159219476 :   return index;
     253             : }
     254             : 
     255             : const unsigned int
     256       32981 : NekSpatialBinUserObject::num_bins() const
     257             : {
     258             :   unsigned int num_bins = 1;
     259       98638 :   for (const auto & b : _bins)
     260       65657 :     num_bins *= b->num_bins();
     261             : 
     262       32981 :   return num_bins;
     263             : }
     264             : 
     265             : void
     266         104 : NekSpatialBinUserObject::computePoints1D()
     267             : {
     268         586 :   for (unsigned int i = 0; i < _bins[0]->num_bins(); ++i)
     269             :   {
     270         482 :     std::vector<unsigned int> indices = {i};
     271         482 :     fillCoordinates(indices, _points[i]);
     272             :   }
     273         104 : }
     274             : 
     275             : void
     276         122 : NekSpatialBinUserObject::computePoints2D()
     277             : {
     278             :   int p = 0;
     279        2398 :   for (unsigned int i = 0; i < _bins[0]->num_bins(); ++i)
     280             :   {
     281       41856 :     for (unsigned int j = 0; j < _bins[1]->num_bins(); ++j, ++p)
     282             :     {
     283       39580 :       std::vector<unsigned int> indices = {i, j};
     284       39580 :       fillCoordinates(indices, _points[p]);
     285             :     }
     286             :   }
     287         122 : }
     288             : 
     289             : void
     290          17 : NekSpatialBinUserObject::computePoints3D()
     291             : {
     292             :   int p = 0;
     293          64 :   for (unsigned int i = 0; i < _bins[0]->num_bins(); ++i)
     294             :   {
     295         180 :     for (unsigned int j = 0; j < _bins[1]->num_bins(); ++j)
     296             :     {
     297       10189 :       for (unsigned int k = 0; k < _bins[2]->num_bins(); ++k, ++p)
     298             :       {
     299       10056 :         std::vector<unsigned int> indices = {i, j, k};
     300       10056 :         fillCoordinates(indices, _points[p]);
     301             :       }
     302             :     }
     303             :   }
     304          17 : }
     305             : 
     306             : void
     307       50118 : NekSpatialBinUserObject::fillCoordinates(const std::vector<unsigned int> & indices, Point & p) const
     308             : {
     309      159928 :   for (unsigned int b = 0; b < _bins.size(); ++b)
     310             :   {
     311             :     const auto & bin = _bins[b];
     312      109810 :     const auto & centers = bin->getBinCenters();
     313             : 
     314      259438 :     for (const auto & d : bin->directions())
     315      149628 :       p(d) = centers[indices[b]](d);
     316             :   }
     317       50118 : }
     318             : 
     319             : #endif

Generated by: LCOV version 1.14