diff --git a/src/blockforest/python/Exports.cpp b/src/blockforest/python/Exports.cpp index 66fd6c3d9d4fbf44a61253fe820f5918ca7783a4..181d14bf1197e457a596f53ee4ddb834c144ff7e 100644 --- a/src/blockforest/python/Exports.cpp +++ b/src/blockforest/python/Exports.cpp @@ -28,6 +28,9 @@ #include "blockforest/communication/UniformBufferedScheme.h" #include "blockforest/Initialization.h" #include "blockforest/SetupBlock.h" +#include "blockforest/SetupBlockForest.h" +#include "blockforest/loadbalancing/StaticCurve.h" + #include "core/logging/Logging.h" #include "domain_decomposition/StructuredBlockStorage.h" #include "python_coupling/Manager.h" @@ -38,6 +41,7 @@ #include "stencil/D3Q27.h" #include <boost/algorithm/string.hpp> +#include <sstream> #ifdef _MSC_VER # pragma warning(push) @@ -131,6 +135,113 @@ object python_createUniformBlockGrid(tuple args, dict kw) } +shared_ptr<StructuredBlockForest> createStructuredBlockForest( Vector3<uint_t> blocks, + Vector3<uint_t> cellsPerBlock, + Vector3<bool> periodic, + object blockExclusionCallback = object(), + object workloadMemoryCallback = object(), + object refinementCallback = object(), + const real_t dx = 1.0, + memory_t processMemoryLimit = std::numeric_limits<memory_t>::max(), + const bool keepGlobalBlockInformation = false) +{ + using namespace blockforest; + Vector3<real_t> bbMax; + for( uint_t i=0; i < 3; ++i ) + bbMax[i] = real_c( blocks[i] * cellsPerBlock[i] ) * dx; + AABB domainAABB ( Vector3<real_t>(0), bbMax ); + + SetupBlockForest sforest; + + auto blockExclusionFunc = [&blockExclusionCallback] ( std::vector<walberla::uint8_t>& excludeBlock, const SetupBlockForest::RootBlockAABB& aabb ) -> void + { + for( uint_t i = 0; i != excludeBlock.size(); ++i ) + { + AABB bb = aabb(i); + auto pythonReturnVal = blockExclusionCallback(bb); + if( ! extract<bool>( pythonReturnVal ).check() ) { + PyErr_SetString( PyExc_ValueError, "blockExclusionCallback has to return a boolean"); + throw boost::python::error_already_set(); + } + + bool returnVal = extract<bool>(pythonReturnVal); + if ( returnVal ) + excludeBlock[i] = 1; + } + }; + + auto workloadMemoryFunc = [&workloadMemoryCallback] ( SetupBlockForest & forest )-> void + { + std::vector< SetupBlock* > blockVector; + forest.getBlocks( blockVector ); + + for( uint_t i = 0; i != blockVector.size(); ++i ) { + blockVector[i]->setMemory( memory_t(1) ); + blockVector[i]->setWorkload( workload_t(1) ); + workloadMemoryCallback( boost::python::ptr(blockVector[i]) ); + } + }; + + auto refinementFunc = [&refinementCallback] ( SetupBlockForest & forest )-> void + { + for( auto block = forest.begin(); block != forest.end(); ++block ) + { + SetupBlock * sb = &(*block); + auto pythonRes = refinementCallback( boost::python::ptr(sb) ); + if( ! extract<bool>( pythonRes ).check() ) { + PyErr_SetString( PyExc_ValueError, "refinementCallback has to return a boolean"); + throw boost::python::error_already_set(); + } + bool returnVal = extract<bool>( pythonRes ); + if( returnVal ) + block->setMarker( true ); + } + }; + + if ( blockExclusionCallback ) { + if( !PyCallable_Check( blockExclusionCallback.ptr() ) ) { + PyErr_SetString( PyExc_ValueError, "blockExclusionCallback has to be callable"); + throw boost::python::error_already_set(); + } + sforest.addRootBlockExclusionFunction( blockExclusionFunc ); + } + + if ( workloadMemoryCallback ) { + if( !PyCallable_Check( workloadMemoryCallback.ptr() ) ) { + PyErr_SetString( PyExc_ValueError, "workloadMemoryCallback has to be callable"); + throw boost::python::error_already_set(); + } + sforest.addWorkloadMemorySUIDAssignmentFunction( workloadMemoryFunc ); + } + else + sforest.addWorkloadMemorySUIDAssignmentFunction( uniformWorkloadAndMemoryAssignment ); + + if ( refinementCallback ) { + if( !PyCallable_Check( refinementCallback.ptr() ) ) { + PyErr_SetString( PyExc_ValueError, "refinementCallback has to be callable"); + throw boost::python::error_already_set(); + } + sforest.addRefinementSelectionFunction( refinementFunc ); + } + + sforest.init( domainAABB, blocks[0], blocks[1], blocks[2], periodic[0], periodic[1], periodic[2] ); + + // calculate process distribution + sforest.balanceLoad( blockforest::StaticLevelwiseCurveBalanceWeighted(), + uint_c( MPIManager::instance()->numProcesses() ), + real_t(0), processMemoryLimit ); + + if( !MPIManager::instance()->rankValid() ) + MPIManager::instance()->useWorldComm(); + + // create StructuredBlockForest (encapsulates a newly created BlockForest) + auto bf = shared_ptr< BlockForest >( new BlockForest( uint_c( MPIManager::instance()->rank() ), sforest, keepGlobalBlockInformation ) ); + + auto sbf = shared_ptr< StructuredBlockForest >( new StructuredBlockForest( bf, cellsPerBlock[0], cellsPerBlock[1], cellsPerBlock[2] ) ); + sbf->createCellBoundingBoxes(); + + return sbf; +} object createUniformNeighborScheme( const shared_ptr<StructuredBlockForest> & bf, const std::string & stencil ) @@ -164,6 +275,12 @@ void exportUniformBufferedScheme() } +std::string printSetupBlock(const SetupBlock & b ) +{ + std::stringstream out; + out << "SetupBlock at " << b.getAABB(); + return out.str(); +} @@ -174,12 +291,12 @@ void exportBlockForest() bases<StructuredBlockStorage>, boost::noncopyable > ( "StructuredBlockForest", no_init ); class_< SetupBlock, boost::noncopyable > ( "SetupBlock", no_init ) - .def( "getLevel", &SetupBlock::getLevel ) - .def( "getWorkload", &SetupBlock::getWorkload ) - .def( "setWorkload", &SetupBlock::setWorkload ) - .def( "getMemory", &SetupBlock::getMemory ) - .def( "setMemory", &SetupBlock::setMemory ) - ; + .add_property("level", &SetupBlock::getLevel) + .add_property("workload", &SetupBlock::getWorkload, &SetupBlock::setWorkload) + .add_property("memory", &SetupBlock::getMemory, &SetupBlock::setMemory) + .add_property("aabb", make_function(&SetupBlock::getAABB, return_value_policy<copy_const_reference>())) + .def("__repr__", &printSetupBlock) + ; #ifdef _MSC_VER # pragma warning(push) @@ -191,6 +308,14 @@ void exportBlockForest() # pragma warning(pop) #endif //_MSC_VER + def( "createCustomBlockGrid", createStructuredBlockForest, + (arg("blocks"), arg("cellsPerBlock"), arg("periodic"), + arg("blockExclusionCallback") = object(), + arg("workloadMemoryCallback") = object(), + arg("refinementCallback") = object() , + arg("dx") = 1.0, + arg("processMemoryLimit") = std::numeric_limits<memory_t>::max(), + arg("keepGlobalBlockInformation") = false ) ); } } // namespace domain_decomposition diff --git a/src/field/blockforest/BlockDataHandling.h b/src/field/blockforest/BlockDataHandling.h index 3d6d92a622287bd11d341e14d1e62a707aec464f..f2925452f49a4d0334a830f96986581d95df56ba 100644 --- a/src/field/blockforest/BlockDataHandling.h +++ b/src/field/blockforest/BlockDataHandling.h @@ -24,6 +24,8 @@ #include "blockforest/BlockDataHandling.h" #include "blockforest/StructuredBlockForest.h" #include "core/debug/CheckFunctions.h" +#include "core/math/Vector2.h" +#include "core/math/Vector3.h" #include "field/FlagField.h" @@ -81,6 +83,9 @@ protected: template< typename T > struct Merge { static T result( const T & value ) { return Pseudo2D ? static_cast<T>( value / numeric_cast<T>(4) ) : static_cast<T>( value / numeric_cast<T>(8) ); } }; + template< typename T > struct Merge< Vector2<T> > + { static Vector2<T> result( const Vector2<T> & value ) { return Pseudo2D ? (value / numeric_cast<T>(4)) : (value / numeric_cast<T>(8)); } }; + template< typename T > struct Merge< Vector3<T> > { static Vector3<T> result( const Vector3<T> & value ) { return Pseudo2D ? (value / numeric_cast<T>(4)) : (value / numeric_cast<T>(8)); } }; diff --git a/src/field/python/FieldExport.impl.h b/src/field/python/FieldExport.impl.h index c8c2936a2b80b87a2ab48d3dcad25c5ef0832a66..79571333f9d6736d79fa79037dc9e79a958ec846 100644 --- a/src/field/python/FieldExport.impl.h +++ b/src/field/python/FieldExport.impl.h @@ -517,7 +517,7 @@ namespace internal { } template< typename GhostLayerField_T > - class GhostLayerFieldDataHandling : public blockforest::AlwaysInitializeBlockDataHandling< GhostLayerField_T > + class GhostLayerFieldDataHandling : public field::BlockDataHandling< GhostLayerField_T > { public: typedef typename GhostLayerField_T::value_type Value_T; @@ -527,7 +527,7 @@ namespace internal { blocks_( blocks ), nrOfGhostLayers_( nrOfGhostLayers ), initValue_( initValue ), layout_( layout ), alignment_( alignment ) {} - GhostLayerField_T * initialize( IBlock * const block ) + GhostLayerField_T * allocate( IBlock * const block ) { auto blocks = blocks_.lock(); WALBERLA_CHECK_NOT_NULLPTR( blocks, "Trying to access 'AlwaysInitializeBlockDataHandling' for a block " @@ -540,6 +540,11 @@ namespace internal { return field; } + GhostLayerField_T * reallocate( IBlock * const block ) + { + return allocate(block); + } + private: weak_ptr< StructuredBlockStorage > blocks_; diff --git a/src/mesh/python/Exports.impl.h b/src/mesh/python/Exports.impl.h index d47cd2f7da53984c33cf93b8df31fb8e832f110c..5bf6e5b7a7bf9e78deb84d02dfa84be111a2fdcb 100644 --- a/src/mesh/python/Exports.impl.h +++ b/src/mesh/python/Exports.impl.h @@ -118,6 +118,33 @@ namespace internal } +bool Octree_isAABBFullyInside(const Octree & octree, const AABB & aabb) +{ + for( auto corner: aabb.corners() ) + { + const Octree::Point p ( numeric_cast<Octree::Scalar>(corner[0]), + numeric_cast<Octree::Scalar>(corner[1]), + numeric_cast<Octree::Scalar>(corner[2]) ); + if( octree.sqSignedDistance(p) > 0 ) + return false; + } + return true; +} + + +bool Octree_isAABBFullyOutside(const Octree & octree, const AABB & aabb) +{ + for( auto corner: aabb.corners() ) + { + const Octree::Point p ( numeric_cast<Octree::Scalar>(corner[0]), + numeric_cast<Octree::Scalar>(corner[1]), + numeric_cast<Octree::Scalar>(corner[2]) ); + if( octree.sqSignedDistance(p) < 0 ) + return false; + } + return true; +} + template<typename FlagFields> void exportModuleToPython() @@ -143,6 +170,8 @@ void exportModuleToPython() .def("sqDistance", sqDistance1, (arg("p"))) .def("height", &Octree::height) .def("writeVTKOutput", &Octree::writeVTKOutput, (arg("filestem"))) + .def("isAABBfullyOutside", &Octree_isAABBFullyOutside) + .def("isAABBfullyInside", &Octree_isAABBFullyInside) ; class_<MeshWriter, shared_ptr<MeshWriter> >("VTKMeshWriter", no_init) @@ -150,7 +179,6 @@ void exportModuleToPython() .def("__call__", &MeshWriter::operator()) ; - } diff --git a/src/python_coupling/basic_exports/BasicExports.cpp b/src/python_coupling/basic_exports/BasicExports.cpp index 724efcfce421d3d171604a59617c3e4d6dfa15bc..28c8c12d3ddfcc3031da670f7440a1d3bc1e5dde 100644 --- a/src/python_coupling/basic_exports/BasicExports.cpp +++ b/src/python_coupling/basic_exports/BasicExports.cpp @@ -32,6 +32,7 @@ #include "core/Abort.h" #include "core/cell/CellInterval.h" #include "core/math/AABB.h" +#include "core/mpi/MPIIO.h" #include "core/timing/TimingPool.h" #include "core/timing/TimingTree.h" #include "communication/UniformPackInfo.h" @@ -748,6 +749,13 @@ bool IBlock_equals( IBlock & block1, IBlock & block2 ) return block1.getId() == block2.getId(); } +std::string IBlock_str( IBlock & b ) { + std::stringstream out; + out << "Block at " << b.getAABB(); + return out.str(); + +} + void exportIBlock() { register_exception_translator<NoSuchBlockData>( & NoSuchBlockData::translate ); @@ -761,7 +769,10 @@ void exportIBlock() .add_property( "id", &IBlock_getIntegerID) .def ( "__hash__", &IBlock_getIntegerID) .def ( "__eq__", &IBlock_equals) - .add_property( "__iter__", &IBlock_iter ); + .def ( "__repr__", &IBlock_str ) + .add_property( "__iter__", &IBlock_iter ) + .add_property("aabb", make_function(&IBlock::getAABB, return_value_policy<copy_const_reference>())) + ; } @@ -950,6 +961,24 @@ object SbS_transformLocalToGlobal ( StructuredBlockStorage & s, IBlock & block, throw error_already_set(); } +void SbS_writeBlockData( StructuredBlockStorage & s,const std::string & blockDataId, const std::string & file ) +{ + mpi::SendBuffer buffer; + s.serializeBlockData( blockDataIDFromString(s, blockDataId), buffer); + mpi::writeMPIIO(file, buffer); +} + +void SbS_readBlockData( StructuredBlockStorage & s,const std::string & blockDataId, const std::string & file ) +{ + mpi::RecvBuffer buffer; + mpi::readMPIIO(file, buffer); + + s.deserializeBlockData( blockDataIDFromString(s, blockDataId), buffer ); + if ( ! buffer.isEmpty() ) { + PyErr_SetString(PyExc_RuntimeError, "Reading failed - file does not contain matching data for this type." ); + throw error_already_set(); + } +} CellInterval SbS_getBlockCellBB( StructuredBlockStorage & s, const IBlock * block ) { @@ -1039,10 +1068,11 @@ void exportStructuredBlockStorage() .def( "getBlockCellBB", &SbS_getBlockCellBB ) .def( "transformGlobalToLocal", &SbS_transformGlobalToLocal ) .def( "transformLocalToGlobal", &SbS_transformLocalToGlobal ) - .add_property("__iter__", &StructuredBlockStorage_iter ) + .def( "writeBlockData", &SbS_writeBlockData ) + .def( "readBlockData", &SbS_readBlockData ) + .add_property("__iter__", &StructuredBlockStorage_iter ) .add_property( "containsGlobalBlockInformation", &StructuredBlockStorage::containsGlobalBlockInformation ) .add_property( "periodic", &SbS_periodic ) - ; #if BOOST_VERSION < 106300 diff --git a/utilities/conda/walberla/build.sh b/utilities/conda/walberla/build.sh index b873eaf14260af480a8b078bd11c37744b807225..ac6ff0002fadda4361c8cc2f984440678fd13a3c 100644 --- a/utilities/conda/walberla/build.sh +++ b/utilities/conda/walberla/build.sh @@ -6,7 +6,7 @@ cmake \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DWALBERLA_BUILD_WITH_PYTHON=ON \ -DWALBERLA_BUILD_WITH_PYTHON_MODULE=ON \ - -DWALBERLA_BUILD_WITH_PYTHON_LBM=ON \ + -DWALBERLA_BUILD_WITH_PYTHON_LBM=OFF \ .. make -j ${CPU_COUNT} pythonModuleInstall