diff --git a/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAvMassVelocity.cpp b/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAvMassVelocity.cpp new file mode 100644 index 00000000..580d5ae0 --- /dev/null +++ b/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAvMassVelocity.cpp @@ -0,0 +1,20 @@ +#include "PostprocessOperationAvMassVelocity.hpp" + +pFlow::PostprocessOperationAvMassVelocity::PostprocessOperationAvMassVelocity +( + const dictionary &opDict, + const regionPoints ®Points, + fieldsDataBase &fieldsDB +) +: + PostprocessOperationAverage + ( + opDict, + opDict.getValOrSet("velocityName", "velocity"), + opDict.getValOrSet("massName", "mass"), + "all", + regPoints, + fieldsDB + ) +{ +} \ No newline at end of file diff --git a/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAvMassVelocity.hpp b/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAvMassVelocity.hpp new file mode 100644 index 00000000..16b029f1 --- /dev/null +++ b/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAvMassVelocity.hpp @@ -0,0 +1,173 @@ +/*------------------------------- phasicFlow --------------------------------- + O C enter of + O O E ngineering and + O O M ultiscale modeling of + OOOOOOO F luid flow +------------------------------------------------------------------------------ + Copyright (C): www.cemf.ir + email: hamid.r.norouzi AT gmail.com +------------------------------------------------------------------------------ +Licence: + This file is part of phasicFlow code. It is a free software for simulating + granular and multiphase flows. You can redistribute it and/or modify it under + the terms of GNU General Public License v3 or any other later versions. + + phasicFlow is distributed to help others in their research in the field of + granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +-----------------------------------------------------------------------------*/ + +#ifndef __PostprocessOperationAvMassVelocity_hpp__ +#define __PostprocessOperationAvMassVelocity_hpp__ + +/*! + * @class PostprocessOperationAvMassVelocity + * @brief A class for averaging field values within specified regions during post-processing. + * + * @details + * The PostprocessOperationAvMassVelocity class is a specialized post-processing operation that + * calculates the average of field values within specified regions. It inherits from the + * postprocessOperation base class and implements a weighted averaging operation that + * can be applied to scalar (real), vector (realx3), and tensor (realx4) fields. + * + * The average operation follows the mathematical formula: + * \f[ + * \text{result} = \frac{\sum_{j \in \text{includeMask}} w_j \cdot \phi_j \cdot \text{field}_j} + * {\sum_{i \in \text{processRegion}} w_i \cdot \phi_i} + * \f] + * + * Where: + * - \f$ i \f$ represents all particles within the specified processing region + * - \f$ j \f$ belongs to a subset of \f$ i \f$ based on an includeMask + * - \f$ w_i \f$ is the weight factor for particle \f$ i \f$ + * - \f$ \phi_i \f$ is the value from the phi field for particle \f$ i \f$ + * - \f$ \text{field}_j \f$ is the value from the target field for particle \f$ j \f$ + * + * The calculation can optionally be divided by the region volume (when divideByVolume is set to yes), + * which allows calculating normalized averages: + * \f[ + * \text{result} = \frac{1}{V_{\text{region}}} \frac{\sum_{j \in \text{includeMask}} w_j \cdot \phi_j \cdot \text{field}_j} + * {\sum_{i \in \text{processRegion}} w_i \cdot \phi_i} + * \f] + * + * The averaging can be further filtered using an includeMask to selectively include only + * specific particles that satisfy certain criteria. + * + * This class supports the following field types: + * - real (scalar values) + * - realx3 (vector values) + * - realx4 (tensor values) + * + * @section usage Usage Example + * Below is a sample dictionary showing how to configure and use this class: + * + * ``` + * processMethod arithmetic; // method of performing the sum (arithmetic, uniformDistribution, GaussianDistribution) + * processRegion sphere; // type of region on which processing is performed + * + * sphereInfo + * { + * radius 0.01; + * center (-0.08 -0.08 0.015); + * } + * + * timeControl default; + * + * /// all the post process operations to be done + * operations + * ( + * // computes the arithmetic mean of particle velocity + * averageVel + * { + * function average; + * field velocity; + * dividedByVolume no; // default is no + * threshold 3; // default is 1 + * includeMask all; // include all particles in the calculation + * } + * + * // computes the fraction of par1 in the region + * par1Fraction + * { + * function average; + * field one; // the "one" field is special - all members have value 1.0 + * phi one; // default is "one" + * dividedByVolume no; + * includeMask lessThan; + * + * // diameter of par1 is 0.003, so these settings + * // will select only particles of type par1 + * lessThanInfo + * { + * field diameter; + * value 0.0031; + * } + * } + * ); + * ``` + * + * @section defaults Default Behavior + * - By default, `phi` is set to the field named "one" which contains value 1.0 for all entries + * - `dividedByVolume` is set to "no" by default + * - `threshold` is set to 1 by default + * - `includeMask` can be set to various filters, with "all" being the default to include all particles + * + * @section special Special Fields + * The field named "one" is a special field where all members have the value 1.0. This makes it + * particularly useful for calculating: + * + * 1. Volume or number fractions (as shown in the par1Fraction example) + * 2. Simple counts when used with an appropriate mask + * 3. Normalizing values by particle count + * + * @see postprocessOperation + * @see executeAverageOperation + */ + +#include +#include + +#include "postprocessOperation.hpp" +#include "regionField.hpp" +#include "includeMask.hpp" + +namespace pFlow +{ + + +class PostprocessOperationAvMassVelocity +: + public postprocessOperation +{ + +public: + + TypeInfo("PostprocessOperation"); + + /// @brief Constructs average operation processor + /// @param opDict Operation parameters dictionary + /// @param regPoints Region points data + /// @param fieldsDB Fields database + PostprocessOperationAvMassVelocity( + const dictionary& opDict, + const regionPoints& regPoints, + fieldsDataBase& fieldsDB); + + /// destructor + ~PostprocessOperationAvMassVelocity() override = default; + + /// add this virtual constructor to the base class + add_vCtor + ( + postprocessOperation, + PostprocessOperationAvMassVelocity, + dictionary + ); + +}; + + +} + +#endif //__PostprocessOperationAvMassVelocity_hpp__ \ No newline at end of file diff --git a/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAverage.cpp b/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAverage.cpp new file mode 100644 index 00000000..a0de2384 --- /dev/null +++ b/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAverage.cpp @@ -0,0 +1,138 @@ +#include "PostprocessOperationAverage.hpp" +#include "dictionary.hpp" +#include "fieldsDataBase.hpp" +#include "fieldFunctions.hpp" + +/// Constructs average processor and initializes result field based on input field type +pFlow::PostprocessOperationAverage::PostprocessOperationAverage +( + const dictionary &opDict, + const regionPoints ®Points, + fieldsDataBase &fieldsDB +) +: + postprocessOperation(opDict, regPoints, fieldsDB), + calculateFluctuation2_(opDict.getValOrSet("fluctuation2", Logical(false))) +{ + if( fieldType() == getTypeName() ) + { + processedRegFieldPtr_ = makeUnique( + regionField(processedFieldName(), regPoints, real(0))); + } + else if( fieldType() == getTypeName() ) + { + processedRegFieldPtr_ = makeUnique( + regionField(processedFieldName(), regPoints, realx3(0))); + } + else if( fieldType() == getTypeName() ) + { + processedRegFieldPtr_ = makeUnique( + regionField(processedFieldName(), regPoints, realx4(0))); + } + else + { + fatalErrorInFunction<<" in dictionary "<< opDict.globalName() + << " field type is not supported for average operation" + << " field type is "<< fieldType() + << endl; + fatalExit; + } +} + +pFlow::PostprocessOperationAverage::PostprocessOperationAverage +( + const dictionary &opDict, + const word &fieldName, + const word &phiName, + const word &includeName, + const regionPoints ®Points, + fieldsDataBase &fieldsDB +) +: + postprocessOperation(opDict, fieldName, phiName, includeName, regPoints, fieldsDB), + calculateFluctuation2_(opDict.getValOrSet("fluctuation2", Logical(false))) +{ + if( fieldType() == getTypeName() ) + { + processedRegFieldPtr_ = makeUnique( + regionField(processedFieldName(), regPoints, real(0))); + } + else if( fieldType() == getTypeName() ) + { + processedRegFieldPtr_ = makeUnique( + regionField(processedFieldName(), regPoints, realx3(0))); + } + else if( fieldType() == getTypeName() ) + { + processedRegFieldPtr_ = makeUnique( + regionField(processedFieldName(), regPoints, realx4(0))); + } + else + { + fatalErrorInFunction<<" in dictionary "<< opDict.globalName() + << " field type is not supported for average operation" + << " field type is "<< fieldType() + << endl; + fatalExit; + } +} + +/// Performs weighted average of field values within each region +bool pFlow::PostprocessOperationAverage::execute +( + const std::vector>& weights +) +{ + auto allField = database().updateFieldAll(fieldName()); + auto phi = database().updateFieldReal( + phiFieldName()); + + auto mask = getMask(); + word procName = processedFieldName(); + const auto& regP = regPoints(); + bool dbVol = divideByVolume(); + + processedRegFieldPtr_ = makeUnique + ( + std::visit([&](auto&& field)->processedRegFieldType + { + return executeAverageOperation( + procName, + field, + regP, + dbVol, + weights, + phi, + mask); + }, + allField) + ); + + if(calculateFluctuation2_) + { + auto& processedRegField = processedRegFieldPtr_(); + fluctuation2FieldPtr_ = makeUnique + ( + std::visit([&](auto& field)->processedRegFieldType + { + using T = typename std::decay_t>::valueType; + if constexpr( std::is_same_v || + std::is_same_v|| + std::is_same_v) + { + return executeFluctuation2Operation( + procName, + field, + std::get>(processedRegField), + dbVol, + weights, + mask); + } + }, + allField) + ); + } + + + return true; +} \ No newline at end of file diff --git a/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAverage.hpp b/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAverage.hpp new file mode 100644 index 00000000..93181c1e --- /dev/null +++ b/src/PostprocessData/operation/PostprocessOperation/PostprocessOperationAverage.hpp @@ -0,0 +1,203 @@ +/*------------------------------- phasicFlow --------------------------------- + O C enter of + O O E ngineering and + O O M ultiscale modeling of + OOOOOOO F luid flow +------------------------------------------------------------------------------ + Copyright (C): www.cemf.ir + email: hamid.r.norouzi AT gmail.com +------------------------------------------------------------------------------ +Licence: + This file is part of phasicFlow code. It is a free software for simulating + granular and multiphase flows. You can redistribute it and/or modify it under + the terms of GNU General Public License v3 or any other later versions. + + phasicFlow is distributed to help others in their research in the field of + granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +-----------------------------------------------------------------------------*/ + +#ifndef __PostprocessOperationAverage_hpp__ +#define __PostprocessOperationAverage_hpp__ + +/*! + * @class PostprocessOperationAverage + * @brief A class for averaging field values within specified regions during post-processing. + * + * @details + * The PostprocessOperationAverage class is a specialized post-processing operation that + * calculates the average of field values within specified regions. It inherits from the + * postprocessOperation base class and implements a weighted averaging operation that + * can be applied to scalar (real), vector (realx3), and tensor (realx4) fields. + * + * The average operation follows the mathematical formula: + * \f[ + * \text{result} = \frac{\sum_{j \in \text{includeMask}} w_j \cdot \phi_j \cdot \text{field}_j} + * {\sum_{i \in \text{processRegion}} w_i \cdot \phi_i} + * \f] + * + * Where: + * - \f$ i \f$ represents all particles within the specified processing region + * - \f$ j \f$ belongs to a subset of \f$ i \f$ based on an includeMask + * - \f$ w_i \f$ is the weight factor for particle \f$ i \f$ + * - \f$ \phi_i \f$ is the value from the phi field for particle \f$ i \f$ + * - \f$ \text{field}_j \f$ is the value from the target field for particle \f$ j \f$ + * + * The calculation can optionally be divided by the region volume (when divideByVolume is set to yes), + * which allows calculating normalized averages: + * \f[ + * \text{result} = \frac{1}{V_{\text{region}}} \frac{\sum_{j \in \text{includeMask}} w_j \cdot \phi_j \cdot \text{field}_j} + * {\sum_{i \in \text{processRegion}} w_i \cdot \phi_i} + * \f] + * + * The averaging can be further filtered using an includeMask to selectively include only + * specific particles that satisfy certain criteria. + * + * This class supports the following field types: + * - real (scalar values) + * - realx3 (vector values) + * - realx4 (tensor values) + * + * @section usage Usage Example + * Below is a sample dictionary showing how to configure and use this class: + * + * ``` + * processMethod arithmetic; // method of performing the sum (arithmetic, uniformDistribution, GaussianDistribution) + * processRegion sphere; // type of region on which processing is performed + * + * sphereInfo + * { + * radius 0.01; + * center (-0.08 -0.08 0.015); + * } + * + * timeControl default; + * + * /// all the post process operations to be done + * operations + * ( + * // computes the arithmetic mean of particle velocity + * averageVel + * { + * function average; + * field velocity; + * dividedByVolume no; // default is no + * threshold 3; // default is 1 + * includeMask all; // include all particles in the calculation + * } + * + * // computes the fraction of par1 in the region + * par1Fraction + * { + * function average; + * field one; // the "one" field is special - all members have value 1.0 + * phi one; // default is "one" + * dividedByVolume no; + * includeMask lessThan; + * + * // diameter of par1 is 0.003, so these settings + * // will select only particles of type par1 + * lessThanInfo + * { + * field diameter; + * value 0.0031; + * } + * } + * ); + * ``` + * + * @section defaults Default Behavior + * - By default, `phi` is set to the field named "one" which contains value 1.0 for all entries + * - `dividedByVolume` is set to "no" by default + * - `threshold` is set to 1 by default + * - `includeMask` can be set to various filters, with "all" being the default to include all particles + * + * @section special Special Fields + * The field named "one" is a special field where all members have the value 1.0. This makes it + * particularly useful for calculating: + * + * 1. Volume or number fractions (as shown in the par1Fraction example) + * 2. Simple counts when used with an appropriate mask + * 3. Normalizing values by particle count + * + * @see postprocessOperation + * @see executeAverageOperation + */ + +#include +#include + +#include "postprocessOperation.hpp" +#include "regionField.hpp" +#include "includeMask.hpp" + +namespace pFlow +{ + + +class PostprocessOperationAverage +: + public postprocessOperation +{ +private: + + ///< Flag to calculate fluctuation powered by 2 + Logical calculateFluctuation2_; + + /// Result field containing averages for each region (real, realx3, or realx4) + uniquePtr processedRegFieldPtr_ = nullptr; + + uniquePtr fluctuation2FieldPtr_ = nullptr; + +public: + + TypeInfo("PostprocessOperation"); + + /// @brief Constructs average operation processor + /// @param opDict Operation parameters dictionary + /// @param regPoints Region points data + /// @param fieldsDB Fields database + PostprocessOperationAverage( + const dictionary& opDict, + const regionPoints& regPoints, + fieldsDataBase& fieldsDB); + + PostprocessOperationAverage( + const dictionary& opDict, + const word& fieldName, + const word& phiName, + const word& includeName, + const regionPoints& regPoints, + fieldsDataBase& fieldsDB); + + + /// destructor + ~PostprocessOperationAverage() override = default; + + /// add this virtual constructor to the base class + add_vCtor + ( + postprocessOperation, + PostprocessOperationAverage, + dictionary + ); + + /// @brief Get the processed field containing regional averages + /// @return Const reference to average results + const processedRegFieldType& processedField()const override + { + return processedRegFieldPtr_(); + } + + /// @brief Execute average operation on field values + /// @param weights Weight factors for particles + /// @return True if successful + bool execute(const std::vector>& weights) override; + +}; + + +} + +#endif //__PostprocessOperationAverage_hpp__ \ No newline at end of file diff --git a/src/PostprocessData/region/regionPoints/multipleSpheresRegionPoints/multipleSpheresRegionPoints.cpp b/src/PostprocessData/region/regionPoints/multipleSpheresRegionPoints/multipleSpheresRegionPoints.cpp new file mode 100644 index 00000000..8665f72e --- /dev/null +++ b/src/PostprocessData/region/regionPoints/multipleSpheresRegionPoints/multipleSpheresRegionPoints.cpp @@ -0,0 +1,98 @@ +#include "multipleSpheresRegionPoints.hpp" +#include "fieldsDataBase.hpp" + +pFlow::multipleSpheresRegionPoints::multipleSpheresRegionPoints +( + const dictionary &dict, + fieldsDataBase &fieldsDataBase +) +: + regionPoints(dict, fieldsDataBase) +{ + const auto& multiSphereInfo = dict.subDict("multipleSphereInfo"); + + // Read centers and radii lists + auto centers = multiSphereInfo.getVal>("centers"); + auto radii = multiSphereInfo.getVal>("radii"); + + // Check if lists have the same length + if(centers.size() != radii.size()) + { + fatalErrorInFunction + << "The number of centers (" << centers.size() + << ") does not match the number of radii (" << radii.size() << ")" + << endl; + fatalExit; + } + + uint32 nSpheres = centers.size(); + + // Initialize data structures + sphereRegions_.resize(nSpheres, sphere(realx3(0.0, 0.0, 0.0), 1.0)); + centerPoints_.resize(nSpheres); + diameters_.resize(nSpheres); + volumes_.resize(nSpheres); + selectedPoints_.resize(nSpheres); + + // Setup each sphere + for (uint32 i = 0; i < nSpheres; ++i) + { + real diameter = 2.0 * radii[i]; // Convert radius to diameter + + sphereRegions_[i] = pFlow::sphere(centers[i], radii[i]); + centerPoints_[i] = centers[i]; + diameters_[i] = diameter; + volumes_[i] = sphereRegions_[i].volume(); + } +} + +pFlow::span pFlow::multipleSpheresRegionPoints::indices(uint32 elem) const +{ + if (elem >= size()) + { + fatalErrorInFunction + << "The element index is out of range. elem: " << elem + << " size: " << size() << endl; + fatalExit; + } + + return span(selectedPoints_[elem].data(), selectedPoints_[elem].size()); +} + +bool pFlow::multipleSpheresRegionPoints::update() +{ + const auto points = database().updatePoints(); + for (auto& elem : selectedPoints_) + { + elem.clear(); + } + + for (uint32 i = 0; i < points.size(); ++i) + { + for (uint32 j = 0; j < sphereRegions_.size(); ++j) + { + if (sphereRegions_[j].isInside(points[i])) + { + selectedPoints_[j].push_back(i); + } + } + } + return true; +} + +bool pFlow::multipleSpheresRegionPoints::write(iOstream &os) const +{ + os << "# Multiple spheres region points\n"; + os << "# No." << tab << "centerPoint" << tab << "diameter" << endl; + for (uint32 i = 0; i < sphereRegions_.size(); ++i) + { + os << "# " << i << tab << sphereRegions_[i].center() << tab << diameters_[i] << '\n'; + } + os << "time/No. "; + for (uint32 i = 0; i < sphereRegions_.size(); ++i) + { + os << i << " "; + } + os << endl; + return true; +} diff --git a/src/PostprocessData/region/regionPoints/multipleSpheresRegionPoints/multipleSpheresRegionPoints.hpp b/src/PostprocessData/region/regionPoints/multipleSpheresRegionPoints/multipleSpheresRegionPoints.hpp new file mode 100644 index 00000000..f250d216 --- /dev/null +++ b/src/PostprocessData/region/regionPoints/multipleSpheresRegionPoints/multipleSpheresRegionPoints.hpp @@ -0,0 +1,161 @@ +/*------------------------------- phasicFlow --------------------------------- + O C enter of + O O E ngineering and + O O M ultiscale modeling of + OOOOOOO F luid flow +------------------------------------------------------------------------------ + Copyright (C): www.cemf.ir + email: hamid.r.norouzi AT gmail.com +------------------------------------------------------------------------------ +Licence: + This file is part of phasicFlow code. It is a free software for simulating + granular and multiphase flows. You can redistribute it and/or modify it under + the terms of GNU General Public License v3 or any other later versions. + + phasicFlow is distributed to help others in their research in the field of + granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +-----------------------------------------------------------------------------*/ + +/** + * @class multipleSpheresRegionPoints + * @brief A class to select and track particles contained within multiple + * spherical regions + * @details This class defines multiple spherical regions in the simulation + * domain and identifies which particles are contained within each + * sphere at each time step. It inherits from the regionPoints base + * class and specializes it for handling multiple spherical regions + * simultaneously. + * + * The class reads a list of center points and radii from a dictionary, + * creates sphere objects for each, and provides methods to: + * - Track which particles are inside each spherical region + * - Return volumetric information about the regions + * - Access center points and size information for each sphere + * + * + * @note Used in post-processing workflows to analyze particle behavior + * in specific regions of interest within the simulation domain. + * + * @see regionPoints Base class for all region-based point selection + * @see sphere Geometric primitive used to define spherical regions + * @see postprocessPhasicFlow Utility for post-processing simulation data + * @see fieldsDataBase Database containing simulation field data + */ + +#ifndef __multipleSpheresRegionPoints_hpp__ +#define __multipleSpheresRegionPoints_hpp__ + +#include "regionPoints.hpp" +#include "sphere.hpp" +#include "Vectors.hpp" + +namespace pFlow +{ + +class multipleSpheresRegionPoints +: + public regionPoints +{ +private: + + /// Vector containing all spherical regions used for particle selection + Vector sphereRegions_; + + /// Center coordinates of all spherical regions + realx3Vector centerPoints_; + + /// Diameters of all spherical regions + realVector diameters_; + + /// Volumes of all spherical regions + realVector volumes_; + + /// Vectors of point indices for particles contained in each spherical region + /// Each element corresponds to a particular sphere region + Vector selectedPoints_; + +public: + + /// Type identification for run-time type information + TypeInfo("multipleSpheres"); + + /// Constructor + /// @param dict Dictionary containing multipleSpheresInfo for the regions + /// @param fieldsDataBase Reference to the database containing field data + multipleSpheresRegionPoints( + const dictionary& dict, + fieldsDataBase& fieldsDataBase); + + /// Virtual destructor for proper inheritance cleanup + ~multipleSpheresRegionPoints() override = default; + + /// Returns the number of spherical regions + /// @return Number of spherical regions + uint32 size()const override + { + return sphereRegions_.size(); + } + + /// Checks if there are any spherical regions defined + /// @return True if no regions exist, false otherwise + bool empty()const override + { + return sphereRegions_.empty(); + } + + /// Returns the volumes of all spherical regions + /// @return Span containing the volumes of all regions + span volumes()const override + { + return span(volumes_.data(), volumes_.size()); + } + + /// Returns the diameters of all spherical regions + /// @return Span containing the diameters of all regions + span diameters()const + { + return span(diameters_.data(), diameters_.size()); + } + + /// Returns the equivalent diameters of all spherical regions + /// @return Span containing the equivalent diameters (same as diameters) + span eqDiameters()const + { + return diameters(); + } + + /// Returns the center coordinates of all spherical regions + /// @return Span containing the center points of all regions + span centers()const override + { + return span(centerPoints_.data(), centerPoints_.size()); + } + + /// Returns the indices of particles contained in a specific spherical region + /// @param elem Index of the spherical region to query + /// @return Span containing indices of particles within the specified region + span indices(uint32 elem)const override; + + /// Updates the selection of particles within each spherical region + /// @return True if update was successful, false otherwise + bool update() override; + + /// Determines if data should be written to the same time file + /// @return True to indicate regions should be written to the same time file + bool writeToSameTimeFile()const override + { + return true; + } + + /// Writes region data to the output stream + /// @param os Output stream to write data to + /// @return True if write operation was successful, false otherwise + bool write(iOstream& os) const override; +}; + +} + + +#endif // __multipleSpheresRegionPoints_hpp__ \ No newline at end of file