diff --git a/.gitignore b/.gitignore index a0d82191669d3d3fbb30e64fdf028852e0c990f9..91448c9a7457db41013e441f403c70755ddc358d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,7 @@ CMakeLists.txt.user.* /bin/ /lib/ *.a -/build +/build* # Logfiles diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1a58571600fb6788aa0d0b6b458e62ea5f5073c5..0387e0998a753afeba11bcc668d4bd31110f29e2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,7 +9,7 @@ stages: - test - deploy - + ############################################################################### ## ## ## Build templates ## @@ -1657,8 +1657,8 @@ coverage: ## Windows Builds ## ## ## ############################################################################### - - + + .win_build_template: &win_build_definition tags: - win @@ -1687,7 +1687,7 @@ msvc-14.1_Hybrid_Dbg: only: variables: - $ENABLE_NIGHTLY_BUILDS - + msvc-14.1_Hybrid_SP_Dbg: <<: *win_build_definition variables: @@ -1700,7 +1700,7 @@ msvc-14.1_Hybrid_SP_Dbg: only: variables: - $ENABLE_NIGHTLY_BUILDS - + msvc-14.1_Hybrid: <<: *win_build_definition variables: @@ -1713,7 +1713,7 @@ msvc-14.1_Hybrid: only: variables: - $ENABLE_NIGHTLY_BUILDS - + msvc-14.1_Serial_Dbg: <<: *win_build_definition variables: @@ -1726,7 +1726,7 @@ msvc-14.1_Serial_Dbg: only: variables: - $ENABLE_NIGHTLY_BUILDS - + msvc-14.1_Serial: <<: *win_build_definition variables: @@ -1739,7 +1739,7 @@ msvc-14.1_Serial: only: variables: - $ENABLE_NIGHTLY_BUILDS - + msvc-14.1_MpiOnly_Dbg: <<: *win_build_definition variables: @@ -1752,7 +1752,7 @@ msvc-14.1_MpiOnly_Dbg: only: variables: - $ENABLE_NIGHTLY_BUILDS - + msvc-14.1_MpiOnly: <<: *win_build_definition variables: @@ -1778,7 +1778,7 @@ msvc-14.2_Hybrid_Dbg: except: variables: - $DISABLE_PER_COMMIT_BUILDS - + msvc-14.2_Hybrid_SP_Dbg: <<: *win_build_definition variables: @@ -1791,7 +1791,7 @@ msvc-14.2_Hybrid_SP_Dbg: except: variables: - $DISABLE_PER_COMMIT_BUILDS - + msvc-14.2_Hybrid: <<: *win_build_definition variables: @@ -1804,7 +1804,7 @@ msvc-14.2_Hybrid: except: variables: - $DISABLE_PER_COMMIT_BUILDS - + msvc-14.2_Serial_Dbg: <<: *win_build_definition variables: @@ -1817,7 +1817,7 @@ msvc-14.2_Serial_Dbg: except: variables: - $DISABLE_PER_COMMIT_BUILDS - + msvc-14.2_Serial: <<: *win_build_definition variables: @@ -1830,7 +1830,7 @@ msvc-14.2_Serial: only: variables: - $ENABLE_NIGHTLY_BUILDS - + msvc-14.2_MpiOnly_Dbg: <<: *win_build_definition variables: @@ -1843,7 +1843,7 @@ msvc-14.2_MpiOnly_Dbg: except: variables: - $DISABLE_PER_COMMIT_BUILDS - + msvc-14.2_MpiOnly: <<: *win_build_definition variables: @@ -1949,7 +1949,7 @@ mac_MpiOnly: dependencies: [] when: manual only: - - master@walberla/walberla + - master@walberla/walberla - tags@walberla/walberla conda-py36-win: @@ -1958,7 +1958,7 @@ conda-py36-win: - win script: - conda build --python=3.6 --user=lssfau utilities\\conda\\walberla - + conda-py37-win: <<: *conda_deploy_definition tags: @@ -1975,7 +1975,7 @@ conda-py37-linux: - apt-get update - apt-get install -y build-essential - conda build --python=3.7 --user=lssfau utilities/conda/walberla - + conda-py36-linux: <<: *conda_deploy_definition tags: diff --git a/apps/benchmarks/UniformGrid/UniformGrid.cpp b/apps/benchmarks/UniformGrid/UniformGrid.cpp index a55ca158317e82e716b19773224c1d8673656d17..d5d355e491812c639ac1b4fca5a982622c19af5e 100644 --- a/apps/benchmarks/UniformGrid/UniformGrid.cpp +++ b/apps/benchmarks/UniformGrid/UniformGrid.cpp @@ -1,15 +1,15 @@ //====================================================================================================================== // -// This file is part of waLBerla. waLBerla is free software: you can +// 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 +// 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 +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. -// +// // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // @@ -269,31 +269,20 @@ void createSetupBlockForest( blockforest::SetupBlockForest & sforest, const Conf WALBERLA_MPI_SECTION() { - if ( MPIManager::instance()->isCartesianCommValid() ) - { - MPIManager::instance()->createCartesianComm(numberOfXProcesses, numberOfYProcesses, numberOfZProcesses, false, false, false); + MPIManager::instance()->createCartesianComm(numberOfXProcesses, numberOfYProcesses, numberOfZProcesses, false, false, false); - processIdMap = make_shared<std::vector<uint_t> >(numberOfXProcesses * numberOfYProcesses * numberOfZProcesses); + processIdMap = make_shared<std::vector<uint_t> >(numberOfXProcesses * numberOfYProcesses * numberOfZProcesses); - for (uint_t z = 0; z != numberOfZProcesses; ++z) { - for (uint_t y = 0; y != numberOfYProcesses; ++y) { - for (uint_t x = 0; x != numberOfXProcesses; ++x) - { - (*processIdMap)[z * numberOfXProcesses * numberOfYProcesses + y * numberOfXProcesses + x] = - uint_c(MPIManager::instance()->cartesianRank(x, y, z)); - } + for (uint_t z = 0; z != numberOfZProcesses; ++z) { + for (uint_t y = 0; y != numberOfYProcesses; ++y) { + for (uint_t x = 0; x != numberOfXProcesses; ++x) + { + (*processIdMap)[z * numberOfXProcesses * numberOfYProcesses + y * numberOfXProcesses + x] = + uint_c(MPIManager::instance()->cartesianRank(x, y, z)); } } } - else { - WALBERLA_LOG_WARNING_ON_ROOT( "Your version of OpenMPI contains a bug. See waLBerla issue #73 for more " - "information. As a workaround, MPI_COMM_WORLD instead of a " - "Cartesian MPI communicator is used." ); - MPIManager::instance()->useWorldComm(); - } } - else - MPIManager::instance()->useWorldComm(); sforest.balanceLoad( blockforest::CartesianDistribution( numberOfXProcesses, numberOfYProcesses, numberOfZProcesses, processIdMap.get() ), numberOfXProcesses * numberOfYProcesses * numberOfZProcesses ); @@ -615,7 +604,7 @@ struct AddLB< LatticeModel_T, typename std::enable_if< std::is_same< typename La std::function< void () > commFunction; if( directComm ) { - + if( fullComm ) { blockforest::communication::UniformDirectScheme< stencil::D3Q27 > comm( blocks ); diff --git a/src/blockforest/BlockForest.cpp b/src/blockforest/BlockForest.cpp index a156b7e3f0a77352d82ab2bea02851038fbade58..cfe71d653f3835014ba7db5f28591dd31772742d 100644 --- a/src/blockforest/BlockForest.cpp +++ b/src/blockforest/BlockForest.cpp @@ -1,15 +1,15 @@ //====================================================================================================================== // -// This file is part of waLBerla. waLBerla is free software: you can +// 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 +// 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 +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. -// +// // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // @@ -672,7 +672,7 @@ void BlockForest::getAABB( AABB& aabb, const IBlockID& id ) const { } else { const Block* const block = getBlock( id ); - if( block == nullptr ) + if( block == nullptr ) { const BlockID& bid = *static_cast< const BlockID* >( &id ); aabb = getAABBFromBlockId( bid ); @@ -906,7 +906,7 @@ void BlockForest::refresh() WALBERLA_ASSERT_NOT_NULLPTR( block->second ); block->second->setTargetLevel( block->second->getLevel() ); } - + if( recalculateBlockLevelsInRefresh_ ) { WALBERLA_LOG_PROGRESS( "BlockForest refresh: determining new block levels" ); @@ -929,22 +929,22 @@ void BlockForest::refresh() if( alwaysRebalanceInRefresh_ && refreshPhantomBlockMigrationPreparationFunction_ ) rebalanceAndRedistribute = true; - + while( rebalanceAndRedistribute ) { // create phantom block forest - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: creating phantom forest/blocks" ); refreshTiming_[ "phantom forest creation" ].start(); PhantomBlockForest phantomForest( *this ); phantomForest.initialize( refreshBlockStateDeterminationFunction_, allowChangingDepth_ ); - + refreshTiming_[ "phantom forest creation" ].end(); // move phantom blocks between processes (= dynamic load balancing) - + WALBERLA_MPI_SECTION() { refreshTiming_[ "phantom block redistribution (= load balancing)" ].start(); @@ -966,15 +966,15 @@ void BlockForest::refresh() ++iteration; } phantomBlockMigrationIterations_ = iteration; - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: phantom block redistribution/load balancing finished after " << phantomBlockMigrationIterations_ << " iterations" ); } refreshTiming_[ "phantom block redistribution (= load balancing)" ].end(); } - + // update block forest: transfer block data (includes migrating, deleting and creating blocks), adapt depth, adapt process neighborhood, etc. - + bool performUpdate( true ); if( checkForEarlyOutAfterLoadBalancing_ ) { @@ -1010,23 +1010,23 @@ void BlockForest::refresh() { WALBERLA_ASSERT( recalculateBlockLevelsInRefresh_ ); WALBERLA_ASSERT( allowMultipleRefreshCycles_ ); - + refreshTiming_[ "block level determination" ].start(); - + for( auto block = blocks_.begin(); block != blocks_.end(); ++block ) { WALBERLA_ASSERT_NOT_NULLPTR( block->second ); block->second->setTargetLevel( block->second->getLevel() ); } - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: determining new block levels (again -> more refresh cycles required!)" ); - + bool rerun( true ); while( rerun ) { rebalanceAndRedistribute = determineBlockTargetLevels( additionalRefreshCycleRequired, rerun ); } - + if( !rebalanceAndRedistribute ) WALBERLA_LOG_PROGRESS( "BlockForest refresh: block levels do not change" ); @@ -1035,7 +1035,7 @@ void BlockForest::refresh() else rebalanceAndRedistribute = false; } - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: finished!" ); } @@ -1267,7 +1267,7 @@ void BlockForest::restoreSnapshot( const SnapshotRestorenFunction & processMappi ++iteration; } phantomBlockMigrationIterations_ = iteration; - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: phantom block redistribution/load balancing finished after " << phantomBlockMigrationIterations_ << " iterations" ); } @@ -1651,7 +1651,7 @@ bool BlockForest::determineBlockTargetLevels( bool & additionalRefreshCycleRequi const uint_t level = block->getLevel(); uint_t minTargetLevel = minTargetLevels[id]; - + if( allowMultipleRefreshCycles_ ) { if( minTargetLevel > ( level + uint_t(1) ) ) @@ -1683,7 +1683,7 @@ bool BlockForest::determineBlockTargetLevels( bool & additionalRefreshCycleRequi } } } - + if( allowMultipleRefreshCycles_ ) mpi::allReduceInplace( additionalRefreshCycleRequired, mpi::LOGICAL_OR ); @@ -2037,7 +2037,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) ////////////////////////////////////// std::map< uint_t, uint_t > numberOfBlocksToRecv; // does not include local transfers - + const auto & phantomBlocks = phantomForest.getBlockMap(); for( auto phantom = phantomBlocks.begin(); phantom != phantomBlocks.end(); ++phantom ) { @@ -2061,15 +2061,15 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) else { WALBERLA_ASSERT( blocks_.find( pBlock->getId() ) != blocks_.end() ); } #endif } - + std::map< uint_t, std::vector< uint_t > > recvBufferSizes; // does not include local transfers - + for( auto it = numberOfBlocksToRecv.begin(); it != numberOfBlocksToRecv.end(); ++it ) { WALBERLA_ASSERT_GREATER( it->second, uint_t(0) ); recvBufferSizes[ it->first ].resize( it->second ); } - + std::vector< MPI_Request > recvBufferSizesRequests( numberOfBlocksToRecv.size() ); // do not resize this vector! WALBERLA_LOG_PROGRESS( "BlockForest refresh: - schedule receives for buffer sizes" ); @@ -2081,15 +2081,15 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) int_c( it->first ), 0, MPIManager::instance()->comm(), &(recvBufferSizesRequests[i]) ); ++i; } - + /////////////////////////////// // FETCH TARGET BLOCK STATES // /////////////////////////////// WALBERLA_LOG_PROGRESS( "BlockForest refresh: - fetch target block states" ); - + std::set< mpi::MPIRank > ranksToRecvFrom; - + for( auto it = blocks_.begin(); it != blocks_.end(); ++it ) { auto & block = it->second; @@ -2102,12 +2102,12 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) ranksToRecvFrom.insert( numeric_cast< mpi::MPIRank >(*p) ); } } - + mpi::BufferSystem bufferSystem( MPIManager::instance()->comm(), 1184 ); // blockforest = 98 108 111 99 107 102 111 114 101 115 116 + 2 - bufferSystem.setReceiverInfo( ranksToRecvFrom, true ); // ATTENTION: true = the size of a message from A to B varies - + bufferSystem.setReceiverInfo( ranksToRecvFrom, true ); // ATTENTION: true = the size of a message from A to B varies + std::map< uint_t, std::map< BlockID, Set<SUID> > > blockStates; - + for( auto phantom = phantomBlocks.begin(); phantom != phantomBlocks.end(); ++phantom ) { auto & pBlock = phantom->second; @@ -2126,9 +2126,9 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) } bufferSystem.sendAll(); - + std::map< BlockID, Set<SUID> > targetBlockStates; - + for( auto recvIt = bufferSystem.begin(); recvIt != bufferSystem.end(); ++recvIt ) { WALBERLA_ASSERT_UNEQUAL( uint_c( recvIt.rank() ), process_ ); @@ -2147,9 +2147,9 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) { WALBERLA_ASSERT( targetBlockStates.find( state->first ) == targetBlockStates.end() ); targetBlockStates[ state->first ] = state->second; - } + } } - + /////////////// // PACK DATA // /////////////// @@ -2187,7 +2187,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) auto & id = block->getId(); auto & targetProcesses = block->getTargetProcess(); - + std::vector< Set<SUID> > targetState; // header = sender block ID + receiver block ID @@ -2204,7 +2204,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) processesToSendTo[ targetProcesses[0] ].push_back( &(buffers[0]) ); else localBlocks.push_back( &(buffers[0]) ); - + WALBERLA_ASSERT( targetBlockStates.find(id) != targetBlockStates.end() ); targetState.push_back( targetBlockStates[id] ); } @@ -2221,7 +2221,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) processesToSendTo[ targetProcesses[0] ].push_back( &(buffers[0]) ); else localBlocks.push_back( &(buffers[0]) ); - + WALBERLA_ASSERT( targetBlockStates.find(fid) != targetBlockStates.end() ); targetState.push_back( targetBlockStates[fid] ); } @@ -2241,7 +2241,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) processesToSendTo[ targetProcesses[c] ].push_back( &(buffers[c]) ); else localBlocks.push_back( &(buffers[c]) ); - + WALBERLA_ASSERT( targetBlockStates.find(cid) != targetBlockStates.end() ); targetState.push_back( targetBlockStates[cid] ); } @@ -2298,7 +2298,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) WALBERLA_ASSERT_EQUAL( buffers.size(), uint_t(8) ); WALBERLA_ASSERT_EQUAL( targetProcesses.size(), uint_t(8) ); WALBERLA_ASSERT_EQUAL( targetState.size(), uint_t(8) ); - + for( auto dataItem = blockDataItem_.begin(); dataItem != blockDataItem_.end(); ++dataItem ) { for( uint_t c = uint_t(0); c != uint_t(8); ++c ) @@ -2344,12 +2344,12 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) for( auto it = processesToSendTo.begin(); it != processesToSendTo.end(); ++it ) { WALBERLA_ASSERT( sendBufferSizes.find( it->first ) == sendBufferSizes.end() ); - + auto & sizes = sendBufferSizes[ it->first ]; WALBERLA_ASSERT( sizes.empty() ); for( auto buffer = it->second.begin(); buffer != it->second.end(); ++buffer ) sizes.push_back( (*buffer)->size() ); - + blockDataSendRequests[ it->first ].resize( it->second.size() ); // do not resize this vector after this point! } @@ -2378,7 +2378,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) ++i; } - + /////////////////// // CREATE BLOCKS // /////////////////// @@ -2399,7 +2399,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) blocks_[ pBlock->getId() ]->resetNeighborhood( *pBlock ); } } - + // adapt depth WALBERLA_LOG_PROGRESS( "BlockForest refresh: - adapting block structure (use current state of phantom forest as reference)" ); @@ -2421,21 +2421,21 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) ////////////////////////////////////// // WAIT FOR RECV's FOR BUFFER SIZES // ////////////////////////////////////// - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: - wait for buffer sizes" ); if( ! recvBufferSizesRequests.empty() ) MPI_Waitall( int_c( recvBufferSizesRequests.size() ), &(recvBufferSizesRequests[0]), MPI_STATUSES_IGNORE ); - + //////////////////////////////////// // SCHEDULE RECV's FOR BLOCK DATA // //////////////////////////////////// - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: - schedule block data receive operations" ); std::map< uint_t, std::vector< mpi::RecvBuffer > > recvBlockData; std::map< uint_t, std::vector< MPI_Request > > blockDataRecvRequests; - + for( auto it = recvBufferSizes.begin(); it != recvBufferSizes.end(); ++it ) { WALBERLA_ASSERT_UNEQUAL( it->first, process_ ); @@ -2446,7 +2446,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) requests.resize( sizes.size() ); } auto & recvLocalBlocks = recvBlockData[ process_ ]; - + for( auto it = recvBufferSizes.begin(); it != recvBufferSizes.end(); ++it ) { WALBERLA_ASSERT_UNEQUAL( it->first, process_ ); @@ -2462,11 +2462,11 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) int_c( it->first ), int_c(i)+1, MPIManager::instance()->comm(), &(requests[i]) ); } } - + /////////////////////////// // COPY LOCAL BLOCK DATA // /////////////////////////// - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: - perform local data transfer" ); for( auto buffer = localBlocks.begin(); buffer != localBlocks.end(); ++buffer ) @@ -2475,7 +2475,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) //////////////////////////////////// // WAIT FOR RECV's FOR BLOCK DATA // //////////////////////////////////// - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: - wait for block data to be received" ); for( auto it = blockDataRecvRequests.begin(); it != blockDataRecvRequests.end(); ++it ) @@ -2483,16 +2483,16 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) auto & requests = it->second; MPI_Waitall( int_c( requests.size() ), &(requests[0]), MPI_STATUSES_IGNORE ); } - + ////////////////////////////////// // WAIT FOR ALL SENDS TO FINISH // ////////////////////////////////// - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: - wait for block data sends to complete" ); if( ! sendBufferSizesRequests.empty() ) MPI_Waitall( int_c( sendBufferSizesRequests.size() ), &(sendBufferSizesRequests[0]), MPI_STATUSES_IGNORE ); - + for( auto it = blockDataSendRequests.begin(); it != blockDataSendRequests.end(); ++it ) { auto & requests = it->second; @@ -2502,7 +2502,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) //////////////////////////////////////// // CLEAR SEND BUFFERS (= FREE MEMORY) // //////////////////////////////////////// - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: - clear send buffers (= free memory)" ); for( auto it = blocksToPack.begin(); it != blocksToPack.end(); ++it ) @@ -2515,11 +2515,11 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) //////////////////////////////// // PREPARE DATA FOR UNPACKING // //////////////////////////////// - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: - prepare received data for unpacking" ); std::map< Block *, std::vector< std::pair< Set<SUID>, mpi::RecvBuffer * > > > blocksToUnpack; // includes data that is NOT transfered via MPI but copied locally - + for( auto it = recvBlockData.begin(); it != recvBlockData.end(); ++it ) { auto & buffers = it->second; @@ -2536,7 +2536,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) WALBERLA_ASSERT( phantomBlocks.find( rId ) != phantomBlocks.end() ); Block * block = blocks_[ rId ].get(); const auto & phantom = phantomBlocks.find(rId)->second; - + if( phantom->sourceBlockHasTheSameSize() || phantom->sourceBlockIsLarger() ) { WALBERLA_ASSERT( blocksToUnpack.find( block ) == blocksToUnpack.end() ); @@ -2552,7 +2552,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) } } } - + #ifndef NDEBUG for( auto it = phantomBlocks.begin(); it != phantomBlocks.end(); ++it ) { @@ -2571,7 +2571,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) ////////////////////////////// // GLOBAL BLOCK INFORMATION // ////////////////////////////// - + // adapt global block information stored on every process (this is only done if the block forest is set-up to store global information!) if( containsGlobalBlockInformation() ) @@ -2579,7 +2579,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) WALBERLA_LOG_PROGRESS( "BlockForest refresh: - update global block information that is stored locally on every process" ); constructBlockInformation(); } - + ++modificationStamp_; ////////////// @@ -2595,7 +2595,7 @@ void BlockForest::update( PhantomBlockForest & phantomForest ) ///////////////// // UNPACK DATA // ///////////////// - + WALBERLA_LOG_PROGRESS( "BlockForest refresh: - unpacking block data from buffers" ); std::vector< std::pair< Block *, std::vector< std::pair< Set<SUID>, mpi::RecvBuffer * > > > > dataToUnpack; @@ -2870,6 +2870,13 @@ void BlockForest::saveToFile( const std::string & filename, FileIOMode fileIOMod fileIOMode = MASTER_SLAVE; } + // use serial I/O for versions of OpenMPI that produce segmentation faults when using MPI-IO with a 3D Cartesian MPI + // communicator (see waLBerla issue #73) + if (!MPIManager::instance()->isCommMPIIOValid()) + { + fileIOMode = MASTER_SLAVE; + } + if( fileIOMode == MPI_PARALLEL ) { MPI_File mpiFile = MPI_FILE_NULL; diff --git a/src/blockforest/Initialization.cpp b/src/blockforest/Initialization.cpp index 8771996fbe2715a09e1a8f39876ad85403435848..11d5243e219e826559d0a9e7c9f9ac0cc1ea74fb 100644 --- a/src/blockforest/Initialization.cpp +++ b/src/blockforest/Initialization.cpp @@ -1,15 +1,15 @@ //====================================================================================================================== // -// This file is part of waLBerla. waLBerla is free software: you can +// 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 +// 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 +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. -// +// // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // @@ -230,27 +230,19 @@ createBlockForest( const AABB& domainAABB, //create cartesian communicator only if not yet a cartesian communicator (or other communicator was created) if ( ! mpiManager->rankValid() ) { - if ( mpiManager->isCartesianCommValid() ) { - mpiManager->createCartesianComm( numberOfXProcesses, numberOfYProcesses, numberOfZProcesses, xPeriodic, yPeriodic, zPeriodic ); + mpiManager->createCartesianComm( numberOfXProcesses, numberOfYProcesses, numberOfZProcesses, xPeriodic, yPeriodic, zPeriodic ); - processIdMap.resize( numberOfProcesses ); + processIdMap.resize( numberOfProcesses ); - for( uint_t z = 0; z != numberOfZProcesses; ++z ) { - for( uint_t y = 0; y != numberOfYProcesses; ++y ) { - for( uint_t x = 0; x != numberOfXProcesses; ++x ) { + for( uint_t z = 0; z != numberOfZProcesses; ++z ) { + for( uint_t y = 0; y != numberOfYProcesses; ++y ) { + for( uint_t x = 0; x != numberOfXProcesses; ++x ) { - processIdMap[ z * numberOfXProcesses * numberOfYProcesses + y * numberOfXProcesses + x ] = - uint_c( MPIManager::instance()->cartesianRank(x,y,z) ); - } + processIdMap[ z * numberOfXProcesses * numberOfYProcesses + y * numberOfXProcesses + x ] = + uint_c( MPIManager::instance()->cartesianRank(x,y,z) ); } } } - else { - WALBERLA_LOG_WARNING_ON_ROOT( "Your version of OpenMPI contains a bug. See waLBerla issue #73 for more " - "information. As a workaround, MPI_COMM_WORLD instead of a " - "Cartesian MPI communicator is used." ); - mpiManager->useWorldComm(); - } } } diff --git a/src/core/mpi/MPIIO.cpp b/src/core/mpi/MPIIO.cpp index b8d85cd6db0324064345ab36735a73b420e42f42..b8eb53a7b761ceeba39a8270686ba654b2caf60f 100644 --- a/src/core/mpi/MPIIO.cpp +++ b/src/core/mpi/MPIIO.cpp @@ -17,180 +17,303 @@ //! \ingroup core //! \author Florian Schornbaum <florian.schornbaum@fau.de> //! \author Martin Bauer <martin.bauer@fau.de> +//! \author Christoph Schwarzmeier <christoph.schwarzmeier@fau.de> // //====================================================================================================================== #include "core/DataTypes.h" +#include "core/logging/Logging.h" +#include "core/mpi/MPIManager.h" #include "core/mpi/RecvBuffer.h" #include "core/mpi/SendBuffer.h" -#include "core/mpi/MPIManager.h" -#include "core/mpi/Reduce.h" -#include <fstream> +#include <fstream> -namespace walberla { -namespace mpi { +namespace walberla +{ +namespace mpi +{ +void writeMPIIO(const std::string& file, SendBuffer& buffer) +{ + uint_t dataSize = sizeof(mpi::SendBuffer::ElementType) * buffer.size(); - void writeMPIIO(const std::string & file, SendBuffer & buffer) + WALBERLA_NON_MPI_SECTION() { - uint_t dataSize = sizeof( mpi::SendBuffer::ElementType ) * buffer.size(); + std::ofstream ofile(file.c_str(), std::ofstream::binary); + if (ofile.fail()) { WALBERLA_ABORT("Error while opening file \"" << file << "\" for writing."); } - WALBERLA_NON_MPI_SECTION() + ofile.write(reinterpret_cast< const char* >(buffer.ptr()), numeric_cast< std::streamsize >(dataSize)); + if (ofile.fail()) { WALBERLA_ABORT("Error while writing to file \"" << file << "\"."); } + + ofile.close(); + if (ofile.fail()) { WALBERLA_ABORT("Error while closing file \"" << file << "\" for reading."); } + } + + WALBERLA_MPI_SECTION() + { + // get the size of the data type to be written in the file header + int uintSize; + MPI_Type_size(MPITrait< uint_t >::type(), &uintSize); + + // get the position at which a process writes its data (offset from beginning of the file) + uint_t exscanResult; + MPI_Exscan(&dataSize, &exscanResult, 1, MPITrait< uint_t >::type(), MPI_SUM, MPIManager::instance()->comm()); + if (MPIManager::instance()->rank() == 0) exscanResult = uint_t(0); + const uint_t offset = uint_c(mpi::MPIManager::instance()->numProcesses() * 2 * uintSize) + exscanResult; + + // header of the file contains each process' offset and buffer size + uint_t offsetData[2]; + offsetData[0] = offset; // position at which a process writes its data + offsetData[1] = uint_c(buffer.size()); + + // use serial I/O for versions of OpenMPI that produce segmentation faults when using MPI-IO with a 3D + // Cartesian MPI communicator (see waLBerla issue #73) + if (!MPIManager::instance()->isCommMPIIOValid()) { - std::ofstream ofile( file.c_str(), std::ofstream::binary ); - ofile.write( reinterpret_cast< const char* >( buffer.ptr() ), numeric_cast< std::streamsize >( dataSize ) ); - ofile.close(); + std::ofstream ofile; + + // write each process' offset and buffer size to file + for (int i = 0; i != MPIManager::instance()->numProcesses(); ++i) + { + if (i == MPIManager::instance()->rank()) + { + if (MPIManager::instance()->rank() == 0) { ofile.open(file.c_str(), std::ofstream::binary); } + else + { + ofile.open(file.c_str(), std::ofstream::binary | std::ofstream::app); + } + if (ofile.fail()) { WALBERLA_ABORT("Error while opening file \"" << file << "\" for writing."); } + + ofile.write(reinterpret_cast< const char* >(offsetData), numeric_cast< std::streamsize >(2 * uintSize)); + if (ofile.fail()) { WALBERLA_ABORT("Error while writing to file \"" << file << "\"."); } + + ofile.close(); + if (ofile.fail()) { WALBERLA_ABORT("Error while closing file \"" << file << "\" for writing."); } + } + WALBERLA_MPI_BARRIER(); + } + + // write each process' data to file (starting at offset) + for (int i = 0; i != MPIManager::instance()->numProcesses(); ++i) + { + if (i == MPIManager::instance()->rank()) + { + ofile.open(file.c_str(), std::ofstream::binary | std::ofstream::app); + if (ofile.fail()) { WALBERLA_ABORT("Error while opening file \"" << file << "\" for writing."); } + + ofile.write(reinterpret_cast< const char* >(buffer.ptr()), numeric_cast< std::streamsize >(dataSize)); + if (ofile.fail()) { WALBERLA_ABORT("Error while writing to file \"" << file << "\"."); } + + ofile.close(); + if (ofile.fail()) { WALBERLA_ABORT("Error while closing file \"" << file << "\" for writing."); } + } + WALBERLA_MPI_BARRIER(); + } } - - WALBERLA_MPI_SECTION() + else // use MPI-IO { MPI_File mpiFile = MPI_FILE_NULL; - int result = MPI_SUCCESS; - result = MPI_File_open( MPIManager::instance()->comm(), const_cast<char*>( file.c_str() ), MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, &mpiFile ); - - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while opening file \"" << file << "\" for writing. MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); - - int uintSize; - MPI_Type_size( MPITrait< uint_t >::type(), &uintSize ); - - const MPI_Offset filesize = numeric_cast<MPI_Offset>( mpi::MPIManager::instance()->numProcesses() * 2 * uintSize ) + - numeric_cast<MPI_Offset>( mpi::allReduce( dataSize, mpi::SUM, MPIManager::instance()->comm() ) ); - MPI_File_set_size( mpiFile, filesize ); + int result = MPI_SUCCESS; + result = MPI_File_open(MPIManager::instance()->comm(), const_cast< char* >(file.c_str()), + MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, &mpiFile); - uint_t exscanResult; - MPI_Exscan( &dataSize, &exscanResult, 1, MPITrait<uint_t>::type(), MPI_SUM, MPIManager::instance()->comm() ); - if( MPIManager::instance()->rank() == 0 ) - exscanResult = uint_t( 0 ); - const uint_t offset = uint_c( mpi::MPIManager::instance()->numProcesses() * 2 * uintSize ) + exscanResult; + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while opening file \"" << file << "\" for writing. MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); MPI_Datatype offsettype; - MPI_Type_contiguous( 2, MPITrait< uint_t >::type(), &offsettype ); - MPI_Type_commit( &offsettype ); + MPI_Type_contiguous(2, MPITrait< uint_t >::type(), &offsettype); + MPI_Type_commit(&offsettype); MPI_Datatype arraytype; - MPI_Type_contiguous( int_c( buffer.size() ), MPITrait< mpi::SendBuffer::ElementType >::type(), &arraytype ); - MPI_Type_commit( &arraytype ); + MPI_Type_contiguous(int_c(buffer.size()), MPITrait< mpi::SendBuffer::ElementType >::type(), &arraytype); + MPI_Type_commit(&arraytype); - // offsets to processes data (+ buffer size) + // write each process' offset and buffer size to file + result = + MPI_File_set_view(mpiFile, numeric_cast< MPI_Offset >(mpi::MPIManager::instance()->rank() * 2 * uintSize), + MPITrait< uint_t >::type(), offsettype, const_cast< char* >("native"), MPI_INFO_NULL); - result = MPI_File_set_view( mpiFile, numeric_cast<MPI_Offset>( mpi::MPIManager::instance()->rank() * 2 * uintSize ), - MPITrait< uint_t >::type(), offsettype, const_cast<char*>( "native" ), MPI_INFO_NULL ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString(result) + << "\""); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + result = MPI_File_write_all(mpiFile, reinterpret_cast< char* >(offsetData), 2, MPITrait< uint_t >::type(), + MPI_STATUS_IGNORE); - uint_t offsetData[2]; - offsetData[0] = offset; - offsetData[1] = uint_c( buffer.size() ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while writing to file \"" << file << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) + << "\""); - result = MPI_File_write_all( mpiFile, reinterpret_cast<char*>( offsetData ), 2, MPITrait< uint_t >::type(), MPI_STATUS_IGNORE ); + // write each process' data to file (starting at offset) + result = MPI_File_set_view(mpiFile, numeric_cast< MPI_Offset >(offset), + MPITrait< mpi::SendBuffer::ElementType >::type(), arraytype, + const_cast< char* >("native"), MPI_INFO_NULL); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while writing to file \"" << file << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString(result) + << "\""); - // the data + result = MPI_File_write_all(mpiFile, reinterpret_cast< char* >(buffer.ptr()), int_c(buffer.size()), + MPITrait< mpi::SendBuffer::ElementType >::type(), MPI_STATUS_IGNORE); - result = MPI_File_set_view( mpiFile, numeric_cast<MPI_Offset>( offset ), MPITrait< mpi::SendBuffer::ElementType >::type(), arraytype, - const_cast<char*>( "native" ), MPI_INFO_NULL ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while writing to file \"" << file << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) + << "\""); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + result = MPI_File_close(&mpiFile); + + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while closing file \"" << file << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); + + MPI_Type_free(&arraytype); + MPI_Type_free(&offsettype); + } + } +} - result = MPI_File_write_all( mpiFile, reinterpret_cast<char*>( buffer.ptr() ), int_c( buffer.size() ), MPITrait< mpi::SendBuffer::ElementType >::type(), MPI_STATUS_IGNORE ); +void readMPIIO(const std::string& file, RecvBuffer& buffer) +{ + WALBERLA_NON_MPI_SECTION() + { + std::ifstream ifile(file.c_str(), std::ifstream::binary); + if (ifile.fail()) { WALBERLA_ABORT("Error while opening file \"" << file << "\" for reading."); } - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while writing to file \"" << file << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + ifile.seekg(0, std::ios::end); + const uint_t length = uint_c(static_cast< std::streamoff >(ifile.tellg())); + ifile.seekg(0, std::ios::beg); - result = MPI_File_close( &mpiFile ); + buffer.resize(length / sizeof(mpi::RecvBuffer::ElementType)); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while closing file \"" << file << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + ifile.read(reinterpret_cast< char* >(buffer.ptr()), numeric_cast< std::streamsize >(length)); + if (ifile.fail()) { WALBERLA_ABORT("Error while reading from file \"" << file << "\"."); } - MPI_Type_free( &arraytype ); - MPI_Type_free( &offsettype ); - } + ifile.close(); + if (ifile.fail()) { WALBERLA_ABORT("Error while closing file \"" << file << "\" for reading."); } } - - void readMPIIO(const std::string & file, RecvBuffer & buffer) + WALBERLA_MPI_SECTION() { - WALBERLA_NON_MPI_SECTION() - { - std::ifstream ifile( file.c_str(), std::ifstream::binary ); - - ifile.seekg( 0, std::ios::end ); - const uint_t length = uint_c( static_cast< std::streamoff >( ifile.tellg() ) ); - ifile.seekg( 0, std::ios::beg ); + // get the size of the data type to be read in the file header + int uintSize; + MPI_Type_size(MPITrait< uint_t >::type(), &uintSize); - buffer.resize( length / sizeof( mpi::RecvBuffer::ElementType ) ); + // header of the file contains each process' offset and buffer size + uint_t offsetData[2]; - ifile.read( reinterpret_cast< char* >( buffer.ptr() ), numeric_cast< std::streamsize >( length ) ); - ifile.close(); + // use serial I/O for versions of OpenMPI that produce segmentation faults when using MPI-IO with a 3D + // Cartesian MPI communicator (see waLBerla issue #73) + if (!MPIManager::instance()->isCommMPIIOValid()) + { + std::ifstream ifile; + + // read each process' offset and buffer size from file + for (int i = 0; i != MPIManager::instance()->numProcesses(); ++i) + { + if (i == MPIManager::instance()->rank()) + { + ifile.open(file.c_str(), std::ofstream::binary); + if (ifile.fail()) { WALBERLA_ABORT("Error while opening file \"" << file << "\" for reading."); } + + ifile.seekg(numeric_cast< std::streamoff >(mpi::MPIManager::instance()->rank() * 2 * uintSize)); + ifile.read(reinterpret_cast< char* >(offsetData), numeric_cast< std::streamsize >(2 * uintSize)); + if (ifile.fail()) { WALBERLA_ABORT("Error while reading from file \"" << file << "\"."); } + + ifile.close(); + if (ifile.fail()) { WALBERLA_ABORT("Error while closing file \"" << file << "\" for reading."); } + } + WALBERLA_MPI_BARRIER(); + } + + buffer.resize(offsetData[1] / sizeof(mpi::RecvBuffer::ElementType)); + + // read each process' data from file (starting at offset) + for (int i = 0; i != MPIManager::instance()->numProcesses(); ++i) + { + if (i == MPIManager::instance()->rank()) + { + ifile.open(file.c_str(), std::ofstream::binary); + if (ifile.fail()) { WALBERLA_ABORT("Error while opening file \"" << file << "\" for reading."); } + + ifile.seekg(numeric_cast< std::streamoff >(offsetData[0])); + ifile.read(reinterpret_cast< char* >(buffer.ptr()), numeric_cast< std::streamsize >(offsetData[1])); + if (ifile.fail()) { WALBERLA_ABORT("Error while reading from file \"" << file << "\"."); } + + ifile.close(); + if (ifile.fail()) { WALBERLA_ABORT("Error while closing file \"" << file << "\" for reading."); } + } + WALBERLA_MPI_BARRIER(); + } } - - WALBERLA_MPI_SECTION() + else // use MPI-IO { MPI_File mpiFile = MPI_FILE_NULL; - int result = MPI_SUCCESS; - result = MPI_File_open( MPIManager::instance()->comm(), const_cast<char*>( file.c_str() ), MPI_MODE_RDONLY, MPI_INFO_NULL, &mpiFile ); + int result = MPI_SUCCESS; + result = MPI_File_open(MPIManager::instance()->comm(), const_cast< char* >(file.c_str()), MPI_MODE_RDONLY, + MPI_INFO_NULL, &mpiFile); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while opening file \"" << file << "\" for writing. MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); - - int uintSize; - MPI_Type_size( MPITrait< uint_t >::type(), &uintSize ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while opening file \"" << file << "\" for reading. MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); MPI_Datatype offsettype; - MPI_Type_contiguous( 2, MPITrait< uint_t >::type(), &offsettype ); - MPI_Type_commit( &offsettype ); - - // offsets to processes data (+ buffer size) - - result = MPI_File_set_view( mpiFile, numeric_cast<MPI_Offset>( mpi::MPIManager::instance()->rank() * 2 * uintSize ), - MPITrait< uint_t >::type(), offsettype, const_cast<char*>( "native" ), MPI_INFO_NULL ); - - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + MPI_Type_contiguous(2, MPITrait< uint_t >::type(), &offsettype); + MPI_Type_commit(&offsettype); - uint_t offsetData[2]; + // read each process' offset and buffer size from file + result = + MPI_File_set_view(mpiFile, numeric_cast< MPI_Offset >(mpi::MPIManager::instance()->rank() * 2 * uintSize), + MPITrait< uint_t >::type(), offsettype, const_cast< char* >("native"), MPI_INFO_NULL); - result = MPI_File_read_all( mpiFile, reinterpret_cast<char*>( offsetData ), 2, MPITrait< uint_t >::type(), MPI_STATUS_IGNORE ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString(result) + << "\""); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while writing to file \"" << file << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + result = MPI_File_read_all(mpiFile, reinterpret_cast< char* >(offsetData), 2, MPITrait< uint_t >::type(), + MPI_STATUS_IGNORE); - // the data + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while reading from file \"" << file << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) + << "\""); + // read each process' data from file (starting at offset) MPI_Datatype arraytype; - MPI_Type_contiguous( int_c( offsetData[1] ), MPITrait< mpi::RecvBuffer::ElementType >::type(), &arraytype ); - MPI_Type_commit( &arraytype ); + MPI_Type_contiguous(int_c(offsetData[1]), MPITrait< mpi::RecvBuffer::ElementType >::type(), &arraytype); + MPI_Type_commit(&arraytype); - result = MPI_File_set_view( mpiFile, numeric_cast<MPI_Offset>( offsetData[0] ), MPITrait< mpi::RecvBuffer::ElementType >::type(), arraytype, - const_cast<char*>( "native" ), MPI_INFO_NULL ); + result = MPI_File_set_view(mpiFile, numeric_cast< MPI_Offset >(offsetData[0]), + MPITrait< mpi::RecvBuffer::ElementType >::type(), arraytype, + const_cast< char* >("native"), MPI_INFO_NULL); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString(result) + << "\""); - buffer.resize( offsetData[1] ); + buffer.resize(offsetData[1]); - result = MPI_File_read_all( mpiFile, reinterpret_cast<char*>( buffer.ptr() ), int_c( buffer.size() ), MPITrait< mpi::SendBuffer::ElementType >::type(), MPI_STATUS_IGNORE ); + result = MPI_File_read_all(mpiFile, reinterpret_cast< char* >(buffer.ptr()), int_c(buffer.size()), + MPITrait< mpi::SendBuffer::ElementType >::type(), MPI_STATUS_IGNORE); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while writing to file \"" << file << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while reading from file \"" << file << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) + << "\""); - result = MPI_File_close( &mpiFile ); + result = MPI_File_close(&mpiFile); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while closing file \"" << file << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while closing file \"" << file << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); - MPI_Type_free( &arraytype ); - MPI_Type_free( &offsettype ); + MPI_Type_free(&arraytype); + MPI_Type_free(&offsettype); } } - +} } // namespace mpi } // namespace walberla - - diff --git a/src/core/mpi/MPIManager.cpp b/src/core/mpi/MPIManager.cpp index f8944ab781e8c7c7a587e1a7a484eca30cb1950f..2a2ac24824c86659a405508cb098e6419f44b772 100644 --- a/src/core/mpi/MPIManager.cpp +++ b/src/core/mpi/MPIManager.cpp @@ -1,15 +1,15 @@ //====================================================================================================================== // -// This file is part of waLBerla. waLBerla is free software: you can +// 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 +// 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 +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. -// +// // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // @@ -18,23 +18,25 @@ //! \author Martin Bauer <martin.bauer@fau.de> //! \author Florian Schornbaum <florian.schornbaum@fau.de> //! \author Christian Godenschwager <christian.godenschwager@fau.de> +//! \author Christoph Schwarzmeier <christoph.schwarzmeier@fau.de> // //====================================================================================================================== #include "MPIManager.h" + #include "core/logging/Logging.h" #include <exception> #include <iostream> +#include <mutex> #include <stdexcept> -#include <vector> #include <string> +#include <vector> - -namespace walberla{ -namespace mpi { - - +namespace walberla +{ +namespace mpi +{ /** * Terminate Handler that calls MPI_Abort instead of std::abort * @@ -55,220 +57,209 @@ static void customTerminateHandler() // in linux environments. If this pointer is null, // i.e. in cases when this hack does not work, we just cannot print the message // otherwise we re-throw the exception to get the type, and print exception.what() - try { - if(std::current_exception() ) - std::rethrow_exception(std::current_exception()); - } catch (std::exception const & exc) { + try + { + if (std::current_exception()) std::rethrow_exception(std::current_exception()); + } catch (std::exception const& exc) + { std::cerr << exc.what() << std::endl; } - WALBERLA_MPI_SECTION() { - MPIManager::instance()->abort(); - } - else { - std::abort(); - } + WALBERLA_MPI_SECTION() { MPIManager::instance()->abort(); } + else { std::abort(); } } - - -MPIManager::~MPIManager() -{ - finalizeMPI(); -} +MPIManager::~MPIManager() { finalizeMPI(); } void MPIManager::abort() { currentlyAborting_ = true; WALBERLA_MPI_SECTION() { - if( MPIManager::instance()->isMPIInitialized() ) - MPI_Abort( MPI_COMM_WORLD, EXIT_FAILURE ); + if (MPIManager::instance()->isMPIInitialized()) MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE); } - std::exit( EXIT_FAILURE ); + std::exit(EXIT_FAILURE); } - -void MPIManager::initializeMPI( int* argc, char*** argv, bool abortOnException ) +void MPIManager::initializeMPI(int* argc, char*** argv, bool abortOnException) { WALBERLA_MPI_SECTION() { - WALBERLA_ASSERT( !isMPIInitialized_ ); + WALBERLA_ASSERT(!isMPIInitialized_); // Check first that MPI was not initialized before // f.e. when using Python, MPI could have been initialized by // a different MPI module like mpi4py - int mpiAlreadyInitialized=0; - MPI_Initialized( &mpiAlreadyInitialized ); - if ( ! mpiAlreadyInitialized ) { - MPI_Init( argc, argv ); + int mpiAlreadyInitialized = 0; + MPI_Initialized(&mpiAlreadyInitialized); + if (!mpiAlreadyInitialized) + { + MPI_Init(argc, argv); finalizeOnDestruction_ = true; } isMPIInitialized_ = true; - MPI_Comm_size( MPI_COMM_WORLD, &numProcesses_ ); - MPI_Comm_rank( MPI_COMM_WORLD, &worldRank_ ); + MPI_Comm_size(MPI_COMM_WORLD, &numProcesses_); + MPI_Comm_rank(MPI_COMM_WORLD, &worldRank_); - if( abortOnException ) - std::set_terminate( customTerminateHandler ); + if (abortOnException) std::set_terminate(customTerminateHandler); } } - - void MPIManager::finalizeMPI() { - WALBERLA_MPI_SECTION() { - if( isMPIInitialized_ && !currentlyAborting_ ) + WALBERLA_MPI_SECTION() + { + if (isMPIInitialized_ && !currentlyAborting_) { isMPIInitialized_ = false; - if (finalizeOnDestruction_) - { - MPI_Finalize(); - } + if (finalizeOnDestruction_) { MPI_Finalize(); } } } } - - void MPIManager::resetMPI() { - WALBERLA_MPI_SECTION() { - WALBERLA_ASSERT( isMPIInitialized_ ); - if( rank_ != -1 ) + WALBERLA_MPI_SECTION() + { + WALBERLA_ASSERT(isMPIInitialized_); + if (rank_ != -1) { - if( comm_ == MPI_COMM_WORLD ) + if (comm_ == MPI_COMM_WORLD) comm_ = MPI_COMM_NULL; else - MPI_Comm_free( &comm_ ); + MPI_Comm_free(&comm_); rank_ = -1; } cartesianSetup_ = false; - WALBERLA_ASSERT_EQUAL( comm_, MPI_COMM_NULL ); - WALBERLA_ASSERT_EQUAL( rank_, -1 ); + WALBERLA_ASSERT_EQUAL(comm_, MPI_COMM_NULL); + WALBERLA_ASSERT_EQUAL(rank_, -1); } } - - -void MPIManager::createCartesianComm( int dims[3], int periodicity[3] ) +void MPIManager::createCartesianComm(int dims[3], int periodicity[3]) { - WALBERLA_ASSERT( isMPIInitialized_ ); - WALBERLA_ASSERT_EQUAL( rank_, -1 ); - WALBERLA_ASSERT( !cartesianSetup_ ); - WALBERLA_ASSERT_GREATER( dims[0], 0 ); - WALBERLA_ASSERT_GREATER( dims[1], 0 ); - WALBERLA_ASSERT_GREATER( dims[2], 0 ); - - if ( ! isCartesianCommValid() ) { - WALBERLA_LOG_WARNING_ON_ROOT( "Your version of OpenMPI contains a bug which might lead to a segmentation fault " - "when generating vtk output. Since the bug only occurs with a 3D Cartesian MPI " - "communicator, try to use MPI_COMM_WORLD instead. See waLBerla issue #73 for " - "additional information." ); - } - - MPI_Cart_create( MPI_COMM_WORLD, 3, dims, periodicity, true, &comm_ ); - MPI_Comm_rank( comm_, &rank_ ); + WALBERLA_ASSERT(isMPIInitialized_); + WALBERLA_ASSERT_EQUAL(rank_, -1); + WALBERLA_ASSERT(!cartesianSetup_); + WALBERLA_ASSERT_GREATER(dims[0], 0); + WALBERLA_ASSERT_GREATER(dims[1], 0); + WALBERLA_ASSERT_GREATER(dims[2], 0); + + MPI_Cart_create(MPI_COMM_WORLD, 3, dims, periodicity, true, &comm_); + MPI_Comm_rank(comm_, &rank_); cartesianSetup_ = true; WALBERLA_ASSERT_UNEQUAL(comm_, MPI_COMM_NULL); } - - -void MPIManager::createCartesianComm( const uint_t xProcesses, const uint_t yProcesses, const uint_t zProcesses, - const bool xPeriodic, const bool yPeriodic, const bool zPeriodic ) +void MPIManager::createCartesianComm(const uint_t xProcesses, const uint_t yProcesses, const uint_t zProcesses, + const bool xPeriodic, const bool yPeriodic, const bool zPeriodic) { int dims[3]; - dims[0] = numeric_cast<int>( xProcesses ); - dims[1] = numeric_cast<int>( yProcesses ); - dims[2] = numeric_cast<int>( zProcesses ); + dims[0] = numeric_cast< int >(xProcesses); + dims[1] = numeric_cast< int >(yProcesses); + dims[2] = numeric_cast< int >(zProcesses); int periodicity[3]; periodicity[0] = xPeriodic ? 1 : 0; periodicity[1] = yPeriodic ? 1 : 0; periodicity[2] = zPeriodic ? 1 : 0; - createCartesianComm( dims, periodicity ); + createCartesianComm(dims, periodicity); } +void MPIManager::cartesianCoord(int coordOut[3]) const { cartesianCoord(rank_, coordOut); } - -void MPIManager::cartesianCoord( int coordOut[3] ) const +void MPIManager::cartesianCoord(int rankIn, int coordOut[3]) const { - cartesianCoord( rank_, coordOut ); -} - - - -void MPIManager::cartesianCoord( int rankIn, int coordOut[3] ) const -{ - WALBERLA_ASSERT( isMPIInitialized_ ); - WALBERLA_ASSERT( cartesianSetup_ ); - WALBERLA_ASSERT_UNEQUAL( comm_, MPI_COMM_NULL ); + WALBERLA_ASSERT(isMPIInitialized_); + WALBERLA_ASSERT(cartesianSetup_); + WALBERLA_ASSERT_UNEQUAL(comm_, MPI_COMM_NULL); - MPI_Cart_coords( comm_, rankIn, 3, coordOut ); + MPI_Cart_coords(comm_, rankIn, 3, coordOut); } - - -int MPIManager::cartesianRank( int coords[3] ) const +int MPIManager::cartesianRank(int coords[3]) const { - WALBERLA_ASSERT( isMPIInitialized_ ); - WALBERLA_ASSERT( cartesianSetup_ ); - WALBERLA_ASSERT_UNEQUAL( comm_, MPI_COMM_NULL ); + WALBERLA_ASSERT(isMPIInitialized_); + WALBERLA_ASSERT(cartesianSetup_); + WALBERLA_ASSERT_UNEQUAL(comm_, MPI_COMM_NULL); int r; - MPI_Cart_rank( comm_, coords, &r ); + MPI_Cart_rank(comm_, coords, &r); return r; } - - -int MPIManager::cartesianRank( const uint_t x, const uint_t y, const uint_t z ) const +int MPIManager::cartesianRank(const uint_t x, const uint_t y, const uint_t z) const { int coords[3]; - coords[0] = numeric_cast<int>(x); - coords[1] = numeric_cast<int>(y); - coords[2] = numeric_cast<int>(z); + coords[0] = numeric_cast< int >(x); + coords[1] = numeric_cast< int >(y); + coords[2] = numeric_cast< int >(z); - return cartesianRank( coords ); + return cartesianRank(coords); } -bool MPIManager::isCartesianCommValid() const +bool MPIManager::isCommMPIIOValid() const { - // Avoid using the Cartesian MPI-communicator with certain (buggy) versions of OpenMPI (see waLBerla issue #73) - #if defined(OMPI_MAJOR_VERSION) && defined(OMPI_MINOR_VERSION) && defined(OMPI_RELEASE_VERSION) - std::string ompi_ver = std::to_string(OMPI_MAJOR_VERSION) + "." + std::to_string(OMPI_MINOR_VERSION) + "." + - std::to_string(OMPI_RELEASE_VERSION); - - if (ompi_ver == "2.0.0" || ompi_ver == "2.0.1" || ompi_ver == "2.0.2" || ompi_ver == "2.0.3" || - ompi_ver == "2.1.0" || ompi_ver == "2.1.1") { - return false; - } - else { - return true; - } - #else - return true; - #endif + // certain versions of OpenMPI produce segmentation faults when using MPI-IO with a 3D Cartesian MPI communicator + // (see waLBerla issue #73) + + if (!hasCartesianSetup()) { return true; } + +#if defined(OMPI_MAJOR_VERSION) && defined(OMPI_MINOR_VERSION) && defined(OMPI_RELEASE_VERSION) + + static std::once_flag printWarningOnce; + + std::string ompi_ver = std::to_string(OMPI_MAJOR_VERSION) + "." + std::to_string(OMPI_MINOR_VERSION) + "." + + std::to_string(OMPI_RELEASE_VERSION); + + if (ompi_ver == "2.0.0" || ompi_ver == "2.0.1" || ompi_ver == "2.0.2" || ompi_ver == "2.0.3" || + ompi_ver == "2.1.0" || ompi_ver == "2.1.1") + { + std::call_once(printWarningOnce, [](){ + WALBERLA_LOG_WARNING_ON_ROOT( + "Your version of OpenMPI is known to produce segmentation faults when using MPI-IO with a 3D Cartesian MPI " + "communicator (see waLBerla issue #73). Please try to use a different version of OpenMPI. As a workaround, " + "serial I/O is used now. This might lead to a decrease in performance."); + }); + + return false; + } + + if (ompi_ver == "4.0.0") + { + std::call_once(printWarningOnce, [](){ + WALBERLA_LOG_WARNING_ON_ROOT( + "Several users of waLBerla with your version of OpenMPI have experienced issues with corrupt VTK output " + "files. While the VTK files are fine at first, the MPI-IO seems to sporadically produce erroneous output from " + "a certain point on. This has primarily been noticed when writing large files from more than a few hundreds " + "of processes. We are unaware whether this is a general problem with OpenMPI 4.0.0 or if this problem only " + "occurs on the specific machine that these users have used. If you experience similar problems, please do not " + "hesitate to inform us."); + }); + } +#endif + + return true; } std::string MPIManager::getMPIErrorString(int errorCode) { WALBERLA_NON_MPI_SECTION() { - throw std::logic_error("Trying to use function 'MPIManager::getMPIErrorString' but waLBerla is compiled without MPI-support!"); + throw std::logic_error( + "Trying to use function 'MPIManager::getMPIErrorString' but waLBerla is compiled without MPI-support!"); } WALBERLA_ASSERT_GREATER(MPI_MAX_ERROR_STRING, 0); - std::vector<char> errorString(MPI_MAX_ERROR_STRING); + std::vector< char > errorString(MPI_MAX_ERROR_STRING); int resultLen; MPI_Error_string(errorCode, &errorString[0], &resultLen); WALBERLA_ASSERT_GREATER_EQUAL(resultLen, 0); - WALBERLA_ASSERT_LESS_EQUAL(resultLen, numeric_cast<int>(errorString.size())); + WALBERLA_ASSERT_LESS_EQUAL(resultLen, numeric_cast< int >(errorString.size())); return std::string(errorString.begin(), errorString.begin() + resultLen); } @@ -276,16 +267,17 @@ std::string MPIManager::getMPICommName(MPI_Comm comm) { WALBERLA_NON_MPI_SECTION() { - throw std::logic_error("Trying to use function 'MPIManager::getMPICommName' but waLBerla is compiled without MPI-support!"); + throw std::logic_error( + "Trying to use function 'MPIManager::getMPICommName' but waLBerla is compiled without MPI-support!"); } WALBERLA_ASSERT_GREATER(MPI_MAX_OBJECT_NAME, 0); - std::vector<char> commName(MPI_MAX_OBJECT_NAME); + std::vector< char > commName(MPI_MAX_OBJECT_NAME); int resultLen; MPI_Comm_get_name(comm, &commName[0], &resultLen); WALBERLA_ASSERT_GREATER_EQUAL(resultLen, 0); - WALBERLA_ASSERT_LESS_EQUAL(resultLen, numeric_cast<int>(commName.size())); + WALBERLA_ASSERT_LESS_EQUAL(resultLen, numeric_cast< int >(commName.size())); return std::string(commName.begin(), commName.begin() + resultLen); } diff --git a/src/core/mpi/MPIManager.h b/src/core/mpi/MPIManager.h index 620d69a70898520db946a8f3110e67c5b541a76a..cef90cd62eeffa5e898f007a88fbf829280992cb 100644 --- a/src/core/mpi/MPIManager.h +++ b/src/core/mpi/MPIManager.h @@ -1,15 +1,15 @@ //====================================================================================================================== // -// This file is part of waLBerla. waLBerla is free software: you can +// 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 +// 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 +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. -// +// // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // @@ -123,8 +123,9 @@ public: /// Rank is valid after calling createCartesianComm() or useWorldComm() bool rankValid() const { return rank_ >= 0; } - /// Using a Cartesian MPI communicator is not valid for certain versions of OpenMPI (see waLBerla issue #73) - bool isCartesianCommValid() const; + /// Indicates whether MPI-IO can be used with the current MPI communicator; certain versions of OpenMPI produce + /// segmentation faults when using MPI-IO with a 3D Cartesian MPI communicator (see waLBerla issue #73) + bool isCommMPIIOValid() const; //@} //******************************************************************************************************************* diff --git a/src/core/mpi/MPITextFile.cpp b/src/core/mpi/MPITextFile.cpp index ac9a40cea0bba20bc75c6a55d3abbe7e4c8df7f9..2d78b1acd8e812f02f76e5341105d04f6fcda483 100644 --- a/src/core/mpi/MPITextFile.cpp +++ b/src/core/mpi/MPITextFile.cpp @@ -1,34 +1,36 @@ //====================================================================================================================== // -// This file is part of waLBerla. waLBerla is free software: you can +// 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 +// 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 +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. -// +// // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // //! \file MPITextFile.cpp //! \ingroup core //! \author Christian Godenschwager <christian.godenschwager@fau.de> +//! \author Christoph Schwarzmeier <christoph.schwarzmeier@fau.de> // //====================================================================================================================== #include "MPITextFile.h" +#include "core/logging/Logging.h" #include "core/mpi/Reduce.h" #include <fstream> -namespace walberla { -namespace mpi { - - +namespace walberla +{ +namespace mpi +{ //====================================================================================================================== /*! * \brief Writes file using MPI IO with each process providing a part of it @@ -40,12 +42,12 @@ namespace mpi { * \param processLocalPart The part of the file belonging to the calling process (size may differ among processes) */ //====================================================================================================================== -void writeMPITextFile( const std::string & filename, const std::string & processLocalPart, - const MPI_Comm comm /*= MPI_COMM_WORLD*/ ) +void writeMPITextFile(const std::string& filename, const std::string& processLocalPart, + const MPI_Comm comm /*= MPI_COMM_WORLD*/) { WALBERLA_NON_MPI_SECTION() { - std::ofstream ofs( filename.c_str() ); + std::ofstream ofs(filename.c_str()); ofs << processLocalPart; ofs.close(); } @@ -54,53 +56,82 @@ void writeMPITextFile( const std::string & filename, const std::string & process { int rank; int numProcesses; - MPI_Comm_rank( comm, &rank ); - MPI_Comm_size( comm, &numProcesses ); - - if( processLocalPart.size() > numeric_cast<std::string::size_type>( std::numeric_limits<int>::max() ) ) - WALBERLA_ABORT( "writeMPITextFile does not support more than " << std::numeric_limits<int>::max() << " characters per process!" ); - - MPI_File mpiFile; - int result = MPI_SUCCESS; - result = MPI_File_open( comm, const_cast<char*>( filename.c_str() ), MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, - &mpiFile ); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while opening file \"" << filename << "\" for writing. MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); - - const MPI_Offset filesize = numeric_cast<MPI_Offset>( allReduce( processLocalPart.size(), mpi::SUM, comm ) ); - - size_t exscanResult; - size_t input = processLocalPart.size(); - MPI_Exscan( &input, &exscanResult, 1, MPITrait<size_t>::type(), MPI_SUM, comm ); - if( rank == 0 ) - exscanResult = size_t( 0 ); - - const MPI_Offset offset = numeric_cast<MPI_Offset>( exscanResult ); - - MPI_File_set_size( mpiFile, filesize ); - - MPI_Datatype arraytype; - MPI_Type_contiguous( int_c( processLocalPart.size() ), MPITrait<char>::type(), &arraytype); - MPI_Type_commit( &arraytype ); - - result = MPI_File_set_view( mpiFile, offset, MPITrait<char>::type(), arraytype, const_cast<char*>( "native" ), - MPI_INFO_NULL ); - - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); - - result = MPI_File_write_all( mpiFile, const_cast<char*>( processLocalPart.c_str() ), int_c( processLocalPart.size() ), - MPITrait<char>::type(), MPI_STATUS_IGNORE ); - - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while writing to file \"" << filename << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); - - result = MPI_File_close( &mpiFile ); - - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while closing file \"" << filename << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); - - MPI_Type_free( &arraytype ); + MPI_Comm_rank(comm, &rank); + MPI_Comm_size(comm, &numProcesses); + + // use serial I/O for versions of OpenMPI that produce segmentation faults when using MPI-IO with a 3D Cartesian + // MPI communicator (see waLBerla issue #73) + if (!MPIManager::instance()->isCommMPIIOValid()) + { + std::ofstream ofs; + + for (int i = 0; i != numProcesses; ++i) + { + if (i == rank) + { + if (rank == 0) { ofs.open(filename.c_str()); } + else + { + ofs.open(filename.c_str(), std::ofstream::app); + } + ofs << processLocalPart; + ofs.close(); + } + WALBERLA_MPI_BARRIER(); + } + } + else + { + if (processLocalPart.size() > numeric_cast< std::string::size_type >(std::numeric_limits< int >::max())) + WALBERLA_ABORT("writeMPITextFile does not support more than " << std::numeric_limits< int >::max() + << " characters per process!"); + + MPI_File mpiFile; + int result = MPI_SUCCESS; + result = MPI_File_open(comm, const_cast< char* >(filename.c_str()), MPI_MODE_WRONLY | MPI_MODE_CREATE, + MPI_INFO_NULL, &mpiFile); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while opening file \"" << filename << "\" for writing. MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); + + const MPI_Offset filesize = numeric_cast< MPI_Offset >(allReduce(processLocalPart.size(), mpi::SUM, comm)); + + size_t exscanResult; + size_t input = processLocalPart.size(); + MPI_Exscan(&input, &exscanResult, 1, MPITrait< size_t >::type(), MPI_SUM, comm); + if (rank == 0) exscanResult = size_t(0); + + const MPI_Offset offset = numeric_cast< MPI_Offset >(exscanResult); + + MPI_File_set_size(mpiFile, filesize); + + MPI_Datatype arraytype; + MPI_Type_contiguous(int_c(processLocalPart.size()), MPITrait< char >::type(), &arraytype); + MPI_Type_commit(&arraytype); + + result = MPI_File_set_view(mpiFile, offset, MPITrait< char >::type(), arraytype, const_cast< char* >("native"), + MPI_INFO_NULL); + + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString(result) + << "\""); + + result = MPI_File_write_all(mpiFile, const_cast< char* >(processLocalPart.c_str()), + int_c(processLocalPart.size()), MPITrait< char >::type(), MPI_STATUS_IGNORE); + + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while writing to file \"" << filename << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) + << "\""); + + result = MPI_File_close(&mpiFile); + + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while closing file \"" << filename << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); + + MPI_Type_free(&arraytype); + } } } diff --git a/src/field/FileIO.impl.h b/src/field/FileIO.impl.h index 910ff2f880fdc6642dada18f77aafb90ea2e049c..eae64c6b39b1281c9f2d0c01b5c27e2770d18ffe 100644 --- a/src/field/FileIO.impl.h +++ b/src/field/FileIO.impl.h @@ -16,325 +16,423 @@ //! \file FileIO.impl.h //! \ingroup field //! \author Christian Godenschwager <christian.godenschwager@fau.de> +//! \author Christoph Schwarzmeier <christoph.schwarzmeier@fau.de> // //====================================================================================================================== #pragma once -namespace walberla { -namespace field { - -namespace internal { - - +namespace walberla +{ +namespace field +{ +namespace internal +{ template< typename FieldT > class FieldWriter { -public: - FieldWriter( const std::string & filename, const BlockDataID & fieldID, - const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(), const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() ) - : filename_( filename ), fieldID_( fieldID ), requiredSelectors_( requiredSelectors ), incompatibleSelectors_( incompatibleSelectors ) - { - } + public: + FieldWriter(const std::string& filename, const BlockDataID& fieldID, + const Set< SUID >& requiredSelectors = Set< SUID >::emptySet(), + const Set< SUID >& incompatibleSelectors = Set< SUID >::emptySet()) + : filename_(filename), fieldID_(fieldID), requiredSelectors_(requiredSelectors), + incompatibleSelectors_(incompatibleSelectors) + {} - void writeToFile( const BlockStorage & blockStorage ) const; - void readFromFile( BlockStorage & blockStorage ) const; -private: + void writeToFile(const BlockStorage& blockStorage) const; + void readFromFile(BlockStorage& blockStorage) const; - std::vector< IBlock * > getBlocks( BlockStorage & blockStorage ) const; - std::vector< const IBlock * > getBlocks( const BlockStorage & blockStorage ) const; + private: + std::vector< IBlock* > getBlocks(BlockStorage& blockStorage) const; + std::vector< const IBlock* > getBlocks(const BlockStorage& blockStorage) const; - void writeToFileNonMPI( const std::vector< const IBlock * > & blocks ) const; - void readFromFileNonMPI( const std::vector< IBlock * > & blocks ) const; + void writeToFileNonMPI(const std::vector< const IBlock* >& blocks) const; + void readFromFileNonMPI(const std::vector< IBlock* >& blocks) const; - std::vector< uint_t > computeBlockOffsets( const std::vector< const IBlock * > & blocks ) const; - std::vector< uint_t > computeBlockOffsets( const std::vector< IBlock * > & blocks ) const; - - MPI_Offset computeProcessByteOffset( uint_t processNumElements ) const; + std::vector< uint_t > computeBlockOffsets(const std::vector< const IBlock* >& blocks) const; + std::vector< uint_t > computeBlockOffsets(const std::vector< IBlock* >& blocks) const; + MPI_Offset computeProcessByteOffset(uint_t processNumElements) const; std::string filename_; BlockDataID fieldID_; - - Set<SUID> requiredSelectors_; - Set<SUID> incompatibleSelectors_; -}; - + Set< SUID > requiredSelectors_; + Set< SUID > incompatibleSelectors_; +}; template< typename FieldT > -void FieldWriter<FieldT>::writeToFile( const BlockStorage & blockStorage ) const +void FieldWriter< FieldT >::writeToFile(const BlockStorage& blockStorage) const { - std::vector< const IBlock * > blocks = getBlocks( blockStorage ); - + std::vector< const IBlock* > blocks = getBlocks(blockStorage); + WALBERLA_NON_MPI_SECTION() { - writeToFileNonMPI( blocks ); + writeToFileNonMPI(blocks); return; } - std::vector< uint_t > blockOffsets = computeBlockOffsets( blocks ); + std::vector< uint_t > blockOffsets = computeBlockOffsets(blocks); + const MPI_Offset offset = computeProcessByteOffset(blockOffsets.back()); - if( blockOffsets.back() > uint_c( std::numeric_limits<int>::max() ) ) - WALBERLA_ABORT( "writeToFile does not support writing more than " << std::numeric_limits<int>::max() << " field elements per process!" ); + if (blockOffsets.back() > uint_c(std::numeric_limits< int >::max())) + WALBERLA_ABORT("writeToFile does not support writing more than " << std::numeric_limits< int >::max() + << " field elements per process!"); - const MPI_Offset filesize = numeric_cast<MPI_Offset>( mpi::allReduce( blockOffsets.back(), mpi::SUM, MPIManager::instance()->comm() ) * sizeof( typename FieldT::value_type ) ); + // use serial I/O for versions of OpenMPI that produce segmentation faults when using MPI-IO with a 3D Cartesian MPI + // communicator (see waLBerla issue #73) + if (!MPIManager::instance()->isCommMPIIOValid()) + { + std::ofstream ofs; - MPI_Datatype arraytype; - MPI_Type_contiguous( int_c( blockOffsets.back() ), MPITrait< typename FieldT::value_type >::type(), &arraytype ); - MPI_Type_commit( &arraytype ); + // clear an existing file's content + WALBERLA_ROOT_SECTION() + { + ofs.open(filename_.c_str(), std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); + if (ofs.fail()) { WALBERLA_ABORT("Error while opening file \"" << filename_ << "\" for writing."); } - const MPI_Offset offset = computeProcessByteOffset( blockOffsets.back() ); + ofs.close(); + if (ofs.fail()) { WALBERLA_ABORT("Error while closing file \"" << filename_ << "\" for writing."); } + } - MPI_File mpiFile = MPI_FILE_NULL; - int result = MPI_SUCCESS; - result = MPI_File_open( MPIManager::instance()->comm(), const_cast<char*>( filename_.c_str() ), MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, &mpiFile ); + // write every block's field to the file + for (int i = 0; i != MPIManager::instance()->numProcesses(); ++i) + { + if (i == MPIManager::instance()->rank()) + { + ofs.open(filename_.c_str(), std::ofstream::out | std::ofstream::binary | std::ofstream::app); + if (ofs.fail()) { WALBERLA_ABORT("Error while opening file \"" << filename_ << "\" for writing."); } + + size_t blockIdx = 0; + for (auto block = blocks.begin(); block != blocks.end(); ++block, ++blockIdx) + { + const FieldT* field = (*block)->template getData< FieldT >(fieldID_); + + std::vector< typename FieldT::value_type > data(field->xSize() * field->ySize() * field->zSize() * + field->fSize()); + + auto dataIt = data.begin(); + for (auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt, ++dataIt) + { + WALBERLA_ASSERT_UNEQUAL(dataIt, data.end()); + *dataIt = *fieldIt; + } + WALBERLA_ASSERT_EQUAL(dataIt, data.end()); + + ofs.seekp(numeric_cast< std::streamoff >(numeric_cast< uint_t >(offset) + + blockOffsets[blockIdx] * sizeof(typename FieldT::value_type))); + ofs.write(reinterpret_cast< const char* >(&data[0]), + numeric_cast< std::streamsize >(data.size() * sizeof(typename FieldT::value_type))); + if (ofs.fail()) { WALBERLA_ABORT("Error while writing to file \"" << filename_ << "\"."); } + } + ofs.close(); + if (ofs.fail()) { WALBERLA_ABORT("Error while closing file \"" << filename_ << "\" for writing."); } + } + WALBERLA_MPI_BARRIER(); + } + } + else // use MPI-IO + { + const MPI_Offset filesize = + numeric_cast< MPI_Offset >(mpi::allReduce(blockOffsets.back(), mpi::SUM, MPIManager::instance()->comm()) * + sizeof(typename FieldT::value_type)); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while opening file \"" << filename_ << "\" for writing. MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + MPI_Datatype arraytype; + MPI_Type_contiguous(int_c(blockOffsets.back()), MPITrait< typename FieldT::value_type >::type(), &arraytype); + MPI_Type_commit(&arraytype); - MPI_File_set_size( mpiFile, filesize ); + MPI_File mpiFile = MPI_FILE_NULL; + int result = MPI_SUCCESS; + result = MPI_File_open(MPIManager::instance()->comm(), const_cast< char* >(filename_.c_str()), + MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, &mpiFile); - result = MPI_File_set_view( mpiFile, offset, MPITrait< typename FieldT::value_type >::type(), arraytype, const_cast<char*>( "native" ), MPI_INFO_NULL ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while opening file \"" << filename_ << "\" for writing. MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + MPI_File_set_size(mpiFile, filesize); - size_t blockIdx = 0; - for( auto block = blocks.begin(); block != blocks.end(); ++block, ++blockIdx ) - { - const FieldT * field = (*block)->template getData<FieldT>( fieldID_ ); + result = MPI_File_set_view(mpiFile, offset, MPITrait< typename FieldT::value_type >::type(), arraytype, + const_cast< char* >("native"), MPI_INFO_NULL); - std::vector< typename FieldT::value_type > data( field->xSize() * field->ySize() * field->zSize() * field->fSize() ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString(result) + << "\""); - auto dataIt = data.begin(); - for( auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt, ++dataIt ) + size_t blockIdx = 0; + for (auto block = blocks.begin(); block != blocks.end(); ++block, ++blockIdx) { - WALBERLA_ASSERT( dataIt != data.end() ); - *dataIt = *fieldIt; + const FieldT* field = (*block)->template getData< FieldT >(fieldID_); + + std::vector< typename FieldT::value_type > data(field->xSize() * field->ySize() * field->zSize() * + field->fSize()); + + auto dataIt = data.begin(); + for (auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt, ++dataIt) + { + WALBERLA_ASSERT_UNEQUAL(dataIt, data.end()); + *dataIt = *fieldIt; + } + WALBERLA_ASSERT_EQUAL(dataIt, data.end()); + + result = + MPI_File_write_at(mpiFile, numeric_cast< MPI_Offset >(blockOffsets[blockIdx]), &data[0], int_c(data.size()), + MPITrait< typename FieldT::value_type >::type(), MPI_STATUS_IGNORE); + + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while writing to file \"" << filename_ << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) + << "\""); } - WALBERLA_ASSERT_EQUAL( dataIt, data.end() ); - - result = MPI_File_write_at( mpiFile, numeric_cast<MPI_Offset>( blockOffsets[blockIdx] ), &data[0], int_c( data.size() ), - MPITrait<typename FieldT::value_type>::type(), MPI_STATUS_IGNORE ); - - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while writing to file \"" << filename_ << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); - } - result = MPI_File_close( &mpiFile ); + result = MPI_File_close(&mpiFile); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while closing file \"" << filename_ << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while closing file \"" << filename_ << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); - MPI_Type_free( &arraytype ); + MPI_Type_free(&arraytype); + } } - - template< typename FieldT > -void FieldWriter<FieldT>::readFromFile( BlockStorage & blockStorage ) const +void FieldWriter< FieldT >::readFromFile(BlockStorage& blockStorage) const { - std::vector< IBlock * > blocks = getBlocks( blockStorage ); - + std::vector< IBlock* > blocks = getBlocks(blockStorage); + WALBERLA_NON_MPI_SECTION() { - readFromFileNonMPI( blocks ); + readFromFileNonMPI(blocks); return; } - std::vector< uint_t > blockOffsets = computeBlockOffsets( blocks ); - - if( blockOffsets.back() > uint_c( std::numeric_limits<int>::max() ) ) - WALBERLA_ABORT( "readFromFile does not support reading more than " << std::numeric_limits<int>::max() << " field elements per process!" ); - - MPI_Datatype arraytype; - MPI_Type_contiguous( int_c( blockOffsets.back() ), MPITrait< typename FieldT::value_type >::type(), &arraytype ); - MPI_Type_commit( &arraytype ); - - const MPI_Offset offset = computeProcessByteOffset( blockOffsets.back() ); + std::vector< uint_t > blockOffsets = computeBlockOffsets(blocks); + const MPI_Offset offset = computeProcessByteOffset(blockOffsets.back()); - MPI_File mpiFile; - int result = MPI_SUCCESS; - result = MPI_File_open( MPIManager::instance()->comm(), const_cast<char*>( filename_.c_str() ), MPI_MODE_RDONLY, MPI_INFO_NULL, &mpiFile ); + if (blockOffsets.back() > uint_c(std::numeric_limits< int >::max())) + WALBERLA_ABORT("readFromFile does not support reading more than " << std::numeric_limits< int >::max() + << " field elements per process!"); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while opening file \"" << filename_ << "\" for reading. MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + // use serial I/O for versions of OpenMPI that produce segmentation faults when using MPI-IO with a 3D Cartesian MPI + // communicator (see waLBerla issue #73) + if (!MPIManager::instance()->isCommMPIIOValid()) + { + std::ifstream ifs; - result = MPI_File_set_view( mpiFile, offset, MPITrait< typename FieldT::value_type >::type(), arraytype, const_cast<char*>( "native" ), MPI_INFO_NULL ); + // read every block's field from file + for (int i = 0; i != MPIManager::instance()->numProcesses(); ++i) + { + if (i == MPIManager::instance()->rank()) + { + ifs.open(filename_.c_str(), std::ifstream::in | std::ifstream::binary); + if (ifs.fail()) { WALBERLA_ABORT("Error while opening file \"" << filename_ << "\" for reading."); } + + size_t blockIdx = 0; + for (auto block = blocks.begin(); block != blocks.end(); ++block, ++blockIdx) + { + FieldT* field = (*block)->template getData< FieldT >(fieldID_); + + std::vector< typename FieldT::value_type > data(field->xSize() * field->ySize() * field->zSize() * + field->fSize()); + + ifs.seekg(numeric_cast< std::streamoff >(numeric_cast< uint_t >(offset) + + blockOffsets[blockIdx] * sizeof(typename FieldT::value_type))); + ifs.read(reinterpret_cast< char* >(&data[0]), + numeric_cast< std::streamsize >(data.size() * sizeof(typename FieldT::value_type))); + if (ifs.fail()) { WALBERLA_ABORT("Error while reading from file \"" << filename_ << "\"."); } + + auto dataIt = data.begin(); + for (auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt, ++dataIt) + { + WALBERLA_ASSERT_UNEQUAL(dataIt, data.end()); + *fieldIt = *dataIt; + } + WALBERLA_ASSERT_EQUAL(dataIt, data.end()); + } + ifs.close(); + if (ifs.fail()) { WALBERLA_ABORT("Error while closing file \"" << filename_ << "\" for reading."); } + } + WALBERLA_MPI_BARRIER(); + } + } + else // use MPI-IO + { + MPI_Datatype arraytype; + MPI_Type_contiguous(int_c(blockOffsets.back()), MPITrait< typename FieldT::value_type >::type(), &arraytype); + MPI_Type_commit(&arraytype); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + MPI_File mpiFile; + int result = MPI_SUCCESS; + result = MPI_File_open(MPIManager::instance()->comm(), const_cast< char* >(filename_.c_str()), MPI_MODE_RDONLY, + MPI_INFO_NULL, &mpiFile); - size_t blockIdx = 0; - for( auto block = blocks.begin(); block != blocks.end(); ++block, ++blockIdx ) - { - FieldT * field = (*block)->template getData<FieldT>( fieldID_ ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while opening file \"" << filename_ << "\" for reading. MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); - std::vector< typename FieldT::value_type > data( field->xSize() * field->ySize() * field->zSize() * field->fSize() ); + result = MPI_File_set_view(mpiFile, offset, MPITrait< typename FieldT::value_type >::type(), arraytype, + const_cast< char* >("native"), MPI_INFO_NULL); - result = MPI_File_read_at( mpiFile, numeric_cast<MPI_Offset>( blockOffsets[blockIdx] ), &data[0], int_c( data.size() ), - MPITrait<typename FieldT::value_type>::type(), MPI_STATUS_IGNORE ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Internal MPI-IO error! MPI Error is \"" << MPIManager::instance()->getMPIErrorString(result) + << "\""); - auto dataIt = data.begin(); - for( auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt, ++dataIt ) + size_t blockIdx = 0; + for (auto block = blocks.begin(); block != blocks.end(); ++block, ++blockIdx) { - WALBERLA_ASSERT_UNEQUAL( dataIt, data.end() ); - *fieldIt = *dataIt; + FieldT* field = (*block)->template getData< FieldT >(fieldID_); + + std::vector< typename FieldT::value_type > data(field->xSize() * field->ySize() * field->zSize() * + field->fSize()); + + result = + MPI_File_read_at(mpiFile, numeric_cast< MPI_Offset >(blockOffsets[blockIdx]), &data[0], int_c(data.size()), + MPITrait< typename FieldT::value_type >::type(), MPI_STATUS_IGNORE); + + auto dataIt = data.begin(); + for (auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt, ++dataIt) + { + WALBERLA_ASSERT_UNEQUAL(dataIt, data.end()); + *fieldIt = *dataIt; + } + WALBERLA_ASSERT_EQUAL(dataIt, data.end()); + + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while reading from file \"" << filename_ << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) + << "\""); } - WALBERLA_ASSERT_EQUAL( dataIt, data.end() ); - - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while reading from file \"" << filename_ << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); - } - result = MPI_File_close( &mpiFile ); + result = MPI_File_close(&mpiFile); - if( result != MPI_SUCCESS ) - WALBERLA_ABORT( "Error while closing file \"" << filename_ << "\". MPI Error is \"" << MPIManager::instance()->getMPIErrorString( result ) << "\"" ); + if (result != MPI_SUCCESS) + WALBERLA_ABORT("Error while closing file \"" << filename_ << "\". MPI Error is \"" + << MPIManager::instance()->getMPIErrorString(result) << "\""); - MPI_Type_free( &arraytype ); + MPI_Type_free(&arraytype); + } } - - -inline bool sortBlocksByID( IBlock * lhs, IBlock * rhs ) { return lhs->getId() < rhs->getId(); } +inline bool sortBlocksByID(IBlock* lhs, IBlock* rhs) { return lhs->getId() < rhs->getId(); } template< typename FieldT > -std::vector< IBlock * > FieldWriter<FieldT>::getBlocks( BlockStorage & blockStorage ) const +std::vector< IBlock* > FieldWriter< FieldT >::getBlocks(BlockStorage& blockStorage) const { - std::vector< IBlock * > blocks; - for( auto it = blockStorage.begin( requiredSelectors_, incompatibleSelectors_ ); it != blockStorage.end(); ++it ) - blocks.push_back( it.get() ); - std::sort( blocks.begin(), blocks.end(), sortBlocksByID ); + std::vector< IBlock* > blocks; + for (auto it = blockStorage.begin(requiredSelectors_, incompatibleSelectors_); it != blockStorage.end(); ++it) + blocks.push_back(it.get()); + std::sort(blocks.begin(), blocks.end(), sortBlocksByID); return blocks; } - - -inline bool sortConstBlocksByID( const IBlock * lhs, const IBlock * rhs ) { return lhs->getId() < rhs->getId(); } +inline bool sortConstBlocksByID(const IBlock* lhs, const IBlock* rhs) { return lhs->getId() < rhs->getId(); } template< typename FieldT > -std::vector< const IBlock * > FieldWriter<FieldT>::getBlocks( const BlockStorage & blockStorage ) const +std::vector< const IBlock* > FieldWriter< FieldT >::getBlocks(const BlockStorage& blockStorage) const { - std::vector< const IBlock * > blocks; - for( auto it = blockStorage.begin( requiredSelectors_, incompatibleSelectors_ ); it != blockStorage.end(); ++it ) - blocks.push_back( it.get() ); - std::sort( blocks.begin(), blocks.end(), sortConstBlocksByID ); + std::vector< const IBlock* > blocks; + for (auto it = blockStorage.begin(requiredSelectors_, incompatibleSelectors_); it != blockStorage.end(); ++it) + blocks.push_back(it.get()); + std::sort(blocks.begin(), blocks.end(), sortConstBlocksByID); return blocks; } - - template< typename FieldT > -void FieldWriter<FieldT>::writeToFileNonMPI( const std::vector< const IBlock * > & blocks ) const +void FieldWriter< FieldT >::writeToFileNonMPI(const std::vector< const IBlock* >& blocks) const { - std::ofstream ofs( filename_.c_str(), std::ofstream::out | std::ofstream::binary ); + std::ofstream ofs(filename_.c_str(), std::ofstream::out | std::ofstream::binary); - for( auto block = blocks.begin(); block != blocks.end(); ++block ) + for (auto block = blocks.begin(); block != blocks.end(); ++block) { - const FieldT * field = (*block)->template getData<FieldT>( fieldID_ ); + const FieldT* field = (*block)->template getData< FieldT >(fieldID_); - for( auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt ) + for (auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt) { - ofs.write( reinterpret_cast<const char*>( &( *fieldIt ) ), sizeof( typename FieldT::value_type ) ); + ofs.write(reinterpret_cast< const char* >(&(*fieldIt)), sizeof(typename FieldT::value_type)); } } ofs.close(); } - - template< typename FieldT > -void FieldWriter<FieldT>::readFromFileNonMPI( const std::vector< IBlock * > & blocks ) const +void FieldWriter< FieldT >::readFromFileNonMPI(const std::vector< IBlock* >& blocks) const { - std::ifstream ifs( filename_.c_str(), std::ifstream::in | std::ifstream::binary ); + std::ifstream ifs(filename_.c_str(), std::ifstream::in | std::ifstream::binary); - for( auto block = blocks.begin(); block != blocks.end(); ++block ) + for (auto block = blocks.begin(); block != blocks.end(); ++block) { - FieldT * field = (*block)->template getData<FieldT>( fieldID_ ); + FieldT* field = (*block)->template getData< FieldT >(fieldID_); - for( auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt ) + for (auto fieldIt = field->begin(); fieldIt != field->end(); ++fieldIt) { - ifs.read( reinterpret_cast<char*>( &( *fieldIt ) ), sizeof( typename FieldT::value_type ) ); + ifs.read(reinterpret_cast< char* >(&(*fieldIt)), sizeof(typename FieldT::value_type)); } } ifs.close(); } - - template< typename FieldT > -std::vector< uint_t > FieldWriter<FieldT>::computeBlockOffsets( const std::vector< const IBlock * > & blocks ) const +std::vector< uint_t > FieldWriter< FieldT >::computeBlockOffsets(const std::vector< const IBlock* >& blocks) const { std::vector< uint_t > blockOffsets; - blockOffsets.push_back( 0 ); + blockOffsets.push_back(0); - for( auto block = blocks.begin(); block != blocks.end(); ++block ) + for (auto block = blocks.begin(); block != blocks.end(); ++block) { - const FieldT * field = (*block)->template getData<FieldT>( fieldID_ ); + const FieldT* field = (*block)->template getData< FieldT >(fieldID_); const uint_t offset = blockOffsets.back() + field->xSize() * field->ySize() * field->zSize() * field->fSize(); - blockOffsets.push_back( offset ); + blockOffsets.push_back(offset); } return blockOffsets; } - - template< typename FieldT > -std::vector< uint_t > FieldWriter<FieldT>::computeBlockOffsets( const std::vector< IBlock * > & blocks ) const +std::vector< uint_t > FieldWriter< FieldT >::computeBlockOffsets(const std::vector< IBlock* >& blocks) const { std::vector< uint_t > blockOffsets; - blockOffsets.push_back( 0 ); + blockOffsets.push_back(0); - for( auto block = blocks.begin(); block != blocks.end(); ++block ) + for (auto block = blocks.begin(); block != blocks.end(); ++block) { - const FieldT * field = (*block)->template getData<FieldT>( fieldID_ ); + const FieldT* field = (*block)->template getData< FieldT >(fieldID_); const uint_t offset = blockOffsets.back() + field->xSize() * field->ySize() * field->zSize() * field->fSize(); - blockOffsets.push_back( offset ); + blockOffsets.push_back(offset); } return blockOffsets; } - - template< typename FieldT > -MPI_Offset FieldWriter<FieldT>::computeProcessByteOffset( uint_t processNumElements ) const +MPI_Offset FieldWriter< FieldT >::computeProcessByteOffset(uint_t processNumElements) const { uint_t exscanResult; - MPI_Exscan( &processNumElements, &exscanResult, 1, MPITrait<uint_t>::type(), MPI_SUM, MPIManager::instance()->comm() ); - if( MPIManager::instance()->rank() == 0 ) - exscanResult = uint_t( 0 ); + MPI_Exscan(&processNumElements, &exscanResult, 1, MPITrait< uint_t >::type(), MPI_SUM, + MPIManager::instance()->comm()); + if (MPIManager::instance()->rank() == 0) exscanResult = uint_t(0); - return numeric_cast<MPI_Offset>( exscanResult * sizeof( typename FieldT::value_type ) ); + return numeric_cast< MPI_Offset >(exscanResult * sizeof(typename FieldT::value_type)); } - } // namespace internal - - template< typename FieldT > -void writeToFile( const std::string & filename, const BlockStorage & blockStorage, const BlockDataID & fieldID, - const Set<SUID> & requiredSelectors, const Set<SUID> & incompatibleSelectors ) +void writeToFile(const std::string& filename, const BlockStorage& blockStorage, const BlockDataID& fieldID, + const Set< SUID >& requiredSelectors, const Set< SUID >& incompatibleSelectors) { - internal::FieldWriter<FieldT> writer( filename, fieldID, requiredSelectors, incompatibleSelectors ); - writer.writeToFile( blockStorage ); + internal::FieldWriter< FieldT > writer(filename, fieldID, requiredSelectors, incompatibleSelectors); + writer.writeToFile(blockStorage); } - - template< typename FieldT > -void readFromFile( const std::string & filename, BlockStorage & blockStorage, const BlockDataID & fieldID, - const Set<SUID> & requiredSelectors, const Set<SUID> & incompatibleSelectors ) +void readFromFile(const std::string& filename, BlockStorage& blockStorage, const BlockDataID& fieldID, + const Set< SUID >& requiredSelectors, const Set< SUID >& incompatibleSelectors) { - internal::FieldWriter<FieldT> writer( filename, fieldID, requiredSelectors, incompatibleSelectors ); - writer.readFromFile( blockStorage ); + internal::FieldWriter< FieldT > writer(filename, fieldID, requiredSelectors, incompatibleSelectors); + writer.readFromFile(blockStorage); } -} // namespace walberla } // namespace field +} // namespace walberla diff --git a/tests/blockforest/BlockDataIOTest.cpp b/tests/blockforest/BlockDataIOTest.cpp index 291b3322a6e1b1324bf74f2ce27cbaed5ec0423e..7f1507bc1ca508cca10dd070d5feb7f549d5ffe9 100644 --- a/tests/blockforest/BlockDataIOTest.cpp +++ b/tests/blockforest/BlockDataIOTest.cpp @@ -1,21 +1,22 @@ //====================================================================================================================== // -// This file is part of waLBerla. waLBerla is free software: you can +// 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 +// 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 +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. -// +// // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // //! \file BlockDataIOTest.cpp //! \ingroup field //! \author Florian Schornbaum <florian.schornbaum@fau.de> +//! \author Christoph Schwarzmeier <christoph.schwarzmeier@fau.de> // //====================================================================================================================== @@ -27,102 +28,110 @@ #include "core/debug/TestSubsystem.h" #include "core/math/Random.h" #include "core/mpi/Environment.h" -#include "core/timing/Timer.h" #include "field/AddToStorage.h" #include "field/Field.h" - -namespace block_data_io_test { - +namespace block_data_io_test +{ using namespace walberla; using walberla::uint8_t; -const SUID Empty( "empty" ); -const Set<SUID> None( Set<SUID>::emptySet() ); +const SUID Empty("empty"); +const Set< SUID > None(Set< SUID >::emptySet()); -static void refinementSelectionFunction( SetupBlockForest& forest ) +static void refinementSelectionFunction(SetupBlockForest& forest) { - for( auto block = forest.begin(); block != forest.end(); ++block ) - if( block->getAABB().contains( Vector3<real_t>( real_t(75) ) ) ) - if( !block->hasFather() ) - block->setMarker( true ); + for (auto block = forest.begin(); block != forest.end(); ++block) + if (block->getAABB().contains(Vector3< real_t >(real_t(75)))) + if (!block->hasFather()) block->setMarker(true); } -static void workloadMemorySUIDAssignmentFunction( SetupBlockForest& forest ) +static void workloadMemorySUIDAssignmentFunction(SetupBlockForest& forest) { - for( auto block = forest.begin(); block != forest.end(); ++block ) + for (auto block = forest.begin(); block != forest.end(); ++block) { - block->setMemory( memory_t(1) ); - block->setWorkload( workload_t(1) ); - if( block->getAABB().contains( Vector3<real_t>( real_t(25) ) ) ) - block->addState( Empty ); + block->setMemory(memory_t(1)); + block->setWorkload(workload_t(1)); + if (block->getAABB().contains(Vector3< real_t >(real_t(25)))) block->addState(Empty); } } -int main( int argc, char* argv[] ) +void test() { - typedef field::GhostLayerField<double, 2> FieldType; + typedef field::GhostLayerField< double, 2 > FieldType; - debug::enterTestMode(); + SetupBlockForest sforest; - mpi::Environment mpiEnv( argc, argv ); + sforest.addRefinementSelectionFunction(refinementSelectionFunction); + sforest.addWorkloadMemorySUIDAssignmentFunction(workloadMemorySUIDAssignmentFunction); - MPIManager::instance()->useWorldComm(); + AABB domain(0, 0, 0, 100, 100, 100); - SetupBlockForest sforest; + sforest.init(domain, uint_t(2), uint_t(2), uint_t(2), true, false, false); - sforest.addRefinementSelectionFunction( refinementSelectionFunction ); - sforest.addWorkloadMemorySUIDAssignmentFunction( workloadMemorySUIDAssignmentFunction ); + sforest.balanceLoad(blockforest::StaticLevelwiseCurveBalance(true), uint_c(MPIManager::instance()->numProcesses())); - AABB domain( 0, 0, 0, 100, 100, 100 ); - - sforest.init( domain, uint_t(2), uint_t(2), uint_t(2), true, false, false ); + auto sbf = make_shared< StructuredBlockForest >( + make_shared< BlockForest >(uint_c(MPIManager::instance()->rank()), sforest, true), uint_t(10), uint_t(8), + uint_t(14)); - sforest.balanceLoad( blockforest::StaticLevelwiseCurveBalance(true), uint_c( MPIManager::instance()->numProcesses() ) ); - - auto sbf = make_shared<StructuredBlockForest>( make_shared< BlockForest >( uint_c( MPIManager::instance()->rank() ), sforest, true ), - uint_t(10), uint_t(8), uint_t(14) ); + blockforest::BlockForestEvaluation evaluation(sbf->getBlockForest()); + WALBERLA_LOG_INFO_ON_ROOT("BlockForest:\n" << evaluation.toString()); - blockforest::BlockForestEvaluation evaluation( sbf->getBlockForest() ); - WALBERLA_LOG_INFO_ON_ROOT( "BlockForest:\n" << evaluation.toString() ); + // auto originalFieldId = field::addToStorage< FieldType >( sbf, "OriginalField", 0.0, field::zyxf, uint_t(3), false, + // None, Empty ); + auto dataHandling = make_shared< field::DefaultBlockDataHandling< FieldType > >(sbf, uint_t(3), 0.0, field::zyxf); + auto originalFieldId = sbf->addBlockData(dataHandling, "OriginalField", None, Empty); - //auto originalFieldId = field::addToStorage< FieldType >( sbf, "OriginalField", 0.0, field::zyxf, uint_t(3), false, None, Empty ); - auto dataHandling = make_shared< field::DefaultBlockDataHandling< FieldType > >( sbf, uint_t(3), 0.0, field::zyxf ); - auto originalFieldId = sbf->addBlockData( dataHandling, "OriginalField", None, Empty ); - - math::seedRandomGenerator( numeric_cast<std::mt19937::result_type>( MPIManager::instance()->rank() ) ); + math::seedRandomGenerator(numeric_cast< std::mt19937::result_type >(MPIManager::instance()->rank())); - for( auto it = sbf->begin( None, Empty ); it != sbf->end(); ++it ) + for (auto it = sbf->begin(None, Empty); it != sbf->end(); ++it) { - auto field = it->getData< FieldType >( originalFieldId ); + auto field = it->getData< FieldType >(originalFieldId); - for( auto dataIt = field->begin(); dataIt != field->end(); ++dataIt ) + for (auto dataIt = field->begin(); dataIt != field->end(); ++dataIt) *dataIt = math::realRandom< FieldType::value_type >(); } - - sbf->saveBlockData( "block.data", originalFieldId ); - + + sbf->saveBlockData("block.data", originalFieldId); + WALBERLA_MPI_BARRIER() - - auto readFieldId = sbf->loadBlockData( "block.data", dataHandling, "ReadField", None, Empty ); - - for( auto it = sbf->begin( None, Empty ); it != sbf->end(); ++it ) + + auto readFieldId = sbf->loadBlockData("block.data", dataHandling, "ReadField", None, Empty); + + for (auto it = sbf->begin(None, Empty); it != sbf->end(); ++it) { - auto originalField = it->getData< FieldType >( originalFieldId ); - auto readField = it->getData< FieldType >( readFieldId ); + auto originalField = it->getData< FieldType >(originalFieldId); + auto readField = it->getData< FieldType >(readFieldId); auto readIt = readField->begin(); - for( auto origIt = originalField->begin(); origIt != originalField->end(); ++origIt, ++readIt ) - WALBERLA_CHECK_IDENTICAL( *origIt, *readIt ); + for (auto origIt = originalField->begin(); origIt != originalField->end(); ++origIt, ++readIt) + WALBERLA_CHECK_IDENTICAL(*origIt, *readIt); } - - return EXIT_SUCCESS; } -} +} // namespace block_data_io_test -int main( int argc, char* argv[] ) +int main(int argc, char* argv[]) { - return block_data_io_test::main( argc, argv ); + walberla::debug::enterTestMode(); + + walberla::mpi::Environment mpiEnv(argc, argv); + + // test with MPI_WORLD_COMM + walberla::MPIManager::instance()->useWorldComm(); + block_data_io_test::test(); + + // test with Cartesian MPI communicator + // this is tested additionally because some versions of OpenMPI are known to produce segmentation faults when using + // MPI-IO with a 3D Cartesian MPI communicator; for those OpenMPI versions, serial I/O is used instead + if (walberla::MPIManager::instance()->numProcesses() == 8) + { + walberla::MPIManager::instance()->resetMPI(); + + walberla::MPIManager::instance()->createCartesianComm(walberla::uint_c(2), walberla::uint_c(2), + walberla::uint_c(2), false, false, false); + block_data_io_test::test(); + } } diff --git a/tests/core/mpi/MPITextFileTest.cpp b/tests/core/mpi/MPITextFileTest.cpp index f3e17dbd188ad3f0a091fafa801cfdb58303e252..34eeba5099cb4b46b9965fdd2126e89a258dce99 100644 --- a/tests/core/mpi/MPITextFileTest.cpp +++ b/tests/core/mpi/MPITextFileTest.cpp @@ -1,81 +1,75 @@ //====================================================================================================================== // -// This file is part of waLBerla. waLBerla is free software: you can +// 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 +// 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 +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. -// +// // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // //! \file MPITextFileText.cpp //! \ingroup core //! \author Christian Godenschwager <christian.godenschwager@fau.de> +//! \author Christoph Schwarzmeier <christoph.schwarzmeier@fau.de> // //====================================================================================================================== +#include "core/mpi/MPITextFile.h" + #include "core/Abort.h" #include "core/DataTypes.h" - +#include "core/Filesystem.h" #include "core/debug/TestSubsystem.h" - #include "core/mpi/MPIManager.h" -#include "core/mpi/MPITextFile.h" - #include "core/stringToNum.h" -#include "core/Filesystem.h" - -#include <vector> -#include <sstream> #include <fstream> +#include <sstream> +#include <vector> - -void testSameSizeFile( const std::string & filename, const size_t chunkSize ) +void testSameSizeFile(const std::string& filename, const size_t chunkSize) { using namespace walberla; - WALBERLA_CHECK_GREATER( chunkSize, 0 ); + WALBERLA_CHECK_GREATER(chunkSize, 0); const int rank = MPIManager::instance()->rank(); std::ostringstream oss; oss << rank; - std::string chunk( chunkSize, char( 'A' + static_cast<char>( rank % 26 ) ) ); - chunk[ chunk.size() - size_t(1) ] = '\n'; - - mpi::writeMPITextFile( filename, chunk ); + std::string chunk(chunkSize, char('A' + static_cast< char >(rank % 26))); + chunk[chunk.size() - size_t(1)] = '\n'; - std::ifstream ifs( filename.c_str() ); - ifs.seekg( numeric_cast< std::ifstream::off_type >( uint_c(rank) * chunkSize ), std::ios_base::beg ); - std::vector<char> buffer( chunkSize ); - ifs.read( &(buffer[0]), numeric_cast< std::streamsize >( chunkSize ) ); + mpi::writeMPITextFile(filename, chunk); + + std::ifstream ifs(filename.c_str()); + ifs.seekg(numeric_cast< std::ifstream::off_type >(uint_c(rank) * chunkSize), std::ios_base::beg); + std::vector< char > buffer(chunkSize); + ifs.read(&(buffer[0]), numeric_cast< std::streamsize >(chunkSize)); ifs.close(); - std::string referenceChunk( buffer.begin(), buffer.end() ); + std::string referenceChunk(buffer.begin(), buffer.end()); - WALBERLA_CHECK_EQUAL( chunk, referenceChunk ); + WALBERLA_CHECK_EQUAL(chunk, referenceChunk); WALBERLA_MPI_BARRIER(); WALBERLA_ROOT_SECTION() { - if( filesystem::exists( filename ) ) - filesystem::remove( filename ); + if (filesystem::exists(filename)) filesystem::remove(filename); } WALBERLA_MPI_BARRIER(); } - - -void testDifferentSizeFile( const std::string & filename, const size_t minChunkSize ) +void testDifferentSizeFile(const std::string& filename, const size_t minChunkSize) { using namespace walberla; - WALBERLA_CHECK_GREATER( minChunkSize, 0 ); + WALBERLA_CHECK_GREATER(minChunkSize, 0); const int rank = MPIManager::instance()->rank(); std::ostringstream oss; @@ -83,53 +77,64 @@ void testDifferentSizeFile( const std::string & filename, const size_t minChunkS const size_t chunkSize = minChunkSize * uint_c(rank + 1); - std::string chunk(chunkSize , char( 'A' + static_cast<char>( rank % 26 ) ) ); - chunk[ chunk.size() - size_t(1) ] = '\n'; + std::string chunk(chunkSize, char('A' + static_cast< char >(rank % 26))); + chunk[chunk.size() - size_t(1)] = '\n'; - mpi::writeMPITextFile( filename, chunk ); + mpi::writeMPITextFile(filename, chunk); - std::ifstream ifs( filename.c_str() ); - ifs.seekg( numeric_cast< std::ifstream::off_type >( uint_c( ( rank * rank + rank ) / 2 ) * minChunkSize ), std::ios_base::beg ); - std::vector<char> buffer( chunkSize ); - ifs.read( &(buffer[0]), numeric_cast< std::streamsize >( chunkSize ) ); + std::ifstream ifs(filename.c_str()); + ifs.seekg(numeric_cast< std::ifstream::off_type >(uint_c((rank * rank + rank) / 2) * minChunkSize), + std::ios_base::beg); + std::vector< char > buffer(chunkSize); + ifs.read(&(buffer[0]), numeric_cast< std::streamsize >(chunkSize)); ifs.close(); - std::string referenceChunk( buffer.begin(), buffer.end() ); + std::string referenceChunk(buffer.begin(), buffer.end()); - WALBERLA_CHECK_EQUAL( chunk, referenceChunk ); + WALBERLA_CHECK_EQUAL(chunk, referenceChunk); WALBERLA_MPI_BARRIER(); WALBERLA_ROOT_SECTION() { - if( filesystem::exists( filename ) ) - filesystem::remove( filename ); + if (filesystem::exists(filename)) filesystem::remove(filename); } WALBERLA_MPI_BARRIER(); } - - -int main( int argc, char * argv[] ) +int main(int argc, char* argv[]) { - walberla::MPIManager::instance()->initializeMPI( &argc, &argv ); - - walberla::debug::enterTestMode(); + walberla::MPIManager::instance()->initializeMPI(&argc, &argv); - walberla::MPIManager::instance()->useWorldComm(); + walberla::debug::enterTestMode(); - std::vector<std::string> args( argv, argv + argc ); + std::vector< std::string > args(argv, argv + argc); - size_t chunkSize; + size_t chunkSize; std::string filename; try { - chunkSize = walberla::stringToNum<size_t>( args.at(2) ); - filename = args.at( 1 ); - } - catch( ... ) + chunkSize = walberla::stringToNum< size_t >(args.at(2)); + filename = args.at(1); + } catch (...) + { - WALBERLA_ABORT_NO_DEBUG_INFO( "Usage:\n" << args[0] << " FILENAME CHUNK_SIZE" ); + WALBERLA_ABORT_NO_DEBUG_INFO("Usage:\n" << args[0] << " FILENAME CHUNK_SIZE"); } - testSameSizeFile( filename, chunkSize ); - testDifferentSizeFile( filename, chunkSize ); + // test with MPI_WORLD_COMM + walberla::MPIManager::instance()->useWorldComm(); + testSameSizeFile(filename, chunkSize); + testDifferentSizeFile(filename, chunkSize); + + // test with Cartesian MPI communicator + // this is tested additionally since some versions of OpenMPI are known to produce segmentation faults when using + // MPI-IO with a 3D Cartesian MPI communicator; for those OpenMPI versions, serial I/O is used instead + if (walberla::MPIManager::instance()->numProcesses() == 8) + { + walberla::MPIManager::instance()->resetMPI(); + walberla::MPIManager::instance()->createCartesianComm(walberla::uint_c(2), walberla::uint_c(2), + walberla::uint_c(2), false, false, false); + + testSameSizeFile(filename, chunkSize); + testDifferentSizeFile(filename, chunkSize); + } } diff --git a/tests/field/FieldFileIOTest.cpp b/tests/field/FieldFileIOTest.cpp index 53d1c106166cab0265eff8f5d96510dd3c6ce960..8559deacfd9f9a0eb567f5c276e241b0df451fd7 100644 --- a/tests/field/FieldFileIOTest.cpp +++ b/tests/field/FieldFileIOTest.cpp @@ -1,21 +1,22 @@ //====================================================================================================================== // -// This file is part of waLBerla. waLBerla is free software: you can +// 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 +// 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 +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. -// +// // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // //! \file FieldFileIOTest.cpp //! \ingroup field //! \author Christian Godenschwager <christian.godenschwager@fau.de> +//! \author Christoph Schwarzmeier <christoph.schwarzmeier@fau.de> // //====================================================================================================================== @@ -24,102 +25,95 @@ #include "blockforest/loadbalancing/StaticCurve.h" #include "core/debug/TestSubsystem.h" +#include "core/math/IntegerFactorization.h" #include "core/math/Random.h" #include "core/mpi/Environment.h" -#include "core/timing/Timer.h" -#include "core/math/IntegerFactorization.h" #include "core/stringToNum.h" +#include "core/timing/Timer.h" #include "field/AddToStorage.h" #include "field/Field.h" #include "field/FileIO.h" - -namespace mpi_file_io_test { - +namespace mpi_file_io_test +{ using namespace walberla; using walberla::uint8_t; -static void refinementSelectionFunction( SetupBlockForest& forest ) +static void refinementSelectionFunction(SetupBlockForest& forest) { const uint_t numRootBlocks = forest.getNumberOfRootBlocks(); - for( uint_t i = 0; i < numRootBlocks; i += 8 ) + for (uint_t i = 0; i < numRootBlocks; i += 8) { - SetupBlock* block = forest.getRootBlock( i ); + SetupBlock* block = forest.getRootBlock(i); - if( !block->hasChildren() ) - block->setMarker( true ); + if (!block->hasChildren()) block->setMarker(true); } } - - -static void workloadMemorySUIDAssignmentFunction( SetupBlockForest& forest ) +static void workloadMemorySUIDAssignmentFunction(SetupBlockForest& forest) { std::vector< SetupBlock* > blocks; - forest.getBlocks( blocks ); + forest.getBlocks(blocks); - for( uint_t i = 0; i != blocks.size(); ++i ) { - blocks[i]->setMemory( 1.0 ); - blocks[i]->setWorkload( 1.0 ); + for (uint_t i = 0; i != blocks.size(); ++i) + { + blocks[i]->setMemory(1.0); + blocks[i]->setWorkload(1.0); } } - -int main( int argc, char* argv[] ) +void test(int argc, char* argv[]) { - typedef field::GhostLayerField<double, 3> FieldType; + typedef field::GhostLayerField< double, 3 > FieldType; - debug::enterTestMode(); + std::vector< std::string > args(argv, argv + argc); - mpi::Environment mpiEnv( argc, argv ); - - MPIManager::instance()->useWorldComm(); - - std::vector<std::string> args( argv, argv + argc ); - - uint_t numBlocks = 8; + uint_t numBlocks = 8; uint_t xBlockSize = 3; uint_t yBlockSize = 5; uint_t zBlockSize = 7; - - if( args.size() == 5 ) + + if (args.size() == 5) { - numBlocks = stringToNum<uint_t>( args[1] ); - xBlockSize = stringToNum<uint_t>( args[2] ); - yBlockSize = stringToNum<uint_t>( args[3] ); - zBlockSize = stringToNum<uint_t>( args[4] ); + numBlocks = stringToNum< uint_t >(args[1]); + xBlockSize = stringToNum< uint_t >(args[2]); + yBlockSize = stringToNum< uint_t >(args[3]); + zBlockSize = stringToNum< uint_t >(args[4]); } - else if( args.size() > 5 ) + else if (args.size() > 5) { - WALBERLA_ABORT( "USAGE:\n\n" << args[0] << " <NUMBER_OF_COARSE_BLOCKS> <X_BLOCK_SIZE> <Y_BLOCK_SIZE> <Z_BLOCK_SIZE>" ); + WALBERLA_ABORT("USAGE:\n\n" + << args[0] << " <NUMBER_OF_COARSE_BLOCKS> <X_BLOCK_SIZE> <Y_BLOCK_SIZE> <Z_BLOCK_SIZE>"); } SetupBlockForest sforest; - sforest.addRefinementSelectionFunction( refinementSelectionFunction ); - sforest.addWorkloadMemorySUIDAssignmentFunction( workloadMemorySUIDAssignmentFunction ); + sforest.addRefinementSelectionFunction(refinementSelectionFunction); + sforest.addWorkloadMemorySUIDAssignmentFunction(workloadMemorySUIDAssignmentFunction); - AABB domain( 0, 0, 0, 100, 100, 100 ); + AABB domain(0, 0, 0, 100, 100, 100); - auto factors = math::getFactors3D( numBlocks ); + auto factors = math::getFactors3D(numBlocks); - sforest.init( domain, factors[0], factors[1], factors[2], true, false, false ); + sforest.init(domain, factors[0], factors[1], factors[2], true, false, false); - sforest.balanceLoad( blockforest::StaticLevelwiseCurveBalance(true), uint_c( MPIManager::instance()->numProcesses() ) ); + sforest.balanceLoad(blockforest::StaticLevelwiseCurveBalance(true), uint_c(MPIManager::instance()->numProcesses())); - auto sbf = make_shared<StructuredBlockForest>( make_shared< BlockForest >( uint_c( MPIManager::instance()->rank() ), sforest, true ), xBlockSize, yBlockSize, zBlockSize ); + auto sbf = make_shared< StructuredBlockForest >( + make_shared< BlockForest >(uint_c(MPIManager::instance()->rank()), sforest, true), xBlockSize, yBlockSize, + zBlockSize); - auto originalFieldId = field::addToStorage< FieldType >( sbf, "OriginalField" ); - auto readFieldId = field::addToStorage< FieldType >( sbf, "ReadField" ); + auto originalFieldId = field::addToStorage< FieldType >(sbf, "OriginalField"); + auto readFieldId = field::addToStorage< FieldType >(sbf, "ReadField"); - math::seedRandomGenerator( numeric_cast<std::mt19937::result_type>( MPIManager::instance()->rank() ) ); + math::seedRandomGenerator(numeric_cast< std::mt19937::result_type >(MPIManager::instance()->rank())); - for( auto it = sbf->begin(); it != sbf->end(); ++it ) + for (auto it = sbf->begin(); it != sbf->end(); ++it) { - auto field = it->getData< FieldType >( originalFieldId ); + auto field = it->getData< FieldType >(originalFieldId); - for( auto dataIt = field->begin(); dataIt != field->end(); ++dataIt ) + for (auto dataIt = field->begin(); dataIt != field->end(); ++dataIt) *dataIt = math::realRandom< FieldType::value_type >(); } @@ -127,34 +121,50 @@ int main( int argc, char* argv[] ) WALBERLA_MPI_BARRIER(); timer.start(); - field::writeToFile<FieldType>( "mpiFile.wlb", sbf->getBlockStorage(), originalFieldId ); + field::writeToFile< FieldType >("mpiFile.wlb", sbf->getBlockStorage(), originalFieldId); WALBERLA_MPI_BARRIER(); timer.end(); - WALBERLA_LOG_INFO_ON_ROOT( "Writing took " << timer.last() << "s" ); + WALBERLA_LOG_INFO_ON_ROOT("Writing took " << timer.last() << "s"); WALBERLA_MPI_BARRIER(); timer.start(); - field::readFromFile<FieldType>( "mpiFile.wlb", sbf->getBlockStorage(), readFieldId ); + field::readFromFile< FieldType >("mpiFile.wlb", sbf->getBlockStorage(), readFieldId); WALBERLA_MPI_BARRIER(); timer.end(); - WALBERLA_LOG_INFO_ON_ROOT( "Reading took " << timer.last() << "s" ); + WALBERLA_LOG_INFO_ON_ROOT("Reading took " << timer.last() << "s"); - for( auto it = sbf->begin(); it != sbf->end(); ++it ) + for (auto it = sbf->begin(); it != sbf->end(); ++it) { - auto originalField = it->getData< FieldType >( originalFieldId ); - auto readField = it->getData< FieldType >( readFieldId ); + auto originalField = it->getData< FieldType >(originalFieldId); + auto readField = it->getData< FieldType >(readFieldId); auto readIt = readField->begin(); - for( auto origIt = originalField->begin(); origIt != originalField->end(); ++origIt, ++readIt ) - WALBERLA_CHECK_IDENTICAL( *origIt, *readIt ); + for (auto origIt = originalField->begin(); origIt != originalField->end(); ++origIt, ++readIt) + WALBERLA_CHECK_IDENTICAL(*origIt, *readIt); } - - return EXIT_SUCCESS; } -} +} // namespace mpi_file_io_test -int main( int argc, char* argv[] ) +int main(int argc, char* argv[]) { - return mpi_file_io_test::main( argc, argv ); + walberla::debug::enterTestMode(); + + walberla::mpi::Environment mpiEnv(argc, argv); + + // test with MPI_WORLD_COMM + walberla::MPIManager::instance()->useWorldComm(); + mpi_file_io_test::test(argc, argv); + + // test with Cartesian MPI communicator + // this is tested additionally because some versions of OpenMPI are known to produce segmentation faults when using + // MPI-IO with a 3D Cartesian MPI communicator; for those OpenMPI versions, serial I/O is used instead + if (walberla::MPIManager::instance()->numProcesses() == 16) + { + walberla::MPIManager::instance()->resetMPI(); + + walberla::MPIManager::instance()->createCartesianComm(walberla::uint_c(4), walberla::uint_c(2), + walberla::uint_c(2), false, false, false); + mpi_file_io_test::test(argc, argv); + } }