diff --git a/src/field/interpolators/FieldInterpolatorCreators.h b/src/field/interpolators/FieldInterpolatorCreators.h new file mode 100644 index 0000000000000000000000000000000000000000..9b7c4f656c7cf158856945c0a7f09741f9d8475b --- /dev/null +++ b/src/field/interpolators/FieldInterpolatorCreators.h @@ -0,0 +1,107 @@ +//====================================================================================================================== +// +// This file is part of waLBerla. waLBerla is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. +// +//! \file FieldInterpolatorCreators.h +//! \ingroup field +//! \author Christoph Rettinger <christoph.rettinger@fau.de> +// +//====================================================================================================================== + +#pragma once + +#include "core/DataTypes.h" +#include "core/Set.h" + +#include "blockforest/BlockDataHandling.h" + +#include "domain_decomposition/StructuredBlockStorage.h" + +namespace walberla { +namespace field { + +//********************************************************************************************************************** +/*! FieldInterpolatorCreators + * + * \ingroup field + * + * Interpolator_T: A field interpolator that has a constructor + * ( const shared_ptr<StructuredBlockStorage> & blockStorage, const IBlock & block, const BaseField_T & baseField, + * const FlagField_T & flagField, const flag_t & evaluationMask ) + * and getter functions: + * template< typename ForwardIterator_T > inline void get( const Vector3<real_t> & position, ForwardIterator_T interpolationResultBegin ) + * template< typename ForwardIterator_T > inline void get( const real_t & x, const real_t & y, const real_t & z, ForwardIterator_T interpolationResultBegin ) + * + * See TrilinearFieldInterpolator for an example implementation. + * + * A field interpolator is aware of the flag field (FlagField_T) and uses only values that contain flags from a given mask. + * + * Field Interpolators can be used to sample the underlying field at certain positions. + * E.g. the fluid velocity can be interpolated to a desired global position from a velocity field. + * + */ +//********************************************************************************************************************** +template< typename Interpolator_T, typename FlagField_T > +class InterpolatorHandling : public blockforest::AlwaysInitializeBlockDataHandling< Interpolator_T > +{ +public: + + InterpolatorHandling( const shared_ptr<StructuredBlockStorage> & blockStorage, + const ConstBlockDataID & interpolatedFieldID, + const ConstBlockDataID & flagFieldID, + const Set< FlagUID > & cellsToEvaluate ) : + blockStorage_( blockStorage ), interpolatedFieldID_( interpolatedFieldID ), flagFieldID_( flagFieldID ), cellsToEvaluate_( cellsToEvaluate ) + {} + + Interpolator_T * initialize( IBlock * const block ) + { + typedef typename Interpolator_T::BaseField_T InterpolatedField_T; + typedef typename FlagField_T::flag_t flag_t; + const InterpolatedField_T * interpolatedField = block->getData< InterpolatedField_T >( interpolatedFieldID_ ); + const FlagField_T * flagField = block->getData< FlagField_T >( flagFieldID_ ); + + WALBERLA_ASSERT_NOT_NULLPTR( interpolatedField ); + WALBERLA_ASSERT_NOT_NULLPTR( flagField ); + + const flag_t evaluationMask = flagField->getMask( cellsToEvaluate_ ); + + return new Interpolator_T( blockStorage_, *block, *interpolatedField, *flagField, evaluationMask ); + } + +private: + + shared_ptr<StructuredBlockStorage> blockStorage_; + ConstBlockDataID interpolatedFieldID_; + ConstBlockDataID flagFieldID_; + Set< FlagUID > cellsToEvaluate_; + +}; // class InterpolatorHandling + + + +template< typename Interpolator_T, typename FlagField_T > +inline BlockDataID addFieldInterpolator( const shared_ptr< StructuredBlockStorage > & blocks, + const ConstBlockDataID & interpolatedFieldID, + const ConstBlockDataID & flagFieldID, + const Set< FlagUID > & cellsToEvaluate, + const std::string & identifier = std::string(), + const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(), + const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() ) +{ + return blocks->addBlockData( make_shared< InterpolatorHandling< Interpolator_T, FlagField_T > >( blocks, interpolatedFieldID, flagFieldID, cellsToEvaluate ), identifier, requiredSelectors, incompatibleSelectors ); +} + + +} // namespace field +} // namespace walberla diff --git a/src/field/interpolators/KernelFieldInterpolator.h b/src/field/interpolators/KernelFieldInterpolator.h new file mode 100644 index 0000000000000000000000000000000000000000..8e22b16609890258147fdea20d35b946192f674d --- /dev/null +++ b/src/field/interpolators/KernelFieldInterpolator.h @@ -0,0 +1,269 @@ +//====================================================================================================================== +// +// This file is part of waLBerla. waLBerla is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. +// +//! \file KernelFieldInterpolator.h +//! \ingroup field +//! \author Christoph Rettinger <christoph.rettinger@fau.de> +// +//====================================================================================================================== + +#pragma once + +#include "core/debug/Debug.h" +#include "core/math/Vector3.h" + +#include "domain_decomposition/StructuredBlockStorage.h" + +#include "field/FlagField.h" + +#include "stencil/D3Q27.h" + +#include <numeric> + +namespace walberla { +namespace field { + +namespace kernelweights +{ + +// corresponds to the smoothed dirac delta function from Roma et al - An Adaptive Version of the Immersed Boundary Method +// f(r) != 0 for abs(r) < 1.5 -> requires three neighborhood cells +real_t smoothedDeltaFunction( const real_t & r ) +{ + real_t rAbs = std::fabs(r); + if( rAbs <= real_t(0.5) ) + { + return (real_t(1) + std::sqrt( - real_t(3) * r * r + real_t(1) ) ) * (real_t(1) / real_t(3)); + } else if ( rAbs < real_t(1.5) ) + { + return (real_t(5) - real_t(3) * rAbs - std::sqrt( - real_t(3) * ( real_t(1) - rAbs ) * ( real_t(1) - rAbs ) + real_t(1) ) ) * ( real_t(1) / real_t(6) ); + } else + { + return real_t(0); + } + +} + +// X: Lagrangian position, x: Eulerian position (usually cell center), global coordinates +// dx, dy, dz: mesh spacing +real_t kernelWeightFunction( const real_t & X, const real_t & Y, const real_t & Z, + const real_t & x, const real_t & y, const real_t & z, + const real_t & dx = real_t(1), const real_t & dy = real_t(1), const real_t & dz = real_t(1) ) +{ + return smoothedDeltaFunction( ( X - x ) / dx ) * smoothedDeltaFunction( ( Y - y ) / dy ) * smoothedDeltaFunction( ( Z - z ) / dz ); +} + +// X: Lagrangian position, x: Eulerian position (usually cell center), global coordinates +// dx, dy, dz: mesh spacing +real_t kernelWeightFunction( const Vector3<real_t> & X, const Vector3<real_t> & x, + const real_t & dx = real_t(1), const real_t & dy = real_t(1), const real_t & dz = real_t(1) ) +{ + return smoothedDeltaFunction( ( X[0] - x[0] ) / dx ) * smoothedDeltaFunction( ( X[1] - x[1] ) / dy ) * smoothedDeltaFunction( ( X[2] - x[2] ) / dz ); +} + +} // namespace kernelweights + + +/*! Interpolator for walberla::field::GhostLayerField with kernel strategy + * + * \ingroup field + * + * This interpolator uses a smoothed dirac kernel function to interpolate values. + * The applied weights are given in the namespace field::kernelweights. + * Needs the full neighborhood of the containing cell, i.e. 27 cells. + * Never construct this interpolator directly, but use the functionality from the FieldInterpolatorCreator.h instead. + */ +template< typename Field_T, typename FlagField_T > +class KernelFieldInterpolator +{ +public: + + static const uint_t F_SIZE = Field_T::F_SIZE; + + typedef Field_T BaseField_T; + typedef typename FlagField_T::flag_t flag_t; + typedef KernelFieldInterpolator<Field_T,FlagField_T> OwnType; + + KernelFieldInterpolator( const shared_ptr<StructuredBlockStorage> & blockStorage, const IBlock & block, + const BaseField_T & baseField, const FlagField_T & flagField, + const flag_t & evaluationMask ) + : blockStorage_( blockStorage ), block_( block ), baseField_( baseField ), flagField_( flagField ), evaluationMask_( evaluationMask ) + { + WALBERLA_ASSERT(baseField.nrOfGhostLayers() > uint_t(0), "field for kernel interpolation needs at least one ghost layer"); + } + + + inline bool operator==( const OwnType & other ){ return baseField_ == other.baseField_; } + + template< typename ForwardIterator_T > + inline void get( const Vector3<real_t> & position, ForwardIterator_T interpolationResultBegin ) + { + get( position[0], position[1], position[2], interpolationResultBegin ); + } + + template< typename ForwardIterator_T > + inline void get( const real_t & x, const real_t & y, const real_t & z, ForwardIterator_T interpolationResultBegin ) + { + WALBERLA_ASSERT(block_.getAABB().contains(x,y,z), + "Interpolation position <" << x << ", " << y << ", " << z << "> is not contained inside the block of this interpolator with AABB " << block_.getAABB() << " !"); + + Cell centerCell = blockStorage_->getBlockLocalCell( block_, x, y, z ); + + const real_t dx = blockStorage_->dx( blockStorage_->getLevel( block_ ) ); + const real_t dy = blockStorage_->dy( blockStorage_->getLevel( block_ ) ); + const real_t dz = blockStorage_->dz( blockStorage_->getLevel( block_ ) ); + + const uint_t neighborhoodSize = uint_t(1); + + CellInterval cellNeighborhood( centerCell[0] - cell_idx_c(neighborhoodSize), centerCell[1] - cell_idx_c(neighborhoodSize), centerCell[2] - cell_idx_c(neighborhoodSize), + centerCell[0] + cell_idx_c(neighborhoodSize), centerCell[1] + cell_idx_c(neighborhoodSize), centerCell[2] + cell_idx_c(neighborhoodSize) ); + + const uint_t kernelSizeOneDirection = uint_t(2) * neighborhoodSize + uint_t(1); + // store the calculated weighting factors of the available cells, i.e. cells flagged by the evaluation mask + std::vector<real_t> weights( kernelSizeOneDirection * kernelSizeOneDirection * kernelSizeOneDirection, real_t(0) ); + + // store the calculated weighting factors of the UNavailable cells, i.e. cells not included in the evaluation mask + std::vector<real_t> weightsUnavailable( kernelSizeOneDirection * kernelSizeOneDirection * kernelSizeOneDirection, real_t(0) ); + + cell_idx_t cellIdx0x = cellNeighborhood.xMin(); + cell_idx_t cellIdx0y = cellNeighborhood.yMin(); + cell_idx_t cellIdx0z = cellNeighborhood.zMin(); + uint_t nx = kernelSizeOneDirection; + uint_t nxy = kernelSizeOneDirection * kernelSizeOneDirection; + Vector3<real_t> cellCenter0 = blockStorage_->getBlockLocalCellCenter( block_, Cell(cellIdx0x, cellIdx0y, cellIdx0z) ); // = cell in neighborhood with smallest x-, y-, and z-indices + + // calculate kernel weights of all cells in neighborhood + for( uint_t k = uint_t(0); k < kernelSizeOneDirection; ++k) + { + for( uint_t j = uint_t(0); j < kernelSizeOneDirection; ++j) + { + for( uint_t i = uint_t(0); i < kernelSizeOneDirection; ++i) + { + Vector3<real_t> curCellCenter( cellCenter0[0] + real_c(i) * dx, cellCenter0[1] + real_c(j) * dy, cellCenter0[2] + real_c(k) * dz); + if( flagField_.isPartOfMaskSet( cellIdx0x + cell_idx_c(i), cellIdx0y + cell_idx_c(j), cellIdx0z + cell_idx_c(k), evaluationMask_ ) ) + { + weights[k*nxy+j*nx+i] = kernelweights::kernelWeightFunction( x, y, z, curCellCenter[0], curCellCenter[1], curCellCenter[2], dx, dy, dz ); + } + else + { + weightsUnavailable[k*nxy+j*nx+i] = kernelweights::kernelWeightFunction( x, y, z, curCellCenter[0], curCellCenter[1], curCellCenter[2], dx, dy, dz ); + } + } + } + } + +/* + // zero entries in weights array (and non-zero entries in weightsUnavailable) indicate non-fluid nodes + // weight correction to incorporate extrapolation for kernel interpolation, center element can not be redistributed + // without this part, the kernel interpolator can not extrapolate values + // this is based on experimental findings by the author and general correctness is not ensured, thus it is commented out + for( auto stenIt = stencil::D3Q27::beginNoCenter(); stenIt != stencil::D3Q27::end(); ++stenIt ) + { + int cx = stenIt.cx(); + int cy = stenIt.cy(); + int cz = stenIt.cz(); + int i = cx + 1; + int j = cy + 1; + int k = cz + 1; + + if( weightsUnavailable[k*nxy+j*nx+i] > real_t(0) ) + { + // check if we can distribute the non-fluid weight to nearby fluid weights that lie in one line inside the neighborhood + // it should generally not matter in which direction it is distributed + // check x direction + if( weights[k*nxy+j*nx+i-cx] > real_t(0) && weights[k*nxy+j*nx+i-2*cx] > real_t(0) ) + { + weights[k*nxy+j*nx+i-cx] += real_t(2)*weightsUnavailable[k*nxy+j*nx+i]; + weights[k*nxy+j*nx+i-2*cx] -= weightsUnavailable[k*nxy+j*nx+i]; + weightsUnavailable[k*nxy+j*nx+i] = real_t(0); + continue; + } + // check y direction + if( weights[k*nxy+(j-cy)*nx+i] > real_t(0) && weights[k*nxy+(j-2*cy)*nx+i] > real_t(0) ) + { + weights[k*nxy+(j-cy)*nx+i] += real_t(2)*weightsUnavailable[k*nxy+j*nx+i]; + weights[k*nxy+(j-2*cy)*nx+i] -= weightsUnavailable[k*nxy+j*nx+i]; + weightsUnavailable[k*nxy+j*nx+i] = real_t(0); + continue; + } + // check z direction + if( weights[(k-cz)*nxy+j*nx+i] > real_t(0) && weights[(k-2*cz)*nxy+j*nx+i] > real_t(0) ) + { + weights[(k-cz)*nxy+j*nx+i] += real_t(2)*weightsUnavailable[k*nxy+j*nx+i]; + weights[(k-2*cz)*nxy+j*nx+i] -= weightsUnavailable[k*nxy+j*nx+i]; + weightsUnavailable[k*nxy+j*nx+i] = real_t(0); + continue; + } + } + } +*/ + // scale available weights by the total amount of unavailable weights such that afterwards sum over all weights is 1 + const real_t sumOfWeightsUnavailable = std::accumulate(weightsUnavailable.begin(), weightsUnavailable.end(), real_t(0) ); + const real_t sumOfWeights = std::accumulate(weights.begin(), weights.end(), real_t(0) ); + + // check if at least one cell was available, to prevent division by 0 + if( sumOfWeights <= real_t(0) ) + return; + + const real_t scalingFactor = real_t(1) + sumOfWeightsUnavailable / sumOfWeights; + + for( auto weightIt = weights.begin(); weightIt != weights.end(); ++weightIt ) + { + *weightIt *= scalingFactor; + } + + // interpolate value to interpolation position using the previously calculated weights + // values to not be included have a weight of 0 + for( uint_t k = uint_t(0); k < kernelSizeOneDirection; ++k) + { + for( uint_t j = uint_t(0); j < kernelSizeOneDirection; ++j) + { + for( uint_t i = uint_t(0); i < kernelSizeOneDirection; ++i) + { + if ( weights[k*nxy+j*nx+i] > real_t(0) ) + { + Cell curCell( cellIdx0x + cell_idx_c(i), cellIdx0y + cell_idx_c(j), cellIdx0z + cell_idx_c(k) ); + addWeightedCellValue( interpolationResultBegin, curCell, weights[k*nxy+j*nx+i] ); + } + } + } + } + } + +private: + + template< typename ForwardIterator_T > + void addWeightedCellValue( ForwardIterator_T interpolationResultBegin, const Cell & curCell, const real_t & weighting ) + { + for( uint_t f = uint_t(0); f < F_SIZE; ++f ) + { + WALBERLA_ASSERT( !math::isnan(baseField_( curCell, f)), "NaN found in component " << f << " when interpolating from cell " << curCell ); + + *interpolationResultBegin += weighting * baseField_( curCell, f); + ++interpolationResultBegin; + } + } + + shared_ptr<StructuredBlockStorage> blockStorage_; + const IBlock & block_; + const BaseField_T & baseField_; + const FlagField_T & flagField_; + flag_t evaluationMask_; + +}; + + +} // namespace field +} // namespace walberla diff --git a/src/field/interpolators/NearestNeighborFieldInterpolator.h b/src/field/interpolators/NearestNeighborFieldInterpolator.h new file mode 100644 index 0000000000000000000000000000000000000000..677310feb3fcd00c6da69fb8d6c1b8df62a97086 --- /dev/null +++ b/src/field/interpolators/NearestNeighborFieldInterpolator.h @@ -0,0 +1,139 @@ +//====================================================================================================================== +// +// This file is part of waLBerla. waLBerla is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. +// +//! \file NearestNeighborFieldInterpolator.h +//! \ingroup field +//! \author Christoph Rettinger <christoph.rettinger@fau.de> +// +//====================================================================================================================== + +#pragma once + +#include "core/debug/Debug.h" + +#include "domain_decomposition/StructuredBlockStorage.h" + +#include "field/FlagField.h" + +namespace walberla { +namespace field { + +/*! Interpolator for walberla::field::Field with nearest neighbor strategy + * + * \ingroup field + * + * This interpolator obtains the value from the nearest cell, flagged in the evaluation mask, of the interpolation position. + * This is usually the cell that contains the interpolation position. + * If this cell is a cell that is not within the evaluation mask, the direct neighborhood (8 cells) will be searched for an available cell. + * Never construct this interpolator directly, but use the functionality from FieldInterpolatorCreators.h instead. + */ +template< typename Field_T, typename FlagField_T > +class NearestNeighborFieldInterpolator +{ +public: + + static const uint_t F_SIZE = Field_T::F_SIZE; + + typedef Field_T BaseField_T; + typedef typename FlagField_T::flag_t flag_t; + typedef NearestNeighborFieldInterpolator<Field_T,FlagField_T> OwnType; + + NearestNeighborFieldInterpolator( const shared_ptr<StructuredBlockStorage> & blockStorage, const IBlock & block, + const BaseField_T & baseField, const FlagField_T & flagField, + const flag_t & evaluationMask ) + : blockStorage_( blockStorage ), block_( block ), baseField_( baseField ), flagField_( flagField ), evaluationMask_( evaluationMask ) + {} + + + inline bool operator==( const OwnType & other ){ return baseField_ == other.baseField_; } + + template< typename ForwardIterator_T > + inline void get( const Vector3<real_t> & position, ForwardIterator_T interpolationResultBegin ) + { + get( position[0], position[1], position[2], interpolationResultBegin ); + } + + template< typename ForwardIterator_T > + inline void get( const real_t & x, const real_t & y, const real_t & z, ForwardIterator_T interpolationResultBegin ) + { + + WALBERLA_ASSERT(block_.getAABB().contains(x,y,z), + "Interpolation position <" << x << ", " << y << ", " << z << "> is not contained inside the block of this interpolator with AABB " << block_.getAABB() << " !"); + + Cell nearestCell = blockStorage_->getBlockLocalCell( block_, x, y, z ); + + if( flagField_.isPartOfMaskSet( nearestCell, evaluationMask_ ) ) + { + for( uint_t f = uint_t(0); f < F_SIZE; ++f ) + { + WALBERLA_ASSERT( !math::isnan( baseField_( nearestCell, f) ), "NaN found in component " << f << " when interpolating from cell " << nearestCell ); + *interpolationResultBegin = baseField_( nearestCell, f); + ++interpolationResultBegin; + } + } + else + { + // look for available cell in direct neighborhood + + CellInterval fieldXYZSize = baseField_.xyzSize(); + + Vector3<real_t> nearestCellCenter = blockStorage_->getBlockLocalCellCenter( block_, nearestCell ); + const cell_idx_t xNeighbor = cell_idx_c( floor( x - nearestCellCenter[0] ) ); + const cell_idx_t yNeighbor = cell_idx_c( floor( y - nearestCellCenter[1] ) ); + const cell_idx_t zNeighbor = cell_idx_c( floor( z - nearestCellCenter[2] ) ); + + const cell_idx_t xMin = nearestCell.x() + xNeighbor; + const cell_idx_t yMin = nearestCell.y() + yNeighbor; + const cell_idx_t zMin = nearestCell.z() + zNeighbor; + + for( cell_idx_t zC = zMin; zC <= zMin + cell_idx_t(1); ++zC) + { + for( cell_idx_t yC = yMin; yC <= yMin + cell_idx_t(1); ++yC) + { + for( cell_idx_t xC = xMin; xC <= xMin + cell_idx_t(1); ++xC) + { + Cell curCell(xC,yC,zC); + if( fieldXYZSize.contains(curCell) ) + { + if (flagField_.isPartOfMaskSet(curCell, evaluationMask_)) + { + for (uint_t f = uint_t(0); f < F_SIZE; ++f) + { + WALBERLA_ASSERT(!math::isnan(baseField_(curCell, f)), + "NaN found in component " << f << " when interpolating from cell " + << curCell); + *interpolationResultBegin = baseField_(curCell, f); + ++interpolationResultBegin; + } + break; + } + } + } + } + } + } + } + +private: + + shared_ptr<StructuredBlockStorage> blockStorage_; + const IBlock & block_; + const BaseField_T & baseField_; + const FlagField_T & flagField_; + flag_t evaluationMask_; +}; + +} // namespace field +} // namespace walberla diff --git a/src/field/interpolators/TrilinearFieldInterpolator.h b/src/field/interpolators/TrilinearFieldInterpolator.h new file mode 100644 index 0000000000000000000000000000000000000000..4a878381cb9622e55fae5d8cfee5105bddf40def --- /dev/null +++ b/src/field/interpolators/TrilinearFieldInterpolator.h @@ -0,0 +1,188 @@ +//====================================================================================================================== +// +// This file is part of waLBerla. waLBerla is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. +// +//! \file TrilinearFieldInterpolator.h +//! \ingroup field +//! \author Christoph Rettinger <christoph.rettinger@fau.de> +// +//====================================================================================================================== + +#pragma once + +#include "core/debug/Debug.h" + +#include "domain_decomposition/StructuredBlockStorage.h" + +#include "field/FlagField.h" + + +namespace walberla { +namespace field { + +/*! Interpolator for walberla::field::GhostLayerField with trilinear strategy + * + * \ingroup field + * + * This interpolator uses trilinear interpolation to obtain the interpolated value from the interpolation position. + * If at least one of the cells, that are required for trilinear interpolation, i.e. the 8 closest cells, + * is not contained in the evaluation mask, the nearest-neighbor interpolation (see NearestNeighborFieldInterpolator.h) + * will be used instead. + * Never construct this interpolator directly, but use the functionality from FieldInterpolatorCreators.h instead. + */ +template< typename Field_T, typename FlagField_T > +class TrilinearFieldInterpolator +{ +public: + + static const uint_t F_SIZE = Field_T::F_SIZE; + + typedef Field_T BaseField_T; + typedef typename FlagField_T::flag_t flag_t; + typedef TrilinearFieldInterpolator<Field_T,FlagField_T> OwnType; + + TrilinearFieldInterpolator( const shared_ptr<StructuredBlockStorage> & blockStorage, const IBlock & block, + const BaseField_T & baseField, const FlagField_T & flagField, + const flag_t & evaluationMask ) + : blockStorage_( blockStorage ), block_( block ), baseField_( baseField ), flagField_( flagField ), evaluationMask_( evaluationMask ), + nearestNeighborInterpolator_( blockStorage, block, baseField, flagField, evaluationMask ) + { + WALBERLA_ASSERT(baseField.nrOfGhostLayers() > uint_t(0), "field for trilinear interpolation needs at least one ghost layer"); + } + + + inline bool operator==( const OwnType & other ){ return baseField_ == other.baseField_; } + + template< typename ForwardIterator_T > + inline void get( const Vector3<real_t> & position, ForwardIterator_T interpolationResultBegin ) + { + get( position[0], position[1], position[2], interpolationResultBegin ); + } + + template< typename ForwardIterator_T > + inline void get( const real_t & x, const real_t & y, const real_t & z, ForwardIterator_T interpolationResultBegin ) + { + WALBERLA_ASSERT(block_.getAABB().contains(x,y,z), + "Interpolation position <" << x << ", " << y << ", " << z << "> is not contained inside the block of this interpolator with AABB " << block_.getAABB() << " !"); + + const real_t dx = blockStorage_->dx( blockStorage_->getLevel( block_ ) ); + const real_t dy = blockStorage_->dy( blockStorage_->getLevel( block_ ) ); + const real_t dz = blockStorage_->dz( blockStorage_->getLevel( block_ ) ); + + Cell containingCell = blockStorage_->getBlockLocalCell( block_, x, y, z ); + Vector3<real_t> containingCellCenter = blockStorage_->getBlockLocalCellCenter( block_, containingCell ); + + const cell_idx_t xNeighbor1 = cell_idx_c( floor( x - containingCellCenter[0] ) ); + const cell_idx_t xNeighbor2 = xNeighbor1 + cell_idx_t(1); + const cell_idx_t yNeighbor1 = cell_idx_c( floor( y - containingCellCenter[1] ) ); + const cell_idx_t yNeighbor2 = yNeighbor1 + cell_idx_t(1); + const cell_idx_t zNeighbor1 = cell_idx_c( floor( z - containingCellCenter[2] ) ); + const cell_idx_t zNeighbor2 = zNeighbor1 + cell_idx_t(1); + + // define the 8 nearest cells required for the trilienar interpolation + // the cell 'ccc' is the one with the smallest x-, y-, and z-indices + Cell ccc( containingCell.x() + xNeighbor1, containingCell.y() + yNeighbor1, containingCell.z() + zNeighbor1 ); + Cell hcc( containingCell.x() + xNeighbor2, containingCell.y() + yNeighbor1, containingCell.z() + zNeighbor1 ); + Cell chc( containingCell.x() + xNeighbor1, containingCell.y() + yNeighbor2, containingCell.z() + zNeighbor1 ); + Cell hhc( containingCell.x() + xNeighbor2, containingCell.y() + yNeighbor2, containingCell.z() + zNeighbor1 ); + Cell cch( containingCell.x() + xNeighbor1, containingCell.y() + yNeighbor1, containingCell.z() + zNeighbor2 ); + Cell hch( containingCell.x() + xNeighbor2, containingCell.y() + yNeighbor1, containingCell.z() + zNeighbor2 ); + Cell chh( containingCell.x() + xNeighbor1, containingCell.y() + yNeighbor2, containingCell.z() + zNeighbor2 ); + Cell hhh( containingCell.x() + xNeighbor2, containingCell.y() + yNeighbor2, containingCell.z() + zNeighbor2 ); + + // check if all neighboring cells are domain cells + if( flagField_.isPartOfMaskSet( ccc, evaluationMask_ ) && + flagField_.isPartOfMaskSet( hcc, evaluationMask_ ) && + flagField_.isPartOfMaskSet( chc, evaluationMask_ ) && + flagField_.isPartOfMaskSet( hhc, evaluationMask_ ) && + flagField_.isPartOfMaskSet( cch, evaluationMask_ ) && + flagField_.isPartOfMaskSet( hch, evaluationMask_ ) && + flagField_.isPartOfMaskSet( chh, evaluationMask_ ) && + flagField_.isPartOfMaskSet( hhh, evaluationMask_ ) ) + { + // trilinear interpolation can be applied + + const real_t inv_totalVolume = real_t(1) / ( dx * dy * dz ); + Vector3<real_t> cccCellCenter = blockStorage_->getBlockLocalCellCenter( block_, ccc ); + + // weighting = volume of opposing volume element / total volume + real_t weighting(0.0); + // x: (c)---dxc--(X)--------------dxh-------------(h) + const real_t dxc = x - cccCellCenter[0]; + const real_t dxh = cccCellCenter[0] + dx - x; + const real_t dyc = y - cccCellCenter[1]; + const real_t dyh = cccCellCenter[1] + dy - y; + const real_t dzc = z - cccCellCenter[2]; + const real_t dzh = cccCellCenter[2] + dz - z; + + + weighting = dxh * dyh * dzh * inv_totalVolume; + addWeightedCellValue( interpolationResultBegin, ccc, weighting ); + + weighting = dxc * dyh * dzh * inv_totalVolume; + addWeightedCellValue( interpolationResultBegin, hcc, weighting ); + + weighting = dxh * dyc * dzh * inv_totalVolume; + addWeightedCellValue( interpolationResultBegin, chc, weighting ); + + weighting = dxc * dyc * dzh * inv_totalVolume; + addWeightedCellValue( interpolationResultBegin, hhc, weighting ); + + weighting = dxh * dyh * dzc * inv_totalVolume; + addWeightedCellValue( interpolationResultBegin, cch, weighting ); + + weighting = dxc * dyh * dzc * inv_totalVolume; + addWeightedCellValue( interpolationResultBegin, hch, weighting ); + + weighting = dxh * dyc * dzc * inv_totalVolume; + addWeightedCellValue( interpolationResultBegin, chh, weighting ); + + weighting = dxc * dyc * dzc * inv_totalVolume; + addWeightedCellValue( interpolationResultBegin, hhh, weighting ); + + } + else + { + // revert to nearest neighbor interpolation + nearestNeighborInterpolator_.get( x, y, z, interpolationResultBegin ); + } + } + +private: + + template< typename ForwardIterator_T > + void addWeightedCellValue( ForwardIterator_T interpolationResultBegin, const Cell & curCell, const real_t & weighting ) + { + for( uint_t f = uint_t(0); f < F_SIZE; ++f ) + { + WALBERLA_ASSERT( !math::isnan(baseField_( curCell, f)), "NaN found in component " << f << " when interpolating from cell " << curCell ); + + *interpolationResultBegin += weighting * baseField_( curCell, f); + ++interpolationResultBegin; + } + } + + shared_ptr<StructuredBlockStorage> blockStorage_; + const IBlock & block_; + const BaseField_T & baseField_; + const FlagField_T & flagField_; + flag_t evaluationMask_; + + NearestNeighborFieldInterpolator<Field_T,FlagField_T> nearestNeighborInterpolator_; + +}; + + +} // namespace field +} // namespace walberla diff --git a/src/field/interpolators/all.h b/src/field/interpolators/all.h index 0e7cfa80d337842b0320ea205f0dc109e63094a7..92055c83158120b94f671722f30d2e1d46b01ce4 100644 --- a/src/field/interpolators/all.h +++ b/src/field/interpolators/all.h @@ -24,3 +24,8 @@ #include "NearestNeighborInterpolator.h" #include "TrilinearInterpolator.h" + +#include "FieldInterpolatorCreators.h" +#include "KernelFieldInterpolator.h" +#include "NearestNeighborFieldInterpolator.h" +#include "TrilinearFieldInterpolator.h" diff --git a/tests/field/CMakeLists.txt b/tests/field/CMakeLists.txt index 4307644a0dcbbdd7d803f6a91d91b8e02e1c869f..2e490ea32b22d0266e8a7aac742be338758057be 100644 --- a/tests/field/CMakeLists.txt +++ b/tests/field/CMakeLists.txt @@ -25,6 +25,9 @@ waLBerla_execute_test( NAME FlagFieldTest ) waLBerla_compile_test( FILES interpolators/InterpolationTest.cpp) waLBerla_execute_test( NAME InterpolationTest ) +waLBerla_compile_test( FILES interpolators/FieldInterpolationTest.cpp) +waLBerla_execute_test( NAME FieldInterpolationTest ) + waLBerla_compile_test( FILES adaptors/AdaptorTest.cpp DEPENDS blockforest gui lbm ) waLBerla_execute_test( NAME AdaptorTest ) diff --git a/tests/field/interpolators/FieldInterpolationTest.cpp b/tests/field/interpolators/FieldInterpolationTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32adf2898cf57a2ccbc988d01d0f9de02c8ebfd9 --- /dev/null +++ b/tests/field/interpolators/FieldInterpolationTest.cpp @@ -0,0 +1,430 @@ +//====================================================================================================================== +// +// This file is part of waLBerla. waLBerla is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. +// +//! \file FieldInterpolationTest.cpp +//! \ingroup field +//! \author Christoph Rettinger <christoph.rettinger@fau.de> +// +//====================================================================================================================== + +#include "blockforest/all.h" + +#include "core/debug/TestSubsystem.h" +#include "core/mpi/Environment.h" +#include "core/math/all.h" + +#include "field/AddToStorage.h" +#include "field/FlagField.h" +#include "field/GhostLayerField.h" +#include "field/interpolators/all.h" + +#include <vector> + +using namespace walberla; + +namespace field_interpolation_tests { + +const uint_t FieldGhostLayers( 1 ); + +typedef walberla::uint8_t flag_t; +typedef FlagField< flag_t > FlagField_T; + +typedef GhostLayerField< real_t, 1> ScalarField_T; +typedef GhostLayerField< Vector3<real_t>, 1> Vec3Field_T; +typedef GhostLayerField< real_t, 3> MultiComponentField_T; + + +const FlagUID Domain_Flag ( "domain" ); +const FlagUID Boundary_Flag ( "boundary" ); + +void initFlagField( FlagField_T * field, IBlock * const /*block*/ ) +{ + auto domainFlag = field->getOrRegisterFlag( Domain_Flag ); + WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ( field, field->addFlag( x, y, z, domainFlag ); ); +} + +void initScalarField( const shared_ptr<StructuredBlockStorage> & blocks, const BlockDataID & scalarFieldID ) +{ + for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt ) + { + auto field = blockIt->getData<ScalarField_T>( scalarFieldID ); + WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ(field, + const Vector3<real_t> p = blocks->getBlockLocalCellCenter(*blockIt, Cell(x,y,z)) - Vector3<real_t>(real_t(0.5)); + //field->get(x,y,z) = real_t(2); + field->get(x,y,z) = p[0]; + ); + } +} + +void initVectorField( const shared_ptr<StructuredBlockStorage> & blocks, const BlockDataID & vectorFieldID ) +{ + for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt ) + { + auto field = blockIt->getData<Vec3Field_T>( vectorFieldID ); + WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ(field, + const Vector3<real_t> p = blocks->getBlockLocalCellCenter(*blockIt, Cell(x,y,z)) - Vector3<real_t>(real_t(0.5)); + field->get(x,y,z) = Vector3<real_t>(p[0], real_t(2), p[1]); + ); + } +} + +void initMultiComponentField( const shared_ptr<StructuredBlockStorage> & blocks, const BlockDataID & multiComponentFieldID ) +{ + for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt ) + { + auto field = blockIt->getData<MultiComponentField_T>( multiComponentFieldID ); + WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ(field, + const Vector3<real_t> p = blocks->getBlockLocalCellCenter(*blockIt, Cell(x,y,z)) - Vector3<real_t>(real_t(0.5)); + field->get(x,y,z,0) = p[0]; + field->get(x,y,z,1) = real_t(2); + field->get(x,y,z,2) = p[1]; + ); + } +} + +void setBoundaryFlags( const shared_ptr<StructuredBlockStorage> & blocks, + const BlockDataID & flagFieldID, const BlockDataID & scalarFieldID ) +{ + for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt ) + { + auto flagField = blockIt->getData<FlagField_T>( flagFieldID ); + auto valueField = blockIt->getData<ScalarField_T>( scalarFieldID ); + auto domainFlag = flagField->getOrRegisterFlag( Domain_Flag ); + auto boundaryFlag = flagField->getOrRegisterFlag( Boundary_Flag ); + WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ(flagField, + if( x == 2) + { + flagField->removeFlag(x,y,z,domainFlag); + flagField->addFlag(x,y,z,boundaryFlag); + valueField->get(x,y,z) = std::numeric_limits<real_t>::max(); + } + ); + } +} + +void testNearestNeighborFieldInterpolator( const shared_ptr<StructuredBlockStorage> & blocks, const BlockDataID & flagFieldID, + const BlockDataID & scalarFieldID, const BlockDataID & vectorFieldID, const BlockDataID & multiComponentFieldID ) +{ + // field interpolators + typedef field::NearestNeighborFieldInterpolator<ScalarField_T, FlagField_T> ScalarFieldInterpolator_T; + typedef field::NearestNeighborFieldInterpolator<Vec3Field_T, FlagField_T> Vec3FieldInterpolator_T; + typedef field::NearestNeighborFieldInterpolator<MultiComponentField_T, FlagField_T> MultiComponentFieldInterpolator_T; + BlockDataID scalarFieldInterpolatorID = field::addFieldInterpolator< ScalarFieldInterpolator_T, FlagField_T >( blocks, scalarFieldID, flagFieldID, Domain_Flag ); + BlockDataID vectorFieldInterpolatorID = field::addFieldInterpolator< Vec3FieldInterpolator_T, FlagField_T >( blocks, vectorFieldID, flagFieldID, Domain_Flag ); + BlockDataID multiComponentFieldInterpolatorID = field::addFieldInterpolator< MultiComponentFieldInterpolator_T, FlagField_T >( blocks, multiComponentFieldID, flagFieldID, Domain_Flag ); + + // check scalar interpolation + { + Vector3<real_t> interpolationPoint(real_t(1.9), real_t(2.1), real_t(2.1)); + auto containingBlockID = blocks->getBlock(interpolationPoint); + if( containingBlockID != NULL ) + { + real_t interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<ScalarFieldInterpolator_T>(scalarFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue, real_t(1), "NearestNeighborFieldInterpolator: Scalar interpolation failed"); + } + } + + // check vector interpolation + { + Vector3<real_t> interpolationPoint(real_t(5.4),real_t(2.1),real_t(3.2)); + auto containingBlockID = blocks->getBlock( interpolationPoint ); + if( containingBlockID != NULL ) { + Vector3<real_t> interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<Vec3FieldInterpolator_T>(vectorFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[0], real_t(5), "NearestNeighborFieldInterpolator: Vec3[0] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[1], real_t(2), "NearestNeighborFieldInterpolator: Vec3[1] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[2], real_t(2), "NearestNeighborFieldInterpolator: Vec3[2] interpolation failed"); + } + } + + // check multi component interpolation + { + Vector3<real_t> interpolationPoint(real_t(4.4),real_t(2.1),real_t(3.2)); + auto containingBlockID = blocks->getBlock( interpolationPoint ); + if( containingBlockID != NULL ) { + std::vector<real_t> interpolatedValue(3, real_t(0)); + auto interPtr = containingBlockID->getData<MultiComponentFieldInterpolator_T>(multiComponentFieldInterpolatorID); + interPtr->get(interpolationPoint, interpolatedValue.begin()); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[0], real_t(4), "NearestNeighborFieldInterpolator: Multi Component[0] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[1], real_t(2), "NearestNeighborFieldInterpolator: Multi Component[1] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[2], real_t(2), "NearestNeighborFieldInterpolator: Multi Component[2] interpolation failed"); + } + } +} + +void testTrilinearFieldInterpolator( const shared_ptr<StructuredBlockStorage> & blocks, const BlockDataID & flagFieldID, + const BlockDataID & scalarFieldID, const BlockDataID & vectorFieldID, const BlockDataID & multiComponentFieldID ) +{ + // field interpolators + typedef field::TrilinearFieldInterpolator<ScalarField_T, FlagField_T> ScalarFieldInterpolator_T; + typedef field::TrilinearFieldInterpolator<Vec3Field_T, FlagField_T> Vec3FieldInterpolator_T; + typedef field::TrilinearFieldInterpolator<MultiComponentField_T, FlagField_T> MultiComponentFieldInterpolator_T; + BlockDataID scalarFieldInterpolatorID = field::addFieldInterpolator< ScalarFieldInterpolator_T, FlagField_T >( blocks, scalarFieldID, flagFieldID, Domain_Flag ); + BlockDataID vectorFieldInterpolatorID = field::addFieldInterpolator< Vec3FieldInterpolator_T, FlagField_T >( blocks, vectorFieldID, flagFieldID, Domain_Flag ); + BlockDataID multiComponentFieldInterpolatorID = field::addFieldInterpolator< MultiComponentFieldInterpolator_T, FlagField_T >( blocks, multiComponentFieldID, flagFieldID, Domain_Flag ); + + // check scalar interpolation + { + Vector3<real_t> interpolationPoint(real_t(1.9), real_t(2.1), real_t(2.1)); + auto containingBlockID = blocks->getBlock(interpolationPoint); + if( containingBlockID != NULL ) + { + real_t interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<ScalarFieldInterpolator_T>(scalarFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue, real_t(1.4), "TrilinearFieldInterpolator: Scalar interpolation failed"); + } + } + + // check vector interpolation + { + Vector3<real_t> interpolationPoint(real_t(5.4),real_t(2.1),real_t(3.2)); + auto containingBlockID = blocks->getBlock( interpolationPoint ); + if( containingBlockID != NULL ) { + Vector3<real_t> interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<Vec3FieldInterpolator_T>(vectorFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[0], real_t(4.9), "TrilinearFieldInterpolator: Vec3[0] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[1], real_t(2.0), "TrilinearFieldInterpolator: Vec3[1] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[2], real_t(1.6), "TrilinearFieldInterpolator: Vec3[2] interpolation failed"); + } + } + + // check multi component interpolation + { + Vector3<real_t> interpolationPoint(real_t(4.4),real_t(2.1),real_t(3.2)); + auto containingBlockID = blocks->getBlock( interpolationPoint ); + if( containingBlockID != NULL ) { + std::vector<real_t> interpolatedValue(3, real_t(0)); + auto interPtr = containingBlockID->getData<MultiComponentFieldInterpolator_T>(multiComponentFieldInterpolatorID); + interPtr->get(interpolationPoint, interpolatedValue.begin()); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[0], real_t(3.9), "TrilinearFieldInterpolator: Multi Component[0] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[1], real_t(2.0), "TrilinearFieldInterpolator: Multi Component[1] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[2], real_t(1.6), "TrilinearFieldInterpolator: Multi Component[2] interpolation failed"); + } + } +} + +void testKernelFieldInterpolator( const shared_ptr<StructuredBlockStorage> & blocks, const BlockDataID & flagFieldID, + const BlockDataID & scalarFieldID, const BlockDataID & vectorFieldID, const BlockDataID & multiComponentFieldID ) +{ + // field interpolators + typedef field::KernelFieldInterpolator<ScalarField_T, FlagField_T> ScalarFieldInterpolator_T; + typedef field::KernelFieldInterpolator<Vec3Field_T, FlagField_T> Vec3FieldInterpolator_T; + typedef field::KernelFieldInterpolator<MultiComponentField_T, FlagField_T> MultiComponentFieldInterpolator_T; + BlockDataID scalarFieldInterpolatorID = field::addFieldInterpolator< ScalarFieldInterpolator_T, FlagField_T >( blocks, scalarFieldID, flagFieldID, Domain_Flag ); + BlockDataID vectorFieldInterpolatorID = field::addFieldInterpolator< Vec3FieldInterpolator_T, FlagField_T >( blocks, vectorFieldID, flagFieldID, Domain_Flag ); + BlockDataID multiComponentFieldInterpolatorID = field::addFieldInterpolator< MultiComponentFieldInterpolator_T, FlagField_T >( blocks, multiComponentFieldID, flagFieldID, Domain_Flag ); + + // check scalar interpolation + { + Vector3<real_t> interpolationPoint(real_t(1.9), real_t(2.1), real_t(2.1)); + auto containingBlockID = blocks->getBlock(interpolationPoint); + if( containingBlockID != NULL ) + { + real_t interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<ScalarFieldInterpolator_T>(scalarFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue, real_t(1.4), "KernelFieldInterpolator: Scalar interpolation failed"); + } + } + + // check vector interpolation + { + Vector3<real_t> interpolationPoint(real_t(5.4),real_t(2.1),real_t(3.2)); + auto containingBlockID = blocks->getBlock( interpolationPoint ); + if( containingBlockID != NULL ) { + Vector3<real_t> interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<Vec3FieldInterpolator_T>(vectorFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[0], real_t(4.9), "KernelFieldInterpolator: Vec3[0] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[1], real_t(2.0), "KernelFieldInterpolator: Vec3[1] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[2], real_t(1.6), "KernelFieldInterpolator: Vec3[2] interpolation failed"); + } + } + + // check multi component interpolation + { + Vector3<real_t> interpolationPoint(real_t(4.4),real_t(2.1),real_t(3.2)); + auto containingBlockID = blocks->getBlock( interpolationPoint ); + if( containingBlockID != NULL ) { + std::vector<real_t> interpolatedValue(3, real_t(0)); + auto interPtr = containingBlockID->getData<MultiComponentFieldInterpolator_T>(multiComponentFieldInterpolatorID); + interPtr->get(interpolationPoint, interpolatedValue.begin()); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[0], real_t(3.9), "KernelFieldInterpolator: Multi Component[0] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[1], real_t(2.0), "KernelFieldInterpolator: Multi Component[1] interpolation failed"); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue[2], real_t(1.6), "KernelFieldInterpolator: Multi Component[2] interpolation failed"); + } + } +} + + +void testNearestNeighborFieldInterpolatorAtBoundary( const shared_ptr<StructuredBlockStorage> & blocks, + const BlockDataID & flagFieldID, const BlockDataID & scalarFieldID ) { + // field interpolators + typedef field::NearestNeighborFieldInterpolator<ScalarField_T, FlagField_T> ScalarFieldInterpolator_T; + BlockDataID scalarFieldInterpolatorID = field::addFieldInterpolator<ScalarFieldInterpolator_T, FlagField_T>(blocks, scalarFieldID, flagFieldID, Domain_Flag); + + // check scalar interpolation close to boundary + { + Vector3<real_t> interpolationPoint(real_t(1.9), real_t(2.1), real_t(2.1)); + auto containingBlockID = blocks->getBlock(interpolationPoint); + if (containingBlockID != NULL) { + real_t interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<ScalarFieldInterpolator_T>(scalarFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue, real_t(1), + "NearestNeighborFieldInterpolator: Scalar interpolation near boundary failed"); + } + } + + // check scalar interpolation inside boundary + { + Vector3<real_t> interpolationPoint(real_t(2.7), real_t(2.1), real_t(1.1)); + auto containingBlockID = blocks->getBlock(interpolationPoint); + if (containingBlockID != NULL) { + real_t interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<ScalarFieldInterpolator_T>(scalarFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue, real_t(3), + "NearestNeighborFieldInterpolator: Scalar interpolation inside boundary failed"); + } + } +} + +void testTrilinearFieldInterpolatorAtBoundary( const shared_ptr<StructuredBlockStorage> & blocks, + const BlockDataID & flagFieldID, const BlockDataID & scalarFieldID ) { + // field interpolators + typedef field::TrilinearFieldInterpolator<ScalarField_T, FlagField_T> ScalarFieldInterpolator_T; + BlockDataID scalarFieldInterpolatorID = field::addFieldInterpolator<ScalarFieldInterpolator_T, FlagField_T>(blocks, scalarFieldID, flagFieldID, Domain_Flag); + + // check scalar interpolation close to boundary + { + Vector3<real_t> interpolationPoint(real_t(1.9), real_t(2.1), real_t(2.1)); + auto containingBlockID = blocks->getBlock(interpolationPoint); + if (containingBlockID != NULL) { + real_t interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<ScalarFieldInterpolator_T>(scalarFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue, real_t(1), + "TrilinearFieldInterpolator: Scalar interpolation near boundary failed"); + } + } + + // check scalar interpolation inside boundary + { + Vector3<real_t> interpolationPoint(real_t(2.7), real_t(2.1), real_t(1.1)); + auto containingBlockID = blocks->getBlock(interpolationPoint); + if (containingBlockID != NULL) { + real_t interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<ScalarFieldInterpolator_T>(scalarFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + WALBERLA_CHECK_FLOAT_EQUAL(interpolatedValue, real_t(3), + "TrilinearFieldInterpolator: Scalar interpolation inside boundary failed"); + } + } +} + +void testKernelFieldInterpolatorAtBoundary( const shared_ptr<StructuredBlockStorage> & blocks, + const BlockDataID & flagFieldID, const BlockDataID & scalarFieldID ) { + // field interpolators + typedef field::KernelFieldInterpolator<ScalarField_T, FlagField_T> ScalarFieldInterpolator_T; + BlockDataID scalarFieldInterpolatorID = field::addFieldInterpolator<ScalarFieldInterpolator_T, FlagField_T>(blocks, scalarFieldID, flagFieldID, Domain_Flag); + + // check scalar interpolation close to boundary + { + Vector3<real_t> interpolationPoint(real_t(1.9), real_t(2.1), real_t(2.1)); + auto containingBlockID = blocks->getBlock(interpolationPoint); + if (containingBlockID != NULL) { + real_t interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<ScalarFieldInterpolator_T>(scalarFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + // kernel interpolation can not extrapolate values from the available ones (see comments in KernelFieldInterpolator.h) + // it will thus yield a value between the available ones, which are 0 and 1 + WALBERLA_CHECK(interpolatedValue < real_t(1), + "KernelFieldInterpolator: Scalar interpolation near boundary failed"); + WALBERLA_CHECK(interpolatedValue > real_t(0), + "KernelFieldInterpolator: Scalar interpolation near boundary failed"); + } + } + + // check scalar interpolation inside boundary + { + Vector3<real_t> interpolationPoint(real_t(2.7), real_t(2.1), real_t(1.1)); + auto containingBlockID = blocks->getBlock(interpolationPoint); + if (containingBlockID != NULL) { + real_t interpolatedValue(real_t(0)); + auto interPtr = containingBlockID->getData<ScalarFieldInterpolator_T>(scalarFieldInterpolatorID); + interPtr->get(interpolationPoint, &interpolatedValue); + // values has to be between the available ones, i.e. 1 and 3 + WALBERLA_CHECK(interpolatedValue > real_t(1), + "KernelFieldInterpolator: Scalar interpolation inside boundary failed"); + WALBERLA_CHECK(interpolatedValue < real_t(3), + "KernelFieldInterpolator: Scalar interpolation inside boundary failed"); + } + } +} + +int main(int argc, char **argv) { + + mpi::Environment mpiEnv(argc, argv); + debug::enterTestMode(); + + const uint_t numberOfBlocksInDirection = 2; + const uint_t numberOfCellsPerBlockInDirection = 4; + const real_t dx = real_t(1); + + // block storage + auto blocks = blockforest::createUniformBlockGrid( numberOfBlocksInDirection, numberOfBlocksInDirection, numberOfBlocksInDirection, + numberOfCellsPerBlockInDirection, numberOfCellsPerBlockInDirection, numberOfCellsPerBlockInDirection, + dx, 0, false, false, + false, false, false, + false ); + // flag field + BlockDataID flagFieldID = field::addFlagFieldToStorage<FlagField_T>( blocks, "flag field", FieldGhostLayers, false, initFlagField ); + + // data fields + BlockDataID scalarFieldID = field::addToStorage< ScalarField_T >( blocks, "scalar field", real_t(0), field::zyxf, FieldGhostLayers ); + BlockDataID vectorFieldID = field::addToStorage< Vec3Field_T >( blocks, "vec3 field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers ); + BlockDataID multiComponentFieldID = field::addToStorage< MultiComponentField_T >( blocks, "multi component field", real_t(0), field::zyxf, FieldGhostLayers ); + + initScalarField(blocks, scalarFieldID); + initVectorField(blocks, vectorFieldID ); + initMultiComponentField(blocks, multiComponentFieldID ); + + // test all interpolators with domain flags everywhere, i.e. without special boundary treatment necessary + testNearestNeighborFieldInterpolator(blocks, flagFieldID, scalarFieldID, vectorFieldID, multiComponentFieldID); + testTrilinearFieldInterpolator(blocks, flagFieldID, scalarFieldID, vectorFieldID, multiComponentFieldID); + testKernelFieldInterpolator(blocks, flagFieldID, scalarFieldID, vectorFieldID, multiComponentFieldID); + + // set some boundary flags in flag field and invalidate the corresponding scalar field values + setBoundaryFlags(blocks, flagFieldID, scalarFieldID); + + // test all interpolators' behavior close to boundary cells + testNearestNeighborFieldInterpolatorAtBoundary(blocks, flagFieldID, scalarFieldID); + testTrilinearFieldInterpolatorAtBoundary(blocks, flagFieldID, scalarFieldID); + testKernelFieldInterpolatorAtBoundary(blocks, flagFieldID, scalarFieldID); + + return 0; +} + +} // namespace field_interpolation_tests + +int main( int argc, char **argv ){ + field_interpolation_tests::main(argc, argv); +} \ No newline at end of file