//======================================================================================================================
//
// 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 .
//
//! \file Field.impl.h
//! \ingroup field
//! \author Martin Bauer
//! \brief Definitions of Field members
//
//======================================================================================================================
#include "field/iterators/IteratorMacros.h"
#include "core/math/Utility.h" // for equal() in operator==
#include // std::copy
namespace walberla {
namespace field {
//===================================================================================================================
//
// CONSTRUCTION
//
//===================================================================================================================
//*******************************************************************************************************************
/*!Creates an uninitialized field of size zero (no allocated memory)
*
* This field has to be initialized before it can be used using the init() method
*******************************************************************************************************************/
template
Field::Field( )
: values_( nullptr ), valuesWithOffset_( nullptr ),
xSize_(0), ySize_(0), zSize_(0),
xAllocSize_(0), yAllocSize_(0), zAllocSize_(0), fAllocSize_(0)
{
}
//*******************************************************************************************************************
/*!Creates an uninitialized field of given size
*
* \param xSize size of x dimension
* \param ySize size of y dimension
* \param zSize size of z dimension
* \param layout memory layout of the field (see Field::Layout)
* \param alloc class that describes how to allocate memory for the field, see FieldAllocator
*******************************************************************************************************************/
template
Field::Field( uint_t _xSize, uint_t _ySize, uint_t _zSize, const Layout & l,
const shared_ptr > &alloc )
: values_( nullptr ), valuesWithOffset_( nullptr )
{
init(_xSize,_ySize,_zSize,l,alloc);
}
//*******************************************************************************************************************
/*! Creates a field and initializes it with constant
*
* \param xSize size of x dimension
* \param ySize size of y dimension
* \param zSize size of z dimension
* \param initVal every element of the field is set to initVal
* \param layout memory layout of the field (see Field::Layout)
* \param alloc class that describes how to allocate memory for the field, see FieldAllocator
*******************************************************************************************************************/
template
Field::Field( uint_t _xSize, uint_t _ySize, uint_t _zSize, const T & initVal, const Layout & l,
const shared_ptr > &alloc )
: values_( nullptr ), valuesWithOffset_( nullptr )
{
init(_xSize,_ySize,_zSize,l,alloc);
set(initVal);
}
//*******************************************************************************************************************
/*! Creates a field and initializes f coordinate with vector values
*
* \param xSize size of x dimension
* \param ySize size of y dimension
* \param zSize size of z dimension
* \param fValues initializes f coordinate with values from vector (see set(std::vector&) )
* \param layout memory layout of the field (see Field::Layout)
* \param alloc class that describes how to allocate memory for the field, see FieldAllocator
*******************************************************************************************************************/
template
Field::Field( uint_t _xSize, uint_t _ySize, uint_t _zSize,
const std::vector & fValues, const Layout & l,
const shared_ptr > &alloc)
: values_( NULL ), valuesWithOffset_( NULL )
{
init(_xSize,_ySize,_zSize,l,alloc);
set(fValues);
}
//*******************************************************************************************************************
/*! Deletes all stored data, and resizes the field
*
* The resized field is uninitialized.
*******************************************************************************************************************/
template
void Field::resize( uint_t _xSize, uint_t _ySize, uint_t _zSize )
{
if ( _xSize == xSize_ && _ySize == ySize_ && _zSize == zSize_ )
return;
allocator_->decrementReferenceCount( values_ );
values_ = nullptr;
valuesWithOffset_ = nullptr;
init( _xSize, _ySize, _zSize, layout_, allocator_ );
}
//*******************************************************************************************************************
/*! Returns a shallow copy of the current field.
*
* Shallow copy means, that the new field internally uses the same memory as this field.
*
* \return a new field, that has to be freed by caller
*******************************************************************************************************************/
template
Field * Field::cloneShallowCopy() const
{
return cloneShallowCopyInternal() ;
}
//*******************************************************************************************************************
/*! Returns a flattened shallow copy of the current field.
*
* Shallow copy means, that the new field internally uses the same memory as this field.
* Flattened means that any VectorTrait-compatible containers are absorbed into the fSize.
*
* \return a new field, that has to be freed by caller
*******************************************************************************************************************/
template
typename Field::FlattenedField * Field::flattenedShallowCopy() const
{
return flattenedShallowCopyInternal();
}
//*******************************************************************************************************************
/*!\brief Does the same as cloneShallowCopy (but is virtual)
*
* This version has to be implemented by derived classes. The cloneShallowCopy() itself cannot be
* virtual, since the implementation of cloneShallowCopy() of derived classes has a different signature.
*
*******************************************************************************************************************/
template
Field * Field::cloneShallowCopyInternal() const
{
return new Field(*this) ;
}
//*******************************************************************************************************************
/*!\brief Does the same as flattenedShallowCopy (but is virtual)
*
* This version has to be implemented by derived classes. The flattenedShallowCopy() itself cannot be
* virtual, since the implementation of flattenedShallowCopy() of derived classes has a different signature.
*
*******************************************************************************************************************/
template
typename Field::FlattenedField * Field::flattenedShallowCopyInternal() const
{
return new FlattenedField(*this) ;
}
//*******************************************************************************************************************
/*! Creates a new field that has equal size and layout as this field. The memory of the new
* field is uninitialized.
*
* \return a new field, that has to be freed by caller
*******************************************************************************************************************/
template
Field * Field::cloneUninitialized() const
{
Field * res = cloneShallowCopy();
res->allocator_->decrementReferenceCount( res->values_ );
res->values_ = res->allocator_->allocate ( res->allocSize() );
const auto offset = res->xOff_*res->xfact_+ res->yOff_*res->yfact_+ res->zOff_*res->zfact_;
res->valuesWithOffset_ = res->values_ + offset;
WALBERLA_ASSERT ( hasSameSize ( *res ) );
WALBERLA_ASSERT ( hasSameAllocSize( *res ) );
return res;
}
//*******************************************************************************************************************
/*! Returns a deep copy of the current field. The data is copied over.
*
* \return a new field, that has to be freed by caller
*******************************************************************************************************************/
template
Field * Field::clone() const
{
Field * res = cloneUninitialized();
WALBERLA_ASSERT_EQUAL ( allocSize_, res->allocSize_ );
std::copy( values_, values_ + allocSize_, res->values_ );
return res;
}
//*******************************************************************************************************************
/*! Private copy constructor that creates a shallow copy
* i.e. reuses the memory of the copied field
*******************************************************************************************************************/
template
Field::Field( const Field & other )
: values_ ( other.values_ ),
valuesWithOffset_ ( other.valuesWithOffset_ ),
xOff_ ( other.xOff_),
yOff_ ( other.yOff_),
zOff_ ( other.zOff_),
xSize_ ( other.xSize_ ),
ySize_ ( other.ySize_ ),
zSize_ ( other.zSize_ ),
xAllocSize_ ( other.xAllocSize_ ),
yAllocSize_ ( other.yAllocSize_ ),
zAllocSize_ ( other.zAllocSize_ ),
fAllocSize_ ( other.fAllocSize_ ),
layout_ ( other.layout_ ),
allocSize_ ( other.allocSize_ ),
ffact_ ( other.ffact_ ),
xfact_ ( other.xfact_ ),
yfact_ ( other.yfact_ ),
zfact_ ( other.zfact_ ),
allocator_ ( other.allocator_ )
{
allocator_->incrementReferenceCount ( values_ );
}
//*******************************************************************************************************************
/*! Private copy constructor that creates a flattened shallow copy
* i.e. reuses the memory of the copied field
*******************************************************************************************************************/
template
template
Field::Field( const Field & other )
: values_ ( other.values_[0].data() ),
valuesWithOffset_ ( other.valuesWithOffset_[0].data() ),
xOff_ ( other.xOff_),
yOff_ ( other.yOff_),
zOff_ ( other.zOff_),
xSize_ ( other.xSize_ ),
ySize_ ( other.ySize_ ),
zSize_ ( other.zSize_ ),
xAllocSize_ ( other.xAllocSize_ ),
yAllocSize_ ( other.yAllocSize_ ),
zAllocSize_ ( other.zAllocSize_ ),
fAllocSize_ ( other.fAllocSize_*fSize_/fSize2 ),
layout_ ( other.layout_ ),
allocSize_ ( other.allocSize_*fSize_/fSize2 ),
ffact_ ( other.ffact_ ),
xfact_ ( other.xfact_*cell_idx_t(fSize_/fSize2) ),
yfact_ ( other.yfact_*cell_idx_t(fSize_/fSize2) ),
zfact_ ( other.zfact_*cell_idx_t(fSize_/fSize2) ),
allocator_ ( std::shared_ptr>(other.allocator_, reinterpret_cast*>(other.allocator_.get())) )
{
WALBERLA_CHECK_EQUAL(layout_, Layout::zyxf);
static_assert(fSize_ % fSize2 == 0, "number of field components do not match");
static_assert(std::is_same::FlattenedField, Field>::value, "field types are incompatible for flattening");
allocator_->incrementReferenceCount ( values_ );
}
//*******************************************************************************************************************
/*! Initializes the field with a given size, in a given layout
*
* Must be called exactly once! This is automatically called by all constructors
* that take at least one argument
*
* \param xSize size of x dimension
* \param ySize size of y dimension
* \param zSize size of z dimension
* \param layout memory layout of the field (see Field::Layout)
* \param alloc the allocator to use. If a NULL shared pointer is given, a sensible default is selected,
* depending on layout
* \param innerGhostLayerSizeForAlignedAlloc
* This parameter should be set to zero for field that have no ghost layers.
* This parameter is passed to the allocator and can there be used to ensure
* alignment of the first INNER cell in each line
*******************************************************************************************************************/
template
void Field::init( uint_t _xSize, uint_t _ySize, uint_t _zSize,
const Layout & l, shared_ptr > alloc,
uint_t innerGhostLayerSizeForAlignedAlloc )
{
WALBERLA_ASSERT_NULLPTR( values_ );
WALBERLA_ASSERT_NULLPTR( valuesWithOffset_ );
// Automatically select allocator if none was given
if ( alloc == nullptr )
{
#if defined(__ARM_FEATURE_SVE) && defined(__ARM_FEATURE_SVE_BITS) && __ARM_FEATURE_SVE_BITS > 0
const uint_t alignment = __ARM_FEATURE_SVE_BITS/8;
#elif defined(__ARM_FEATURE_SVE)
const uint_t alignment = 64;
#elif defined(__ARM_NEON)
const uint_t alignment = 16;
#elif defined(__AVX512F__)
const uint_t alignment = 64;
#elif defined(__AVX__)
const uint_t alignment = 32;
#elif defined(__SSE__) || defined(_MSC_VER)
const uint_t alignment = 16;
#elif defined(__BIGGEST_ALIGNMENT__)
const uint_t alignment = __BIGGEST_ALIGNMENT__;
#else
const uint_t alignment = 64;
#endif
// aligned allocator only used (by default) if ...
if ( l == fzyx && // ... we use a structure of arrays layout
_xSize * sizeof(T) > alignment && // ... the inner coordinate is sufficiently large
sizeof(T) < alignment && // ... the stored data type is smaller than the alignment
alignment % sizeof(T) == 0 ) // ... there is an integer number of elements fitting in one aligned line
alloc = make_shared >();
else
alloc = make_shared > ();
}
allocator_ = alloc;
allocator_->setInnerGhostLayerSize( innerGhostLayerSizeForAlignedAlloc );
values_ = nullptr;
xSize_ = _xSize;
ySize_ = _ySize;
zSize_ = _zSize;
xAllocSize_ = yAllocSize_ = zAllocSize_ = fAllocSize_ = 0; // is set in alloc->allocate()
layout_ = l;
WALBERLA_ASSERT(layout_ == zyxf || layout_ == fzyx);
if (layout_ == fzyx ) {
values_ = allocator_->allocate(fSize_, zSize_, ySize_, xSize_, zAllocSize_, yAllocSize_, xAllocSize_);
fAllocSize_ = fSize_;
WALBERLA_CHECK_LESS_EQUAL( fSize_ * xAllocSize_ * yAllocSize_ * zAllocSize_ + xSize_ + ySize_ * xAllocSize_ + zSize_ * xAllocSize_ * yAllocSize_,
std::numeric_limits< cell_idx_t >::max(),
"The data type 'cell_idx_t' is too small for your field size! Your field is too large.\nYou may have to set 'cell_idx_t' to an 'int64_t'." );
ffact_ = cell_idx_c(xAllocSize_ * yAllocSize_ * zAllocSize_);
zfact_ = cell_idx_c(xAllocSize_ * yAllocSize_);
yfact_ = cell_idx_c(xAllocSize_);
xfact_ = 1;
} else {
values_ = allocator_->allocate(zSize_, ySize_, xSize_, fSize_, yAllocSize_, xAllocSize_, fAllocSize_);
zAllocSize_ = zSize_;
WALBERLA_CHECK_LESS_EQUAL( fSize_ + xSize_ * fAllocSize_ + ySize_ * fAllocSize_ * xAllocSize_ + zSize_ * fAllocSize_ * xAllocSize_ * yAllocSize_,
std::numeric_limits< cell_idx_t >::max(),
"The data type 'cell_idx_t' is too small for your field size! Your field is too large.\nYou may have to set 'cell_idx_t' to an 'int64_t'." );
zfact_ = cell_idx_c(fAllocSize_ * xAllocSize_ * yAllocSize_);
yfact_ = cell_idx_c(fAllocSize_ * xAllocSize_);
xfact_ = cell_idx_c(fAllocSize_);
ffact_ = 1;
}
WALBERLA_ASSERT(xAllocSize_ >= xSize_);
WALBERLA_ASSERT(yAllocSize_ >= ySize_);
WALBERLA_ASSERT(zAllocSize_ >= zSize_);
allocSize_ = fAllocSize_ * xAllocSize_ * yAllocSize_ * zAllocSize_;
xOff_ = yOff_ = zOff_ = 0;
valuesWithOffset_ = values_;
}
//*******************************************************************************************************************
/*! Destructor, using Allocator template parameter
*******************************************************************************************************************/
template
Field::~Field()
{
allocator_->decrementReferenceCount( values_ );
}
//===================================================================================================================
//
// ITERATORS
//
//===================================================================================================================
//*******************************************************************************************************************
/*! Returns iterator, which can iterate over complete field in a suitable order depending on layout
*
* Use this when iterating over a complete field, faster than 4 nested loops and operator() calls in the
* innermost loop because no index calculations have to be done.
*
*******************************************************************************************************************/
template
inline typename Field::iterator Field::begin()
{
return iterator( this,0,0,0,0, xSize(), ySize(), zSize(), fSize() );
}
//*******************************************************************************************************************
/*! Returns const_iterator, see begin()
*******************************************************************************************************************/
template
inline typename Field::const_iterator Field::begin() const
{
return const_iterator ( const_cast< Field * >(this), 0,0,0,0,
xSize(), ySize(), zSize(), fSize() );
}
//*******************************************************************************************************************
/*! Returns iterator which iterates over a sub-block of the field
*
* - Iterator over block defined by ( xBeg <= x < xEnd, yBeg <= y < yEnd, ....)
* - layout aware
*******************************************************************************************************************/
template
inline typename Field::iterator
Field::beginSlice( cell_idx_t xBeg, cell_idx_t yBeg, cell_idx_t zBeg, cell_idx_t fBeg,
cell_idx_t xEnd, cell_idx_t yEnd, cell_idx_t zEnd, cell_idx_t fEnd )
{
WALBERLA_ASSERT_LESS( xBeg, xEnd );
WALBERLA_ASSERT_LESS( yBeg, yEnd );
WALBERLA_ASSERT_LESS( zBeg, zEnd );
assertValidCoordinates( xBeg , yBeg , zBeg , fBeg );
assertValidCoordinates( xEnd-1, yEnd-1, zEnd-1, fEnd-1 ); // -1 since end points behind valid coordinates
return iterator( this, xBeg, yBeg, zBeg, fBeg, uint_c(xEnd-xBeg), uint_c(yEnd-yBeg), uint_c(zEnd-zBeg), uint_c(fEnd-fBeg) );
}
//*******************************************************************************************************************
/*! Const variant of beginSlice()
*******************************************************************************************************************/
template
inline typename Field::const_iterator
Field::beginSlice( cell_idx_t xBeg, cell_idx_t yBeg, cell_idx_t zBeg, cell_idx_t fBeg,
cell_idx_t xEnd, cell_idx_t yEnd, cell_idx_t zEnd, cell_idx_t fEnd ) const
{
WALBERLA_ASSERT_LESS( xBeg, xEnd);
WALBERLA_ASSERT_LESS( yBeg, yEnd);
WALBERLA_ASSERT_LESS( zBeg, zEnd);
assertValidCoordinates( xBeg , yBeg , zBeg , fBeg );
assertValidCoordinates( xEnd-1, yEnd-1, zEnd-1, fEnd-1 ); // -1 since end points behind valid coordinates
return const_iterator( this, xBeg, yBeg, zBeg, fBeg, uint_c( xEnd-xBeg ), uint_c( yEnd-yBeg ), uint_c( zEnd-zBeg ), uint_c( fEnd-fBeg ) );
}
//*******************************************************************************************************************
/*! Returns iterator which iterates over a slice, but only in x,y,z coordinates.
* \param f fixed value of f coordinate, where iterator points to in each cell
*
*******************************************************************************************************************/
template
inline typename Field::iterator
Field::beginSliceXYZ( const CellInterval & ci, cell_idx_t f )
{
return ci.empty() ? end() : iterator( this, ci.xMin(), ci.yMin(), ci.zMin(), f,
ci.xSize(), ci.ySize(), ci.zSize(), 1 );
}
//*******************************************************************************************************************
/*! Const variant of beginSliceXYZ()
*******************************************************************************************************************/
template
inline typename Field::const_iterator
Field::beginSliceXYZ ( const CellInterval & ci, cell_idx_t f ) const
{
return ci.empty() ? end() : const_iterator( this, ci.xMin(), ci.yMin(), ci.zMin(), f,
ci.xSize(), ci.ySize(), ci.zSize(), 1 );
}
//*******************************************************************************************************************
/*! Iterates only over XYZ coordinate, f is always 0
*******************************************************************************************************************/
template
inline typename Field::iterator
Field::beginXYZ()
{
return iterator( this, 0,0,0,0, xSize(), ySize(), zSize(), 1 );
}
//*******************************************************************************************************************
/*! Const version of beginXYZ()
*******************************************************************************************************************/
template
inline typename Field::const_iterator
Field::beginXYZ() const
{
return const_iterator( this, 0,0,0,0, xSize(), ySize(), zSize(), 1 );
}
//*******************************************************************************************************************
/*! End iterator, can be used with begin() and beginBlock()
*******************************************************************************************************************/
template
const ForwardFieldIterator Field::staticEnd = ForwardFieldIterator();
template
inline const typename Field::iterator & Field::end()
{
return staticEnd;
}
//*******************************************************************************************************************
/*! Const end iterator, see end()
*******************************************************************************************************************/
template
const ForwardFieldIterator Field::staticConstEnd = ForwardFieldIterator();
template
inline const typename Field::const_iterator & Field::end() const
{
return staticConstEnd;
}
//===================================================================================================================
//
// REVERSE ITERATION
//
//===================================================================================================================
//*******************************************************************************************************************
/*! Returns reverse iterator, which can iterate over complete field in a suitable order depending on layout
*******************************************************************************************************************/
template
inline typename Field::reverse_iterator Field::rbegin()
{
return reverse_iterator( this,0,0,0,0, xSize(), ySize(), zSize(), fSize() );
}
//*******************************************************************************************************************
/*! Returns const_reverse_iterator, see begin()
*******************************************************************************************************************/
template
inline typename Field::const_reverse_iterator Field::rbegin() const
{
return const_reverse_iterator ( this, 0,0,0,0, xSize(), ySize(), zSize(), fSize() );
}
//*******************************************************************************************************************
/*! Iterates only over XYZ coordinate, f is always 0
*******************************************************************************************************************/
template
inline typename Field::reverse_iterator
Field::rbeginXYZ()
{
return reverse_iterator( this, 0,0,0, 0, xSize() , ySize() , zSize() , 1 );
}
//*******************************************************************************************************************
/*! Const version of beginXYZ()
*******************************************************************************************************************/
template
inline typename Field::const_reverse_iterator
Field::rbeginXYZ() const
{
return const_reverse_iterator( this, 0,0,0, 0, xSize() , ySize() , zSize() , 1 );
}
//*******************************************************************************************************************
/*! End iterator, can be used with begin() and beginBlock()
*******************************************************************************************************************/
template
const ReverseFieldIterator Field::staticREnd = ReverseFieldIterator();
template
inline const typename Field::reverse_iterator & Field::rend()
{
return staticREnd;
}
//*******************************************************************************************************************
/*! Const end iterator, see end()
*******************************************************************************************************************/
template
const ReverseFieldIterator Field::staticConstREnd = ReverseFieldIterator();
template
inline const typename Field::const_reverse_iterator & Field::rend() const
{
return staticConstREnd;
}
//===================================================================================================================
//
// SIZE INFORMATION
//
//===================================================================================================================
template
inline CellInterval Field::xyzSize() const
{
return CellInterval (0,0,0, cell_idx_c( xSize() )-1,
cell_idx_c( ySize() )-1,
cell_idx_c( zSize() )-1 );
}
template
inline CellInterval Field::xyzAllocSize() const
{
return CellInterval( -xOff_,
-yOff_,
-zOff_,
cell_idx_c( xAllocSize() ) - xOff_-1,
cell_idx_c( yAllocSize() ) - yOff_-1,
cell_idx_c( zAllocSize() ) - zOff_-1 );
}
template
inline uint_t Field::size( uint_t coord ) const
{
switch (coord) {
case 0: return this->xSize();
case 1: return this->ySize();
case 2: return this->zSize();
case 3: return this->fSize();
default: WALBERLA_ASSERT(false); return 0;
}
}
//===================================================================================================================
//
// ELEMENT ACCESS
//
//===================================================================================================================
#ifndef NDEBUG
template
void Field::assertValidCoordinates( cell_idx_t x, cell_idx_t y, cell_idx_t z, cell_idx_t f ) const
{
//Bounds checks
const cell_idx_t xEff = xOff_ + x;
const cell_idx_t yEff = yOff_ + y;
const cell_idx_t zEff = zOff_ + z;
WALBERLA_ASSERT_GREATER_EQUAL( xEff, 0, "Field access out of bounds: x too small: " << x << " < " << - xOff_ );
WALBERLA_ASSERT_GREATER_EQUAL( yEff, 0, "Field access out of bounds: y too small: " << y << " < " << - yOff_ );
WALBERLA_ASSERT_GREATER_EQUAL( zEff, 0, "Field access out of bounds: z too small: " << z << " < " << - zOff_ );
WALBERLA_ASSERT_GREATER_EQUAL( f, 0, "Field access out of bounds: f too small: " << f << " < " << 0 );
WALBERLA_ASSERT_LESS( xEff, cell_idx_c( xAllocSize() ), "Field access out of bounds: x too big: " << x << " >= " << (xAllocSize() - uint_c(xOff_)) );
WALBERLA_ASSERT_LESS( yEff, cell_idx_c( yAllocSize() ), "Field access out of bounds: y too big: " << y << " >= " << (yAllocSize() - uint_c(yOff_)) );
WALBERLA_ASSERT_LESS( zEff, cell_idx_c( zAllocSize() ), "Field access out of bounds: z too big: " << z << " >= " << (zAllocSize() - uint_c(zOff_)) );
WALBERLA_ASSERT_LESS( f, cell_idx_c( fSize_) , "Field access out of bounds: f too big: " << f << " >= " << fSize_ );
}
#else
template
void Field::assertValidCoordinates( cell_idx_t, cell_idx_t, cell_idx_t, cell_idx_t ) const
{ }
#endif
template
bool Field::coordinatesValid( cell_idx_t x, cell_idx_t y, cell_idx_t z, cell_idx_t f ) const
{
//Bounds checks
const cell_idx_t xEff = xOff_ + x;
const cell_idx_t yEff = yOff_ + y;
const cell_idx_t zEff = zOff_ + z;
if (xEff < 0 || yEff < 0 || zEff < 0 || f < 0 )
return false;
if ( xEff >= cell_idx_c( xAllocSize() ) ||
yEff >= cell_idx_c( yAllocSize() ) ||
zEff >= cell_idx_c( zAllocSize() ) ||
f >= cell_idx_c( fSize_ ) )
return false;
return true;
}
//*******************************************************************************************************************
/*! Accesses the value at given coordinate
*
* When WALBERLA_FIELD_MONITORED_ACCESS is defined, all registered monitor functions are executed when get is called
*
* \note operator() is equivalent to this function
*******************************************************************************************************************/
template
inline const T & Field::get( cell_idx_t x, cell_idx_t y, cell_idx_t z, cell_idx_t f ) const
{
assertValidCoordinates( x, y, z, f );
const cell_idx_t index = f*ffact_+ x*xfact_+ y*yfact_+ z*zfact_;
WALBERLA_ASSERT_LESS( int64_c(index) + int64_c(valuesWithOffset_ - values_), int64_c(allocSize_) );
WALBERLA_ASSERT_GREATER_EQUAL( int64_c(index) + int64_c(valuesWithOffset_ - values_), int64_c(0) );
# ifdef WALBERLA_FIELD_MONITORED_ACCESS
for(uint_t i=0; i< monitorFuncs_.size(); ++i )
monitorFuncs_[i] (x,y,z,f, *(valuesWithOffset_ + index) );
# endif
return *( valuesWithOffset_ + index );
}
//*******************************************************************************************************************
/*! Non-Const variant of get()
*******************************************************************************************************************/
template
inline T& Field::get(cell_idx_t x, cell_idx_t y, cell_idx_t z, cell_idx_t f)
{
const Field& const_this = *this;
return const_cast( const_this.get(x,y,z,f) );
}
//*******************************************************************************************************************
/*! get() variant which takes a uint_t as last coordinate, as for example Stencil::toIdx() returns
*******************************************************************************************************************/
template
inline const T & Field::get( cell_idx_t x, cell_idx_t y, cell_idx_t z, uint_t f ) const
{
return get(x,y,z,cell_idx_c(f));
}
//*******************************************************************************************************************
/*! get() variant which takes a uint_t as last coordinate, as for example Stencil::toIdx() returns
*******************************************************************************************************************/
template
inline T & Field::get( cell_idx_t x, cell_idx_t y, cell_idx_t z, uint_t f )
{
return get(x,y,z,cell_idx_c(f));
}
//*******************************************************************************************************************
/*! get function with only (x,y,z) coordinates, assumes fSize=1
*******************************************************************************************************************/
template
inline T & Field::get( cell_idx_t x, cell_idx_t y, cell_idx_t z)
{
static_assert(fSize_ == 1, "f coordinate omitted for field with fSize > 1 ");
return get(x,y,z,0);
}
//*******************************************************************************************************************
/*! get function with only (x,y,z) coordinates, assumes fSize=1
*******************************************************************************************************************/
template
inline const T & Field::get( cell_idx_t x, cell_idx_t y, cell_idx_t z) const
{
static_assert(fSize_ == 1, "f coordinate omitted for field with fSize > 1 ");
return get(x,y,z,0);
}
//*******************************************************************************************************************
/*! get overload using a cell as input, only possible if fSize=1
*******************************************************************************************************************/
template
inline T & Field::get( const Cell & cell )
{
return get( cell.x(), cell.y(), cell.z() );
}
//*******************************************************************************************************************
/*! get overload using a cell as input, only possible if fSize=1
*******************************************************************************************************************/
template
inline const T & Field::get( const Cell & cell ) const
{
return get( cell.x(), cell.y(), cell.z() );
}
//*******************************************************************************************************************
/*! get overload, where position is specified using an iterator of another field with equal size
*
* Do not use this for iterator that belong to this field, here *iterator is more convenient
*
* \param iter Iterator that belongs to another field that has equal size
*******************************************************************************************************************/
template
inline T & Field::get( const base_iterator & iter )
{
WALBERLA_ASSERT( hasSameAllocSize( *iter.getField() ) );
WALBERLA_ASSERT( hasSameSize ( *iter.getField() ) );
WALBERLA_ASSERT( layout() == iter.getField()->layout() );
WALBERLA_ASSERT( this != iter.getField() ); // use *iterator instead!
return *( valuesWithOffset_ + (&(*iter) - iter.getField()->valuesWithOffset_) );
}
//*******************************************************************************************************************
/*! get overload, where position is specified using an iterator of another field with equal size
*******************************************************************************************************************/
template
inline const T & Field::get( const base_iterator & iter ) const
{
WALBERLA_ASSERT( hasSameAllocSize( *iter.getField() ) );
WALBERLA_ASSERT( hasSameSize ( *iter.getField() ) );
WALBERLA_ASSERT( layout() == iter.getField()->layout() );
WALBERLA_ASSERT( this != iter.getField() ); // use *iterator instead!
return *(valuesWithOffset_ + (&(*iter) - iter.getField()->valuesWithOffset_) );
}
//*******************************************************************************************************************
/*! returns neighboring value of cell in the given direction
*******************************************************************************************************************/
template
inline T & Field::getNeighbor( cell_idx_t x, cell_idx_t y, cell_idx_t z, stencil::Direction d )
{
return getNeighbor(x,y,z,cell_idx_t(0),d);
}
//*******************************************************************************************************************
/*! returns neighboring value of cell in the given direction
*******************************************************************************************************************/
template
inline const T & Field::getNeighbor( cell_idx_t x, cell_idx_t y, cell_idx_t z, stencil::Direction d ) const
{
return getNeighbor(x,y,z,cell_idx_t(0),d);
}
//*******************************************************************************************************************
/*! returns neighboring value of cell in the given direction
*******************************************************************************************************************/
template
inline T & Field::getNeighbor( cell_idx_t x, cell_idx_t y, cell_idx_t z, uint_t f, stencil::Direction d )
{
return get( x + stencil::cx[d],
y + stencil::cy[d],
z + stencil::cz[d],
f);
}
//*******************************************************************************************************************
/*! returns neighboring value of cell in the given direction
*******************************************************************************************************************/
template
inline const T & Field::getNeighbor( cell_idx_t x, cell_idx_t y, cell_idx_t z, uint_t f, stencil::Direction d ) const
{
return get( x + stencil::cx[d],
y + stencil::cy[d],
z + stencil::cz[d],
f);
}
//*******************************************************************************************************************
/*! get overload using a cell as input, only possible if fSize=1, with neighbor access
*******************************************************************************************************************/
template
inline T & Field::getNeighbor( const Cell & cell, stencil::Direction d )
{
return get( cell.x() + stencil::cx[d],
cell.y() + stencil::cy[d],
cell.z() + stencil::cz[d] );
}
//*******************************************************************************************************************
/*! get overload using a cell as input, only possible if fSize=1, with neighbor access
*******************************************************************************************************************/
template
inline const T & Field::getNeighbor( const Cell & cell, stencil::Direction d ) const
{
return get( cell.x() + stencil::cx[d],
cell.y() + stencil::cy[d],
cell.z() + stencil::cz[d] );
}
template
inline T & Field::getF( T * const xyz0, const cell_idx_t f )
{
WALBERLA_ASSERT_LESS( f, cell_idx_c(fSize_) );
WALBERLA_ASSERT( addressInsideAllocedSpace( xyz0 ) );
WALBERLA_ASSERT( addressInsideAllocedSpace( xyz0 + f * ffact_ ) );
return *( xyz0 + f * ffact_ );
}
template
inline const T & Field::getF( const T * const xyz0, const cell_idx_t f ) const
{
WALBERLA_ASSERT_LESS( f, cell_idx_c(fSize_) );
WALBERLA_ASSERT( addressInsideAllocedSpace( xyz0 ) );
WALBERLA_ASSERT( addressInsideAllocedSpace( xyz0 + f * ffact_ ) );
return *( xyz0 + f * ffact_ );
}
//*******************************************************************************************************************
/*! Sets all entries of the field to given value
*
* Works only in the regions specified by size(), not in the complete allocated region as specified by allocSize()
*******************************************************************************************************************/
template
void Field::set (const T & value)
{
#ifdef WALBERLA_CXX_COMPILER_IS_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wundefined-bool-conversion"
#endif
// take care of proper thread<->memory assignment (first-touch allocation policy !)
WALBERLA_FOR_ALL_CELLS_XYZ( this,
for( uint_t f = uint_t(0); f < fSize_; ++f )
get(x,y,z,f) = value;
) // WALBERLA_FOR_ALL_CELLS_XYZ
#ifdef WALBERLA_CXX_COMPILER_IS_CLANG
#pragma clang diagnostic pop
#endif
}
//*******************************************************************************************************************
/*! Initializes the f coordinate to values from vector
* Sets the entry (x,y,z,f) to fValues[f]
*
* Works only in the regions specified by size(), not in the complete allocated region as specified by allocSize()
*
*******************************************************************************************************************/
template
void Field::set (const std::vector & fValues)
{
WALBERLA_ASSERT(fValues.size() == fSize_);
#ifdef WALBERLA_CXX_COMPILER_IS_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wundefined-bool-conversion"
#endif
// take care of proper thread<->memory assignment (first-touch allocation policy !)
WALBERLA_FOR_ALL_CELLS_XYZ( this,
for( uint_t f = uint_t(0); f < fSize_; ++f )
get(x,y,z,f) = fValues[f];
) // WALBERLA_FOR_ALL_CELLS_XYZ
#ifdef WALBERLA_CXX_COMPILER_IS_CLANG
#pragma clang diagnostic pop
#endif
}
//*******************************************************************************************************************
/*! Copies all entries of the given field to this field
*
* Only works when xSize(), ySize(), zSize() and allocSize() are equal
* Copies complete allocated region as specified by allocSize()
*
*******************************************************************************************************************/
template
inline void Field::set (const Field & other )
{
WALBERLA_ASSERT_EQUAL( xyzSize(), other.xyzSize() );
WALBERLA_ASSERT_EQUAL( allocSize(), other.allocSize() );
std::copy( other.values_, other.values_ + allocSize_, values_ );
}
//*******************************************************************************************************************
/*! Swap two fields efficiently by exchanging only values_ pointer
* The two fields have to have identical sizes and same layout.
*******************************************************************************************************************/
template
inline void Field::swapDataPointers( Field & other)
{
WALBERLA_ASSERT( hasSameAllocSize(other) );
WALBERLA_ASSERT( hasSameSize(other) );
WALBERLA_ASSERT( layout() == other.layout() );
std::swap( values_, other.values_ );
std::swap( valuesWithOffset_, other.valuesWithOffset_ );
}
//===================================================================================================================
//
// EQUALITY CHECKS
//
//===================================================================================================================
//*********************************************************************************************************************
/*! Equality operator compares element-wise
*******************************************************************************************************************/
template
inline bool Field::operator==(const Field & other) const
{
if ( !hasSameSize(other) )
return false;
const_iterator lhsIt = this->begin();
const_iterator rhsIt = other.begin();
while( lhsIt != this->end() )
{
if( !math::equal( *lhsIt, *rhsIt ) )
return false;
++lhsIt;
++rhsIt;
}
WALBERLA_ASSERT_EQUAL( rhsIt, other.end() );
return true;
}
//*********************************************************************************************************************
/*! Inequality operator compares element-wise
*******************************************************************************************************************/
template
inline bool Field::operator!=( const Field & other ) const
{
return !( *this == other );
}
//*******************************************************************************************************************
/*! True if allocation sizes of all dimensions match
*******************************************************************************************************************/
template
inline bool Field::hasSameAllocSize( const Field & other ) const
{
return xAllocSize_ == other.xAllocSize_ &&
yAllocSize_ == other.yAllocSize_ &&
zAllocSize_ == other.zAllocSize_ &&
fAllocSize_ == other.fAllocSize_;
}
//*******************************************************************************************************************
/*! True if sizes of all dimensions match
*******************************************************************************************************************/
template
inline bool Field::hasSameSize( const Field & other ) const
{
return xSize_ == other.xSize_ &&
ySize_ == other.ySize_ &&
zSize_ == other.zSize_;
}
//===================================================================================================================
//
// CHANGING OFFSETS
//
//===================================================================================================================
//*******************************************************************************************************************
/*! Moves the coordinate system of the field
*
* Can be used by derived classes, to use only a subset of the field.
* The complete field remains accessible, but now has coordinates smaller than 0 or
* bigger than [xyzf]Size()
* This is used for example in the constructor of the GhostLayerField
*
* Internally this is implementing by adding an offset to the values_ pointer, and by adapting the size_ members.
* \param xOff The x coordinate that is afterwards mapped to zero
* \param xs The new size of the x coordinate. Has to be smaller than (old xSize())-xOff
*******************************************************************************************************************/
template
void Field::setOffsets(uint_t xOffset, uint_t xs,
uint_t yOffset, uint_t ys,
uint_t zOffset, uint_t zs)
{
xOff_ = cell_idx_c( xOffset );
yOff_ = cell_idx_c( yOffset );
zOff_ = cell_idx_c( zOffset );
WALBERLA_ASSERT_LESS_EQUAL( uint_c(xOff_) + xs, xAllocSize() );
WALBERLA_ASSERT_LESS_EQUAL( uint_c(yOff_) + ys, yAllocSize() );
WALBERLA_ASSERT_LESS_EQUAL( uint_c(zOff_) + zs, zAllocSize() );
valuesWithOffset_ = values_;
xSize_ = xs;
ySize_ = ys;
zSize_ = zs;
const auto offset = xOff_*xfact_+ yOff_*yfact_+ zOff_*zfact_;
valuesWithOffset_ = values_ + offset;
}
template
inline bool Field::addressInsideAllocedSpace(const T * const value) const
{
return ( value >= values_ && value < values_ + allocSize_ );
}
//===================================================================================================================
//
// SLICING
//
//===================================================================================================================
template
void Field::shiftCoordinates( cell_idx_t cx, cell_idx_t cy, cell_idx_t cz )
{
WALBERLA_ASSERT_LESS_EQUAL ( uint_c(xOff_ + cx) + xSize(), xAllocSize() );
WALBERLA_ASSERT_LESS_EQUAL ( uint_c(yOff_ + cy) + ySize(), yAllocSize() );
WALBERLA_ASSERT_LESS_EQUAL ( uint_c(zOff_ + cz) + zSize(), zAllocSize() );
setOffsets( uint_c( xOff_ + cx ), xSize(), uint_c( yOff_ + cy ), ySize(), uint_c( zOff_ + cz ), zSize() );
}
//*******************************************************************************************************************
/*! Create a different "view" of the field, where the created field has the size of the given sliceInterval
*
* The ownership of the returned pointer is transfered to the caller i.e. the caller is responsible for
* deleting the returned object.
*
* The returned field uses the same data as the original field. However the returned field has a
* different coordinate system defined by the given slice-interval. Modifying the returned slice field
* modifies also the original field!
* The sliced field has the size as given by the slice-interval.
*******************************************************************************************************************/
template
Field * Field::getSlicedField( const CellInterval & interval ) const
{
auto slicedField = cloneShallowCopy();
slicedField->slice ( interval );
return slicedField;
}
//*******************************************************************************************************************
/*! Changes the coordinate system of the field.
*
* The origin of the new coordinates is at the cell given by min() of the CellInterval.
* The new size of the field, is the size of the the CellInterval, however the alloc size does not change.
* Cells that are not in this cell interval can still be accessed
* ( by coordinates smaller 0, or bigger than [xyz]Size)
*******************************************************************************************************************/
template
void Field::slice( const CellInterval & interval )
{
setOffsets ( uint_c( interval.xMin() + xOff_ ), interval.xSize(),
uint_c( interval.yMin() + yOff_ ), interval.ySize(),
uint_c( interval.zMin() + zOff_ ), interval.zSize() );
}
//*******************************************************************************************************************
/*! Returns the number of objects that internally use the same data.
*******************************************************************************************************************/
template
uint_t Field::referenceCount( ) const
{
return allocator_->referenceCount( values_ );
}
//===================================================================================================================
//
// MONITORING
//
//===================================================================================================================
//*******************************************************************************************************************
/*! Registers a monitoring function
*
* Monitoring works only if compiled with WALBERLA_FIELD_MONITORED_ACCESS.
* When a field is accessed either by get() or operator() the monitoring function is called
*******************************************************************************************************************/
# ifdef WALBERLA_FIELD_MONITORED_ACCESS
template
void Field::addMonitoringFunction(const MonitorFunction & func)
{
monitorFuncs_.push_back(func);
}
# else
template
void Field::addMonitoringFunction(const MonitorFunction & )
{
}
# endif
//===================================================================================================================
//
// Low Level Functions - do not use if not absolutely necessary
//
//===================================================================================================================
//*******************************************************************************************************************
/*! Returns internal data allocator
*
* The allocator can for example be used to prevent free() on the field data when class is deleted.
* This is useful if you keep a pointer to the internal data, to make sure it remains valid
* ( also after possible swapDataPointers() etc. )
*
*
* field->getAllocator()->incrementReferenceCount( field->data() );
* Use the data pointer
* field->getAllocator()->decrementReferenceCount( field->data() );
*
*/
//*******************************************************************************************************************
template
shared_ptr< FieldAllocator > Field::getAllocator() const
{
return this->allocator_;
}
}
}