/* mesh.hh - This file is part of MUSIC - a code to generate multi-scale initial conditions for cosmological simulations Copyright (C) 2010 Oliver Hahn */ #ifndef __MESH_HH #define __MESH_HH #include #include #include #include #include #include "config_file.hh" #include "log.hh" #include "region_generator.hh" class refinement_mask { protected: std::vector mask_; size_t nx_, ny_, nz_; public: refinement_mask( void ) : nx_( 0 ), ny_ ( 0 ), nz_( 0 ) { } refinement_mask( size_t nx, size_t ny, size_t nz ) : nx_( nx ), ny_( ny ), nz_( nz ) { mask_.assign( nx_*ny_*nz_, 0 ); } refinement_mask( const refinement_mask& r ) { nx_ = r.nx_; ny_ = r.ny_; nz_ = r.nz_; mask_ = r.mask_; } refinement_mask& operator=( const refinement_mask& r ) { nx_ = r.nx_; ny_ = r.ny_; nz_ = r.nz_; mask_ = r.mask_; return *this; } void init( size_t nx, size_t ny, size_t nz ) { nx_ = nx; ny_ = ny; nz_ = nz; mask_.assign( nx_*ny_*nz_, 0 ); } const short& operator()( size_t i, size_t j, size_t k ) const { return mask_[ (i*ny_+j)*nz_+k ]; } short& operator()( size_t i, size_t j, size_t k ) { return mask_[ (i*ny_+j)*nz_+k ]; } size_t count_flagged( void ) { size_t count = 0; for( size_t i=0; i class Meshvar{ public: typedef T real_t; size_t m_nx, //!< x-extent of the rectangular mesh m_ny, //!< y-extent of the rectangular mesh m_nz; //!< z-extent of the rectangular mesh int m_offx, //!< x-offset of the grid (just as a helper, not used inside the class) m_offy, //!< y-offset of the grid (just as a helper, not used inside the class) m_offz; //!< z-offset of the grid (just as a helper, not used inside the class) real_t * m_pdata; //!< pointer to the dynamic data array //! constructor for cubic mesh explicit Meshvar( size_t n, int offx, int offy, int offz ) : m_nx( n ), m_ny( n ), m_nz( n ), m_offx( offx ), m_offy( offy ), m_offz( offz ) { m_pdata = new real_t[m_nx*m_ny*m_nz]; } //! constructor for rectangular mesh Meshvar( size_t nx, size_t ny, size_t nz, int offx, int offy, int offz ) : m_nx( nx ), m_ny( ny ), m_nz( nz ), m_offx( offx ), m_offy( offy ), m_offz( offz ) { m_pdata = new real_t[m_nx*m_ny*m_nz]; } //! variant copy constructor with optional copying of the actual data Meshvar( const Meshvar& m, bool copy_over=true ) { m_nx = m.m_nx; m_ny = m.m_ny; m_nz = m.m_nz; m_offx = m.m_offx; m_offy = m.m_offy; m_offz = m.m_offz; m_pdata = new real_t[m_nx*m_ny*m_nz]; if( copy_over ) for( size_t i=0; i& m ) { m_nx = m.m_nx; m_ny = m.m_ny; m_nz = m.m_nz; m_offx = m.m_offx; m_offy = m.m_offy; m_offz = m.m_offz; m_pdata = new real_t[m_nx*m_ny*m_nz]; for( size_t i=0; i=(int)m_nx||iy<0||iy>=(int)m_ny||iz<0||iz>=(int)m_nz) LOGERR("Array index (%d,%d,%d) out of bounds",ix,iy,iz); #endif return m_pdata[ ((size_t)ix*m_ny+(size_t)iy)*m_nz + (size_t)iz ]; } //! 3D random access to the data block via index 3-tuples (const) inline const real_t& operator()(const int ix, const int iy, const int iz ) const { #ifdef DEBUG if( ix<0||ix>=(int)m_nx||iy<0||iy>=(int)m_ny||iz<0||iz>=(int)m_nz) LOGERR("Array index (%d,%d,%d) out of bounds",ix,iy,iz); #endif return m_pdata[ ((size_t)ix*m_ny+(size_t)iy)*m_nz + (size_t)iz ]; } //! direct multiplication of the whole data block with a number Meshvar& operator*=( real_t x ) { for( size_t i=0; i& operator+=( real_t x ) { for( size_t i=0; i& operator/=( real_t x ) { for( size_t i=0; i& operator-=( real_t x ) { for( size_t i=0; i& operator*=( const Meshvar& v ) { if( v.m_nx*v.m_ny*v.m_nz != m_nx*m_ny*m_nz ) { LOGERR("Meshvar::operator*= : attempt to operate on incompatible data"); throw std::runtime_error("Meshvar::operator*= : attempt to operate on incompatible data"); } for( size_t i=0; i& operator/=( const Meshvar& v ) { if( v.m_nx*v.m_ny*v.m_nz != m_nx*m_ny*m_nz ) { LOGERR("Meshvar::operator/= : attempt to operate on incompatible data"); throw std::runtime_error("Meshvar::operator/= : attempt to operate on incompatible data"); } for( size_t i=0; i& operator+=( const Meshvar& v ) { if( v.m_nx*v.m_ny*v.m_nz != m_nx*m_ny*m_nz ) { LOGERR("Meshvar::operator+= : attempt to operate on incompatible data"); throw std::runtime_error("Meshvar::operator+= : attempt to operate on incompatible data"); } for( size_t i=0; i& operator-=( const Meshvar& v ) { if( v.m_nx*v.m_ny*v.m_nz != m_nx*m_ny*m_nz ) { LOGERR("Meshvar::operator-= : attempt to operate on incompatible data"); throw std::runtime_error("Meshvar::operator-= : attempt to operate on incompatible data"); } for( size_t i=0; i& operator=( const Meshvar& m ) { m_nx = m.m_nx; m_ny = m.m_ny; m_nz = m.m_nz; m_offx = m.m_offx; m_offy = m.m_offy; m_offz = m.m_offz; if( m_pdata != NULL ) delete m_pdata; m_pdata = new real_t[m_nx*m_ny*m_nz]; for( size_t i=0; i class MeshvarBnd : public Meshvar< T >{ using Meshvar::m_nx; using Meshvar::m_ny; using Meshvar::m_nz; using Meshvar::m_pdata; public: typedef T real_t; //! number of boundary (ghost) cells int m_nbnd; //! most general constructor MeshvarBnd( int nbnd, size_t nx, size_t ny, size_t nz, size_t xoff, size_t yoff, size_t zoff ) : Meshvar( nx+2*nbnd, ny+2*nbnd, nz+2*nbnd, xoff, yoff, zoff ), m_nbnd( nbnd ) { } //! zero-offset constructor MeshvarBnd( size_t nbnd, size_t nx, size_t ny, size_t nz ) : Meshvar( nx+2*nbnd, ny+2*nbnd, nz+2*nbnd, 0, 0, 0 ), m_nbnd( nbnd ) { } //! constructor for cubic meshes MeshvarBnd( size_t nbnd, size_t n, size_t xoff, size_t yoff, size_t zoff ) : Meshvar( n+2*nbnd, xoff, yoff, zoff ), m_nbnd( nbnd ) { } //! constructor for cubic meshes with zero offset MeshvarBnd( size_t nbnd, size_t n ) : Meshvar( n+2*nbnd, 0, 0, 0 ), m_nbnd( nbnd ) { } //! modified copy constructor, allows to avoid copying actual data MeshvarBnd( const MeshvarBnd& v, bool copyover ) : Meshvar( v, copyover ), m_nbnd( v.m_nbnd ) { } //! copy constructor explicit MeshvarBnd( const MeshvarBnd& v ) : Meshvar( v, true ), m_nbnd( v.m_nbnd ) { } //! get extent of the mesh along a specified dimension inline size_t size( unsigned dim=0 ) const { if( dim == 0 ) return m_nx-2*m_nbnd; if( dim == 1 ) return m_ny-2*m_nbnd; return m_nz-2*m_nbnd; } //! 3D random access to the data block via index 3-tuples inline real_t& operator()(const int ix, const int iy, const int iz ) { size_t iix(ix+m_nbnd), iiy(iy+m_nbnd), iiz(iz+m_nbnd); return m_pdata[ (iix*m_ny+iiy)*m_nz + iiz ]; } //! 3D random access to the data block via index 3-tuples (const) inline const real_t& operator()(const int ix, const int iy, const int iz ) const { size_t iix(ix+m_nbnd), iiy(iy+m_nbnd), iiz(iz+m_nbnd); return m_pdata[ (iix*m_ny+iiy)*m_nz + iiz ]; } //! assignment operator for rectangular meshes with ghost zones MeshvarBnd& operator=( const MeshvarBnd& m ) { if( this->m_nx != m.m_nx || this->m_ny != m.m_ny || this->m_nz != m.m_nz ) { this->m_nx = m.m_nx; this->m_ny = m.m_ny; this->m_nz = m.m_nz; if( m_pdata != NULL ) delete[] m_pdata; m_pdata = new real_t[m_nx*m_ny*m_nz]; } for( size_t i=0; im_pdata[i] = m.m_pdata[i]; return *this; } //! sets the value of all ghost zones to zero void zero_bnd( void ) { int nx,ny,nz; nx = this->size(0); ny = this->size(1); nz = this->size(2); for( int j=-m_nbnd; jsize(0) << ", " << this->size(1) << ", " << this->size(2) << "]\n"; std::cout << "ghost region has length of " << nbnd << std::endl; std::cout.precision(3); for(int i=-nbnd; i<(int)this->size(0)+nbnd; ++i ) { std::cout << "ix = " << i << ": \n"; for (int j=-nbnd; j<(int)this->size(1)+nbnd; ++j) { for (int k=-nbnd; k<(int)this->size(2)+nbnd; ++k) { if( i<0||i>=this->size(0)||j<0||j>=this->size(1)||k<0||k>=this->size(2)) std::cout << "[" << std::setw(6) << (*this)(i,j,k) << "] "; else std::cout << std::setw(8) << (*this)(i,j,k) << " "; } std::cout << std::endl; } std::cout << std::endl; } } }; //! class that subsumes a nested grid collection template< typename T > class GridHierarchy { public: //! number of ghost cells on boundary size_t m_nbnd; //! highest level without adaptive refinement unsigned m_levelmin; //! vector of pointers to the underlying rectangular mesh data for each level std::vector< MeshvarBnd* > m_pgrids; std::vector m_xoffabs, //!< vector of x-offsets of a level mesh relative to the coarser level m_yoffabs, //!< vector of x-offsets of a level mesh relative to the coarser level m_zoffabs; //!< vector of x-offsets of a level mesh relative to the coarser level refinement_mask ref_mask; bool bhave_refmask; protected: //! check whether a given grid has identical hierarchy, dimensions to this bool is_consistent( const GridHierarchy& gh ) { if( gh.levelmax()!=levelmax() ) return false; if( gh.levelmin()!=levelmin() ) return false; for( unsigned i=levelmin(); i<=levelmax(); ++i ) for( int j=0; j<3; ++j ) { if( size(i,j) != gh.size(i,j) ) return false; if( offset(i,j) != gh.offset(i,j) ) return false; } return true; } public: //! return a pointer to the MeshvarBnd object representing data for one level MeshvarBnd *get_grid( unsigned ilevel ) { if( ilevel >= m_pgrids.size() ) { LOGERR("Attempt to access level %d but maxlevel = %d", ilevel, m_pgrids.size()-1); throw std::runtime_error("Fatal: attempt to access non-existent grid"); } return m_pgrids[ilevel]; } //! return a pointer to the MeshvarBnd object representing data for one level (const) const MeshvarBnd *get_grid( unsigned ilevel ) const { if( ilevel >= m_pgrids.size() ) { LOGERR("Attempt to access level %d but maxlevel = %d", ilevel, m_pgrids.size()-1 ); throw std::runtime_error("Fatal: attempt to access non-existent grid"); } return m_pgrids[ilevel]; } //! constructor for a collection of rectangular grids representing a multi-level hierarchy /*! creates an empty hierarchy, levelmin is initially zero, no grids are stored * @param nbnd number of ghost zones added at the boundary */ explicit GridHierarchy( size_t nbnd ) : m_nbnd( nbnd ), m_levelmin( 0 ), bhave_refmask( false ) { m_pgrids.clear(); } //! copy constructor explicit GridHierarchy( const GridHierarchy & gh ) { for( unsigned i=0; i<=gh.levelmax(); ++i ) m_pgrids.push_back( new MeshvarBnd( *gh.get_grid(i) ) ); m_nbnd = gh.m_nbnd; m_levelmin = gh.m_levelmin; m_xoffabs = gh.m_xoffabs; m_yoffabs = gh.m_yoffabs; m_zoffabs = gh.m_zoffabs; ref_mask = gh.ref_mask; bhave_refmask = gh.bhave_refmask; } //! destructor ~GridHierarchy() { this->deallocate(); } //! free all memory occupied by the grid hierarchy void deallocate() { for( unsigned i=0; i* >().swap( m_pgrids ); m_xoffabs.clear(); m_yoffabs.clear(); m_zoffabs.clear(); m_levelmin = 0; } void add_refinement_mask( const double *shift ) { bhave_refmask = false; //! generate a mask if( m_levelmin != levelmax() ) { int ilevel = levelmax() - 1; double xq[3], dx = 1.0/(1ul<<(levelmax()-1)); ref_mask.init( size(ilevel,0), size(ilevel,1), size(ilevel,2) ); for( size_t i=0; iquery_point( xq ) ) ref_mask(i,j,k) = true; } } } bhave_refmask = true; } } //! get offset of a grid at specified refinement level /*! the offset describes the shift of a refinement grid with respect to its coarser parent grid * @param ilevel the level for which the offset is to be determined * @param idim the dimension along which the offset is to be determined * @return integer value denoting the offset in units of coarse grid cells * @sa offset_abs */ int offset( int ilevel, int idim ) const { return m_pgrids[ilevel]->offset(idim); } //! get size of a grid at specified refinement level /*! the size describes the number of cells along one dimension of a grid * @param ilevel the level for which the size is to be determined * @param idim the dimension along which the size is to be determined * @return integer value denoting the size of refinement grid at level ilevel along dimension idim */ size_t size( int ilevel, int idim ) const { return m_pgrids[ilevel]->size(idim); } //! get the absolute offset of a grid at specified refinement level /*! the absolute offset describes the shift of a refinement grid with respect to the simulation volume * @param ilevel the level for which the offset is to be determined * @param idim the dimension along which the offset is to be determined * @return integer value denoting the absolute offset in units of fine grid cells * @sa offset */ int offset_abs( int ilevel, int idim ) const { if( idim == 0 ) return m_xoffabs[ilevel]; if( idim == 1 ) return m_yoffabs[ilevel]; return m_zoffabs[ilevel]; } //! get the coordinate posisition of a grid cell /*! returns the position of a grid cell at specified level relative to the simulation volume * @param ilevel the refinement level of the grid cell * @param i the x-index of the cell in the level grid * @param j the y-index of the cell in the level grid * @param k the z-index of the cell in the level grid * @param ppos pointer to a double[3] array to which the coordinates are written * @return none */ void cell_pos( unsigned ilevel, int i, int j, int k, double* ppos ) const { double h = 1.0/(1<= 1.0 || ppos[1] >= 1.0 || ppos[2] >= 1.0 ) std::cerr << " - Cell seems outside domain! : (" << ppos[0] << ", " << ppos[1] << ", " << ppos[2] << "\n"; } //! get the bounding box of a grid in code units /*! returns the bounding box of a grid at specified level in code units * @param ilevel the refinement level of the grid * @param left pointer to a double[3] array to which the left corner is written * @param right pointer to a double[3] array to which the right corner is written * @return none */ void grid_bbox( unsigned ilevel, double *left, double *right ) const { double h = 1.0/(1<= offset(ilevel+1, 0)+(int)size(ilevel+1,0)/2 || j < offset(ilevel+1,1) || j >= offset(ilevel+1, 1)+(int)size(ilevel+1,1)/2 || k < offset(ilevel+1,2) || k >= offset(ilevel+1, 2)+(int)size(ilevel+1,2)/2 ) return false; return true; } //! sets the values of all grids on all levels to zero void zero( void ) { for( unsigned i=0; izero(); } //! count the number of cells that are not further refined (=leafs) /*! for allocation purposes it is useful to query the number of cells to be expected * @param lmin the minimum refinement level to consider * @param lmax the maximum refinement level to consider * @return the integer number of cells between lmin and lmax that are not further refined */ size_t count_leaf_cells( unsigned lmin, unsigned lmax ) const { size_t npcount = 0; for( int ilevel=lmax; ilevel>=(int)lmin; --ilevel ) for( unsigned i=0; isize(0); ++i ) for( unsigned j=0; jsize(1); ++j ) for( unsigned k=0; ksize(2); ++k ) if( ! is_refined(ilevel,i,j,k) ) ++npcount; return npcount; } //! count the number of cells that are not further refined (=leafs) /*! for allocation purposes it is useful to query the number of cells to be expected * @return the integer number of cells in the hierarchy that are not further refined */ size_t count_leaf_cells( void ) const { return count_leaf_cells( levelmin(), levelmax() ); } //! creates a hierarchy of coextensive grids, refined by factors of 2 /*! creates a hierarchy of lmax grids, each extending over the whole simulation volume with * grid length 2^n for level 0<=n<=lmax * @param lmax the maximum refinement level to be added (sets the resolution to 2^lmax for each dim) * @return none */ void create_base_hierarchy( unsigned lmax ) { size_t n=1; this->deallocate(); m_pgrids.clear(); m_xoffabs.clear(); m_yoffabs.clear(); m_zoffabs.clear(); for( unsigned i=0; i<= lmax; ++i ) { //std::cout << "....adding level " << i << " (" << n << ", " << n << ", " << n << ")" << std::endl; m_pgrids.push_back( new MeshvarBnd( m_nbnd, n, n, n, 0, 0, 0 ) ); m_pgrids[i]->zero(); n *= 2; m_xoffabs.push_back( 0 ); m_yoffabs.push_back( 0 ); m_zoffabs.push_back( 0 ); } m_levelmin = lmax; } //! multiply entire grid hierarchy by a constant GridHierarchy& operator*=( T x ) { for( unsigned i=0; i& operator/=( T x ) { for( unsigned i=0; i& operator+=( T x ) { for( unsigned i=0; i& operator-=( T x ) { for( unsigned i=0; i& operator*=( const GridHierarchy& gh ) { if( !is_consistent(gh) ) { LOGERR("GridHierarchy::operator*= : attempt to operate on incompatible data"); throw std::runtime_error("GridHierarchy::operator*= : attempt to operate on incompatible data"); } for( unsigned i=0; i& operator/=( const GridHierarchy& gh ) { if( !is_consistent(gh) ) { LOGERR("GridHierarchy::operator/= : attempt to operate on incompatible data"); throw std::runtime_error("GridHierarchy::operator/= : attempt to operate on incompatible data"); } for( unsigned i=0; i& operator+=( const GridHierarchy& gh ) { if( !is_consistent(gh) ) throw std::runtime_error("GridHierarchy::operator+= : attempt to operate on incompatible data"); for( unsigned i=0; i& operator-=( const GridHierarchy& gh ) { if( !is_consistent(gh) ) { LOGERR("GridHierarchy::operator-= : attempt to operate on incompatible data"); throw std::runtime_error("GridHierarchy::operator-= : attempt to operate on incompatible data"); } for( unsigned i=0; i& operator=( const GridHierarchy& gh ) { bhave_refmask = gh.bhave_refmask; ref_mask = gh.ref_mask; if( !is_consistent(gh) ) { for( unsigned i=0; i( *gh.get_grid(i) ) ); m_levelmin = gh.levelmin(); m_nbnd = gh.m_nbnd; m_xoffabs = gh.m_xoffabs; m_yoffabs = gh.m_yoffabs; m_zoffabs = gh.m_zoffabs; return *this; }//throw std::runtime_error("GridHierarchy::operator= : attempt to operate on incompatible data"); for( unsigned i=0; i& gh ) { for( unsigned i=0; i( *gh.get_grid(i) ) ); m_levelmin = gh.levelmin(); m_nbnd = gh.m_nbnd; m_xoffabs = gh.m_xoffabs; m_yoffabs = gh.m_yoffabs; m_zoffabs = gh.m_zoffabs; return *this; } */ /*! add a new refinement patch to the so-far finest level * @param xoff x-offset in units of the coarser grid (finest grid before adding new patch) * @param yoff y-offset in units of the coarser grid (finest grid before adding new patch) * @param zoff z-offset in units of the coarser grid (finest grid before adding new patch) * @param nx x-extent in fine grid cells * @param ny y-extent in fine grid cells * @param nz z-extent in fine grid cells */ void add_patch( unsigned xoff, unsigned yoff, unsigned zoff, unsigned nx, unsigned ny, unsigned nz ) { m_pgrids.push_back( new MeshvarBnd( m_nbnd, nx, ny, nz, xoff, yoff, zoff ) ); m_pgrids.back()->zero(); //.. add absolute offsets (in units of current level grid cells) m_xoffabs.push_back( 2*(m_xoffabs.back() + xoff) ); m_yoffabs.push_back( 2*(m_yoffabs.back() + yoff) ); m_zoffabs.push_back( 2*(m_zoffabs.back() + zoff) ); } /*! cut a refinement patch to the specified size * @param ilevel grid level on which to perform the size adjustment * @param xoff new x-offset in units of the coarser grid (finest grid before adding new patch) * @param yoff new y-offset in units of the coarser grid (finest grid before adding new patch) * @param zoff new z-offset in units of the coarser grid (finest grid before adding new patch) * @param nx new x-extent in fine grid cells * @param ny new y-extent in fine grid cells * @param nz new z-extent in fine grid cells */ void cut_patch( unsigned ilevel, unsigned xoff, unsigned yoff, unsigned zoff, unsigned nx, unsigned ny, unsigned nz) { unsigned dx,dy,dz,dxtop,dytop,dztop; dx = xoff-m_xoffabs[ilevel]; dy = yoff-m_yoffabs[ilevel]; dz = zoff-m_zoffabs[ilevel]; assert( dx%2==0 && dy%2==0 && dz%2==0 ); dxtop = m_pgrids[ilevel]->offset(0)+dx/2; dytop = m_pgrids[ilevel]->offset(1)+dy/2; dztop = m_pgrids[ilevel]->offset(2)+dz/2; MeshvarBnd *mnew = new MeshvarBnd( m_nbnd, nx, ny, nz, dxtop, dytop, dztop ); //... copy data for( unsigned i=0; ioffset(0) -= dx; m_pgrids[ilevel+1]->offset(1) -= dy; m_pgrids[ilevel+1]->offset(2) -= dz; } find_new_levelmin(); } void cut_patch_enforce_top_density( unsigned ilevel, unsigned xoff, unsigned yoff, unsigned zoff, unsigned nx, unsigned ny, unsigned nz) { unsigned dx,dy,dz,dxtop,dytop,dztop; dx = xoff-m_xoffabs[ilevel]; dy = yoff-m_yoffabs[ilevel]; dz = zoff-m_zoffabs[ilevel]; assert( dx%2==0 && dy%2==0 && dz%2==0 ); dxtop = m_pgrids[ilevel]->offset(0)+dx/2; dytop = m_pgrids[ilevel]->offset(1)+dy/2; dztop = m_pgrids[ilevel]->offset(2)+dz/2; MeshvarBnd *mnew = new MeshvarBnd( m_nbnd, nx, ny, nz, dxtop, dytop, dztop ); double coarsesum = 0.0, finesum = 0.0; size_t coarsecount = 0, finecount = 0; //... copy data for( unsigned i=0; ioffset(0) -= dx; m_pgrids[ilevel+1]->offset(1) -= dy; m_pgrids[ilevel+1]->offset(2) -= dz; } //... enforce top mean density over same patch if( ilevel > levelmin() ) { int ox = m_pgrids[ilevel]->offset(0); int oy = m_pgrids[ilevel]->offset(1); int oz = m_pgrids[ilevel]->offset(2); for( unsigned i=0; isize(0) == n && m_pgrids[i]->size(1) == n && m_pgrids[i]->size(2) == n ) { m_levelmin=i; } } } //! return maximum level in refinement hierarchy unsigned levelmax( void ) const { return m_pgrids.size()-1; } //! return minimum level in refinement hierarchy (the level which extends over the entire domain) unsigned levelmin( void ) const { return m_levelmin; } }; //! class that computes the refinement structure given parameters class refinement_hierarchy { std::vector x0_, //!< x-coordinates of grid origins (in [0..1[) y0_, //!< y-coordinates of grid origins (in [0..1[) z0_, //!< z-coordinates of grid origins (in [0..1[) xl_, //!< x-extent of grids (in [0..1[) yl_, //!< y-extent of grids (in [0..1[) zl_; //!< z-extent of grids (in [0..1[) std::vector ox_, //!< relative x-coordinates of grid origins (in coarser grid cells) oy_, //!< relative y-coordinates of grid origins (in coarser grid cells) oz_, //!< relative z-coordinates of grid origins (in coarser grid cells) oax_, //!< absolute x-coordinates of grid origins (in fine grid cells) oay_, //!< absolute y-coordinates of grid origins (in fine grid cells) oaz_, //!< absolute z-coordinates of grid origins (in fine grid cells) nx_, //!< x-extent of grids (in fine grid cells) ny_, //!< y-extent of grids (in fine grid cells) nz_; //!< z-extent of grids (in fine grid cells) unsigned levelmin_, //!< minimum grid level for Poisson solver levelmax_, //!< maximum grid level for all operations levelmin_tf_, //!< minimum grid level for density calculation padding_, //!< padding in number of coarse cells between refinement levels blocking_factor_; config_file& cf_; //!< reference to config_file bool align_top_, //!< bool whether to align all grids with coarsest grid cells equal_extent_; //!< bool whether the simulation code requires squared refinement regions (e.g. RAMSES) double x0ref_[3], //!< coordinates of refinement region origin (in [0..1[) lxref_[3]; //!< extent of refinement region (int [0..1[) size_t lnref_[3]; bool bhave_nref; int xshift_[3]; //!< shift of refinement region in coarse cells (in order to center it in the domain) double rshift_[3]; public: //! copy constructor refinement_hierarchy( const refinement_hierarchy& rh ) : cf_( rh.cf_ ) { *this = rh; } //! constructor from a config_file holding information about the desired refinement geometry explicit refinement_hierarchy( config_file& cf ) : cf_( cf ) { //... query the parameter data we need levelmin_ = cf_.getValue("setup","levelmin"); levelmax_ = cf_.getValue("setup","levelmax"); levelmin_tf_= cf_.getValueSafe("setup","levelmin_TF",levelmin_); align_top_ = cf_.getValueSafe("setup","align_top",false); equal_extent_ = cf_.getValueSafe("setup","force_equal_extent",false); blocking_factor_= cf.getValueSafe( "setup", "blocking_factor",0); bool bnoshift = cf_.getValueSafe("setup","no_shift",false); bool force_shift = cf_.getValueSafe("setup","force_shift",false); //... call the region generator if( levelmin_ != levelmax_ ) { double x1ref[3]; the_region_generator->get_AABB(x0ref_,x1ref,levelmax_); for( int i=0; i<3; ++i ) lxref_[i] = x1ref[i]-x0ref_[i]; bhave_nref = false; std::string region_type = cf.getValueSafe("setup","region","box"); LOGINFO("refinement region is \'%s\', w/ bounding box\n left = [%f,%f,%f]\n right = [%f,%f,%f]", region_type.c_str(),x0ref_[0],x0ref_[1],x0ref_[2],x1ref[0],x1ref[1],x1ref[2]); bhave_nref = the_region_generator->is_grid_dim_forced( lnref_ ); } // if not doing any refinement levels, set extent to full box if( levelmin_ == levelmax_ ) { x0ref_[0] = 0.0; x0ref_[1] = 0.0; x0ref_[2] = 0.0; lxref_[0] = 1.0; lxref_[1] = 1.0; lxref_[2] = 1.0; } unsigned ncoarse = 1<=ir || jl>=jr || kl>=kr ) { LOGERR("Internal refinement bounding box error: [%d,%d]x[%d,%d]x[%d,%d]",il,ir,jl,jr,kl,kr); throw std::runtime_error("refinement_hierarchy: Internal refinement bounding box error 1"); } //... determine offsets if( levelmin_ != levelmax_ ) { oax_[levelmax_] = (il+nresmax)%nresmax; oay_[levelmax_] = (jl+nresmax)%nresmax; oaz_[levelmax_] = (kl+nresmax)%nresmax; nx_[levelmax_] = ir-il; ny_[levelmax_] = jr-jl; nz_[levelmax_] = kr-kl; if( equal_extent_ ) { if( bhave_nref && (lnref_[0]!=lnref_[1]||lnref_[0]!=lnref_[2]) ) { LOGERR("Specified equal_extent=yes conflicting with ref_dims which are not equal."); throw std::runtime_error("Specified equal_extent=yes conflicting with ref_dims which are not equal."); } size_t ilevel = levelmax_; size_t nmax = std::max( nx_[ilevel], std::max( ny_[ilevel], nz_[ilevel] ) ); int dx = (int)((double)(nmax-nx_[ilevel])*0.5); int dy = (int)((double)(nmax-ny_[ilevel])*0.5); int dz = (int)((double)(nmax-nz_[ilevel])*0.5); oax_[ilevel] -= dx; oay_[ilevel] -= dy; oaz_[ilevel] -= dz; nx_[ilevel] = nmax; ny_[ilevel] = nmax; nz_[ilevel] = nmax; il = oax_[ilevel]; jl = oay_[ilevel]; kl = oaz_[ilevel]; ir = il + nmax; jr = jl + nmax; kr = kl + nmax; } } padding_ = cf_.getValueSafe("setup","padding", 8); //... determine position of coarser grids for( unsigned ilevel=levelmax_-1; ilevel> levelmin_; --ilevel ) { il = (int)((double)il * 0.5 - padding_); jl = (int)((double)jl * 0.5 - padding_); kl = (int)((double)kl * 0.5 - padding_); ir = (int)((double)ir * 0.5 + padding_); jr = (int)((double)jr * 0.5 + padding_); kr = (int)((double)kr * 0.5 + padding_); //... align with coarser grids ... if( align_top_ ) { //... require alignment with top grid unsigned nref = 1<<(ilevel-levelmin_); il = (int)((double)il/nref)*nref; jl = (int)((double)jl/nref)*nref; kl = (int)((double)kl/nref)*nref; ir = (int)((double)ir/nref+1.0)*nref; jr = (int)((double)jr/nref+1.0)*nref; kr = (int)((double)kr/nref+1.0)*nref; } else { //... require alignment with coarser grid il -= il%2; jl -= jl%2; kl -= kl%2; ir += ir%2; jr += jr%2; kr += kr%2; } if( il>=ir || jl>=jr || kl>=kr || il < 0 || jl < 0 || kl < 0) { LOGERR("Internal refinement bounding box error: [%d,%d]x[%d,%d]x[%d,%d], level=%d",il,ir,jl,jr,kl,kr,ilevel); throw std::runtime_error("refinement_hierarchy: Internal refinement bounding box error 2"); } oax_[ilevel] = il; oay_[ilevel] = jl; oaz_[ilevel] = kl; nx_[ilevel] = ir-il; ny_[ilevel] = jr-jl; nz_[ilevel] = kr-kl; if (blocking_factor_) { nx_[ilevel] += nx_[ilevel] % blocking_factor_; ny_[ilevel] += ny_[ilevel] % blocking_factor_; nz_[ilevel] += nz_[ilevel] % blocking_factor_; } if( equal_extent_ ) { size_t nmax = std::max( nx_[ilevel], std::max( ny_[ilevel], nz_[ilevel] ) ); int dx = (int)((double)(nmax-nx_[ilevel])*0.5); int dy = (int)((double)(nmax-ny_[ilevel])*0.5); int dz = (int)((double)(nmax-nz_[ilevel])*0.5); oax_[ilevel] -= dx; oay_[ilevel] -= dy; oaz_[ilevel] -= dz; nx_[ilevel] = nmax; ny_[ilevel] = nmax; nz_[ilevel] = nmax; il = oax_[ilevel]; jl = oay_[ilevel]; kl = oaz_[ilevel]; ir = il + nmax; jr = jl + nmax; kr = kl + nmax; } } //... determine relative offsets between grids for( unsigned ilevel=levelmax_; ilevel>levelmin_; --ilevel ) { ox_[ilevel] = (oax_[ilevel]/2 - oax_[ilevel-1]); oy_[ilevel] = (oay_[ilevel]/2 - oay_[ilevel-1]); oz_[ilevel] = (oaz_[ilevel]/2 - oaz_[ilevel-1]); } //... do a forward sweep to ensure that absolute offsets are also correct now for( unsigned ilevel=levelmin_+1; ilevel<=levelmax_; ++ilevel ) { oax_[ilevel] = 2*oax_[ilevel-1]+2*ox_[ilevel]; oay_[ilevel] = 2*oay_[ilevel-1]+2*oy_[ilevel]; oaz_[ilevel] = 2*oaz_[ilevel-1]+2*oz_[ilevel]; } for( unsigned ilevel=levelmin_+1; ilevel<=levelmax_; ++ilevel ) { double h = 1.0/(1ul< (1ul<<(ilevel-1)) || ny_[ilevel] > (1ul<<(ilevel-1)) || nz_[ilevel] > (1ul<<(ilevel-1)) ) { LOGERR("On level %d, subgrid is larger than half the box. This is not allowed!",ilevel); throw std::runtime_error("Fatal: Subgrid larger than half boxin zoom."); } } // update the region generator with what has been actually created double left[3] = { x0_[levelmax_]+rshift_[0], y0_[levelmax_]+rshift_[1], z0_[levelmax_]+rshift_[2] }; double right[3] = { left[0]+xl_[levelmax_], left[1]+yl_[levelmax_], left[2]+zl_[levelmax_] }; the_region_generator->update_AABB( left, right ); } //! asignment operator refinement_hierarchy& operator=( const refinement_hierarchy& o ) { levelmin_ = o.levelmin_; levelmax_ = o.levelmax_; padding_ = o.padding_; cf_ = o.cf_; align_top_ = o.align_top_; for( int i=0; i<3; ++i ) { x0ref_[i] = o.x0ref_[i]; lxref_[i] = o.lxref_[i]; xshift_[i] = o.xshift_[i]; rshift_[i] = o.rshift_[i]; } x0_ = o.x0_; y0_ = o.y0_; z0_ = o.z0_; xl_ = o.xl_; yl_ = o.yl_; zl_ = o.zl_; ox_ = o.ox_; oy_ = o.oy_; oz_ = o.oz_; oax_= o.oax_; oay_ = o.oay_; oaz_ = o.oaz_; nx_ = o.nx_; ny_=o.ny_; nz_=o.nz_; return *this; } /*! cut a grid level to the specified size * @param ilevel grid level on which to perform the size adjustment * @param nx new x-extent in fine grid cells * @param ny new y-extent in fine grid cells * @param nz new z-extent in fine grid cells * @param oax new x-offset in units fine grid units * @param oay new y-offset in units fine grid units * @param oaz new z-offset in units fine grid units */ void adjust_level( unsigned ilevel, int nx, int ny, int nz, int oax, int oay, int oaz ) { double h = 1.0/(1< grid_hierarchy; typedef MeshvarBnd meshvar_bnd; typedef Meshvar meshvar; #endif