diff --git a/apps/tutorials/pe/01_ConfinedGas.cpp b/apps/tutorials/pe/01_ConfinedGas.cpp index 5e4bc03e8a8ae31680773fdcab2d87f4f7e89cca..c33b26d4b55b1fc40c29827685c10443a2db8ab2 100644 --- a/apps/tutorials/pe/01_ConfinedGas.cpp +++ b/apps/tutorials/pe/01_ConfinedGas.cpp @@ -45,30 +45,30 @@ int main( int argc, char ** argv ) real_t spacing = real_c(1.0); real_t radius = real_c(0.4); real_t vMax = real_c(1.0); - int simulationSteps = 200; + int simulationSteps = 10; real_t dt = real_c(0.01); //! [Parameters] - WALBERLA_LOG_INFO("*** GLOBALBODYSTORAGE ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** GLOBALBODYSTORAGE ***"); //! [GlobalBodyStorage] shared_ptr<BodyStorage> globalBodyStorage = make_shared<BodyStorage>(); //! [GlobalBodyStorage] - WALBERLA_LOG_INFO("*** BLOCKFOREST ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** BLOCKFOREST ***"); // create forest //! [BlockForest] shared_ptr< BlockForest > forest = createBlockForest( AABB(0,0,0,20,20,20), // simulation domain - Vector3<uint_t>(1,1,1), // blocks in each direction + Vector3<uint_t>(2,2,2), // blocks in each direction Vector3<bool>(false, false, false) // periodicity ); //! [BlockForest] if (!forest) { - WALBERLA_LOG_INFO( "No BlockForest created ... exiting!"); + WALBERLA_LOG_INFO_ON_ROOT( "No BlockForest created ... exiting!"); return EXIT_SUCCESS; } - WALBERLA_LOG_INFO("*** STORAGEDATAHANDLING ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** STORAGEDATAHANDLING ***"); // add block data //! [StorageDataHandling] auto storageID = forest->addBlockData(createStorageDataHandling<BodyTypeTuple>(), "Storage"); @@ -78,7 +78,7 @@ int main( int argc, char ** argv ) auto fcdID = forest->addBlockData(fcd::createGenericFCDDataHandling<BodyTypeTuple, fcd::AnalyticCollideFunctor>(), "FCD"); //! [AdditionalBlockData] - WALBERLA_LOG_INFO("*** INTEGRATOR ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** INTEGRATOR ***"); //! [Integrator] cr::HCSITS cr(globalBodyStorage, forest, storageID, ccdID, fcdID); cr.setMaxIterations( 10 ); @@ -87,13 +87,13 @@ int main( int argc, char ** argv ) cr.setGlobalLinearAcceleration( Vec3(0,0,0) ); //! [Integrator] - WALBERLA_LOG_INFO("*** BodyTypeTuple ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** BodyTypeTuple ***"); // initialize body type ids //! [SetBodyTypeIDs] SetBodyTypeIDs<BodyTypeTuple>::execute(); //! [SetBodyTypeIDs] - WALBERLA_LOG_INFO("*** SETUP - START ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - START ***"); //! [Material] const real_t static_cof ( real_c(0.1) / 2 ); // Coefficient of static friction. Note: pe doubles the input coefficient of friction for material-material contacts. const real_t dynamic_cof ( static_cof ); // Coefficient of dynamic friction. Similar to static friction for low speed friction. @@ -124,26 +124,28 @@ int main( int argc, char ** argv ) if (sp != NULL) ++numParticles; } } - WALBERLA_LOG_INFO("#particles created: " << numParticles); + WALBERLA_LOG_INFO_ON_ROOT("#particles created: " << numParticles); + syncNextNeighbors<BodyTypeTuple>(*forest, storageID); //! [Gas] - WALBERLA_LOG_INFO("*** SETUP - END ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - END ***"); - WALBERLA_LOG_INFO("*** SIMULATION - START ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** SIMULATION - START ***"); //! [GameLoop] for (int i=0; i < simulationSteps; ++i) { if( i % 10 == 0 ) { - WALBERLA_LOG_DEVEL( "Timestep " << i << " / " << simulationSteps ); + WALBERLA_LOG_PROGRESS_ON_ROOT( "Timestep " << i << " / " << simulationSteps ); } cr.timestep( real_c(dt) ); + syncNextNeighbors<BodyTypeTuple>(*forest, storageID); } //! [GameLoop] - WALBERLA_LOG_INFO("*** SIMULATION - END ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** SIMULATION - END ***"); - WALBERLA_LOG_INFO("*** GETTING STATISTICAL INFORMATION ***"); + WALBERLA_LOG_INFO_ON_ROOT("*** GETTING STATISTICAL INFORMATION ***"); //! [PostProcessing] Vec3 meanVelocity(0,0,0); for (auto blockIt = forest->begin(); blockIt != forest->end(); ++blockIt) diff --git a/apps/tutorials/pe/01_ConfinedGas.dox b/apps/tutorials/pe/01_ConfinedGas.dox index 28fc0a834f5b6a70d393e94a0c989be6ab04ee1d..33cf0b9acb2b436a30964f9d6bae69fae60e1bcd 100644 --- a/apps/tutorials/pe/01_ConfinedGas.dox +++ b/apps/tutorials/pe/01_ConfinedGas.dox @@ -22,9 +22,13 @@ Next the waLBerla environment is initalized, the random number generator is seed The BlockForest is the main datastructure in the waLBerla framework. It is responsible for the domain decomposition and holds all the blocks with their data. For more information about the general design of the waLBerla framework please refer -to \ref tutorial_basics_01 and the documentation of domain_decomposition::BlockStorage. For this tutorial the number of blocks -in each direction is fixed to 1 and has to stay like that. In tutorial 2 we will talk about using more than one block and -parallelism. +to \ref tutorial_basics_01 and the documentation of domain_decomposition::BlockStorage. You can choose the number of blocks +you want to have in each direction. In a parallel simulation these blocks get assigned to different processes. You should +make sure that you always have at least as many blocks as processes. The number of processes you want your simulation to run +with is specified when you start your programm with mpiexec. + +\attention If you run a simulation with periodic boundaries you need at least three blocks in the direction of periodicity! + \snippet 01_ConfinedGas.cpp BlockForest There are two types of storages to store particle information. One is the global body storage which is responsible for very @@ -59,10 +63,15 @@ which returns a SphereID of the created sphere. This SphereID acts like a pointe If for various reasons the sphere was not created the return value is NULL. \attention Before accessing the underlying sphere you should always check for NULL! +After you have initialized all particles you should synchronize the simulation to make sure that all information is +distributed correctly. Two synchronization methods are available syncNextNeighbors() and syncShadowOwners(). + \snippet 01_ConfinedGas.cpp Gas Since the setup is finished now we can run the simulation loop. The simulation loop is as simple as: \snippet 01_ConfinedGas.cpp GameLoop +cr::ICR::timestep() evolves your simulation in time. The subsequent sychronization keeps all particles that are known to more +than one process in sync. After the simulation is finished we can collect the results. In this case we only calculate the mean velocity of all particles. The particles can be easily accessed via the LocalBodyIterator. This iterator allows us to iterate through all particles and @@ -70,7 +79,7 @@ access their properties. \snippet 01_ConfinedGas.cpp PostProcessing Congratulation! You successfully created your first rigid body simulation. -In the next tutorial we will look at possible extensions which can make your live easier as well as how to run parallel simulations. +In the next tutorial we will look at possible extensions which can make your live easier. */ diff --git a/apps/tutorials/pe/02_ConfinedGasExtended.cfg b/apps/tutorials/pe/02_ConfinedGasExtended.cfg new file mode 100644 index 0000000000000000000000000000000000000000..078bffef5e7c5e9d082e11bb32fb3af2ee63002f --- /dev/null +++ b/apps/tutorials/pe/02_ConfinedGasExtended.cfg @@ -0,0 +1,9 @@ + +ConfinedGasExtended +{ + simulationCorner < 0, 0, 0 >; + simulationDomain < 20, 20, 20 >; + blocks < 2, 2, 2 >; + isPeriodic < 0, 0, 0 >; + +} diff --git a/apps/tutorials/pe/02_ConfinedGasExtended.cpp b/apps/tutorials/pe/02_ConfinedGasExtended.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c58d14c2f13d7ce22f84bd83386ae5791ffc26b8 --- /dev/null +++ b/apps/tutorials/pe/02_ConfinedGasExtended.cpp @@ -0,0 +1,276 @@ +//====================================================================================================================== +// +// This file is part of waLBerla. waLBerla is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. +// +//! \file 02_ConfinedGasExtended.cpp +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +#include <pe/basic.h> +#include <pe/statistics/BodyStatistics.h> +#include <pe/vtk/SphereVtkOutput.h> + +#include <core/Abort.h> +#include <core/Environment.h> +#include <core/grid_generator/HCPIterator.h> +#include <core/grid_generator/SCIterator.h> +#include <core/logging/Logging.h> +#include <core/timing/TimingTree.h> +#include <core/waLBerlaBuildInfo.h> +#include <postprocessing/sqlite/SQLite.h> +#include <vtk/VTKOutput.h> + +using namespace walberla; +using namespace walberla::pe; +using namespace walberla::timing; + +typedef boost::tuple<Sphere, Plane> BodyTuple ; + +int main( int argc, char ** argv ) +{ + Environment env(argc, argv); + + logging::Logging::instance()->setStreamLogLevel(logging::Logging::INFO); + logging::Logging::instance()->setFileLogLevel(logging::Logging::INFO); + + WALBERLA_LOG_INFO_ON_ROOT( "config file: " << argv[1] ) + WALBERLA_LOG_INFO_ON_ROOT( "waLBerla Revision: " << WALBERLA_GIT_SHA1 ); + + math::seedRandomGenerator( static_cast<unsigned int>(1337 * mpi::MPIManager::instance()->worldRank()) ); + + WcTimingTree tt; + + WALBERLA_LOG_INFO_ON_ROOT("*** READING COMMANDLINE ARGUMENTS ***"); + bool syncShadowOwners = false; + + for( int i = 1; i < argc; ++i ) + { + if( std::strcmp( argv[i], "--syncShadowOwners" ) == 0 ) syncShadowOwners = true; + } + + WALBERLA_LOG_INFO_ON_ROOT("*** READING CONFIG FILE ***"); + auto cfg = env.config(); + if (cfg == NULL) WALBERLA_ABORT("No config specified!"); + const Config::BlockHandle mainConf = cfg->getBlock( "ConfinedGasExtended" ); + + const std::string sqlFile = mainConf.getParameter< std::string >( "sqlFile", "ConfinedGas.sqlite" ); + + //! [SQLProperties] + std::map< std::string, walberla::int64_t > integerProperties; + std::map< std::string, double > realProperties; + std::map< std::string, std::string > stringProperties; + //! [SQLProperties] + + stringProperties["walberla_git"] = WALBERLA_GIT_SHA1; + + real_t spacing = mainConf.getParameter<real_t>("spacing", real_c(1.0) ); + WALBERLA_LOG_INFO_ON_ROOT("spacing: " << spacing); + realProperties["spacing"] = double_c(spacing); + + real_t radius = mainConf.getParameter<real_t>("radius", real_c(0.4) ); + WALBERLA_LOG_INFO_ON_ROOT("radius: " << radius); + realProperties["radius"] = double_c(radius); + + real_t vMax = mainConf.getParameter<real_t>("vMax", real_c(1.0) ); + WALBERLA_LOG_INFO_ON_ROOT("vMax: " << vMax); + realProperties["vMax"] = vMax; + + int warmupSteps = mainConf.getParameter<int>("warmupSteps", 0 ); + WALBERLA_LOG_INFO_ON_ROOT("warmupSteps: " << warmupSteps); + integerProperties["warmupSteps"] = warmupSteps; + + int simulationSteps = mainConf.getParameter<int>("simulationSteps", 10 ); + WALBERLA_LOG_INFO_ON_ROOT("simulationSteps: " << simulationSteps); + integerProperties["simulationSteps"] = simulationSteps; + + real_t dt = mainConf.getParameter<real_t>("dt", real_c(0.01) ); + WALBERLA_LOG_INFO_ON_ROOT("dt: " << dt); + realProperties["dt"] = dt; + + const int visSpacing = mainConf.getParameter<int>("visSpacing", 1000 ); + WALBERLA_LOG_INFO_ON_ROOT("visSpacing: " << visSpacing); + const std::string path = mainConf.getParameter<std::string>("path", "vtk_out" ); + WALBERLA_LOG_INFO_ON_ROOT("path: " << path); + + WALBERLA_LOG_INFO_ON_ROOT("syncShadowOwners: " << syncShadowOwners); + integerProperties["syncShadowOwners"] = syncShadowOwners; + + WALBERLA_LOG_INFO_ON_ROOT("*** GLOBALBODYSTORAGE ***"); + shared_ptr<BodyStorage> globalBodyStorage = make_shared<BodyStorage>(); + + WALBERLA_LOG_INFO_ON_ROOT("*** BLOCKFOREST ***"); + // create forest + shared_ptr< BlockForest > forest = createBlockForestFromConfig( mainConf ); + if (!forest) + { + WALBERLA_LOG_INFO_ON_ROOT( "No BlockForest created ... exiting!"); + return EXIT_SUCCESS; + } + + WALBERLA_LOG_INFO_ON_ROOT("simulationDomain: " << forest->getDomain()); + integerProperties["sim_x"] = int64_c(forest->getDomain().maxCorner()[0]); + integerProperties["sim_y"] = int64_c(forest->getDomain().maxCorner()[1]); + integerProperties["sim_z"] = int64_c(forest->getDomain().maxCorner()[2]); + + WALBERLA_LOG_INFO_ON_ROOT("blocks: " << Vector3<uint_t>(forest->getXSize(), forest->getYSize(), forest->getZSize()) ); + integerProperties["blocks_x"] = int64_c(forest->getXSize()); + integerProperties["blocks_y"] = int64_c(forest->getYSize()); + integerProperties["blocks_z"] = int64_c(forest->getZSize()); + + WALBERLA_LOG_INFO_ON_ROOT("*** BODYTUPLE ***"); + // initialize body type ids + SetBodyTypeIDs<BodyTuple>::execute(); + + WALBERLA_LOG_INFO_ON_ROOT("*** STORAGEDATAHANDLING ***"); + // add block data + auto storageID = forest->addBlockData(createStorageDataHandling<BodyTuple>(), "Storage"); + auto ccdID = forest->addBlockData(ccd::createHashGridsDataHandling( globalBodyStorage, storageID ), "CCD"); + auto fcdID = forest->addBlockData(fcd::createGenericFCDDataHandling<BodyTuple, fcd::AnalyticCollideFunctor>(), "FCD"); + + WALBERLA_LOG_INFO_ON_ROOT("*** INTEGRATOR ***"); + cr::HCSITS cr(globalBodyStorage, forest, storageID, ccdID, fcdID); + cr.setMaxIterations( 10 ); + cr.setRelaxationModel( cr::HardContactSemiImplicitTimesteppingSolvers::ApproximateInelasticCoulombContactByDecoupling ); + cr.setRelaxationParameter( real_t(0.7) ); + cr.setGlobalLinearAcceleration( Vec3(0,0,0) ); + + WALBERLA_LOG_INFO_ON_ROOT("*** SYNCCALL ***"); + boost::function<void(void)> syncCall; + if (!syncShadowOwners) + { + syncCall = boost::bind( pe::syncNextNeighbors<BodyTuple>, boost::ref(*forest), storageID, &tt, real_c(0.0), false ); + } else + { + syncCall = boost::bind( pe::syncShadowOwners<BodyTuple>, boost::ref(*forest), storageID, &tt, real_c(0.0), false ); + } + + //! [Bind Sync Call] + boost::function<void(void)> syncCallWithoutTT; + if (!syncShadowOwners) + { + syncCallWithoutTT = boost::bind( pe::syncNextNeighbors<BodyTuple>, boost::ref(*forest), storageID, static_cast<WcTimingTree*>(NULL), real_c(0.0), false ); + } else + { + syncCallWithoutTT = boost::bind( pe::syncShadowOwners<BodyTuple>, boost::ref(*forest), storageID, static_cast<WcTimingTree*>(NULL), real_c(0.0), false ); + } + //! [Bind Sync Call] + + WALBERLA_LOG_INFO_ON_ROOT("*** VTK ***"); + //! [VTK Domain Output] + auto vtkDomainOutput = vtk::createVTKOutput_DomainDecomposition( forest, "domain_decomposition", 1, "vtk_out", "simulation_step" ); + //! [VTK Domain Output] + //! [VTK Sphere Output] + auto vtkSphereHelper = make_shared<SphereVtkOutput>(storageID, *forest) ; + auto vtkSphereOutput = vtk::createVTKOutput_PointData(vtkSphereHelper, "Bodies", 1, "vtk_out", "simulation_step", false, false); + //! [VTK Sphere Output] + + WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - START ***"); + const real_t static_cof ( real_c(0.1) / 2 ); // Coefficient of static friction. Note: pe doubles the input coefficient of friction for material-material contacts. + const real_t dynamic_cof ( static_cof ); // Coefficient of dynamic friction. Similar to static friction for low speed friction. + MaterialID material = createMaterial( "granular", real_t( 1.0 ), 0, static_cof, dynamic_cof, real_t( 0.5 ), 1, 1, 0, 0 ); + + auto simulationDomain = forest->getDomain(); + auto generationDomain = simulationDomain; // simulationDomain.getExtended(-real_c(0.5) * spacing); + createPlane(*globalBodyStorage, 0, Vec3(1,0,0), simulationDomain.minCorner(), material ); + createPlane(*globalBodyStorage, 0, Vec3(-1,0,0), simulationDomain.maxCorner(), material ); + createPlane(*globalBodyStorage, 0, Vec3(0,1,0), simulationDomain.minCorner(), material ); + createPlane(*globalBodyStorage, 0, Vec3(0,-1,0), simulationDomain.maxCorner(), material ); + createPlane(*globalBodyStorage, 0, Vec3(0,0,1), simulationDomain.minCorner(), material ); + createPlane(*globalBodyStorage, 0, Vec3(0,0,-1), simulationDomain.maxCorner(), material ); + + uint_t numParticles = uint_c(0); + + for (auto blkIt = forest->begin(); blkIt != forest->end(); ++blkIt) + { + IBlock & currentBlock = *blkIt; + for (auto it = grid_generator::SCIterator(currentBlock.getAABB().getIntersection(generationDomain), Vector3<real_t>(spacing, spacing, spacing) * real_c(0.5), spacing); it != grid_generator::SCIterator(); ++it) + { + SphereID sp = pe::createSphere( *globalBodyStorage, *forest, storageID, 0, *it, radius, material); + Vec3 rndVel(math::realRandom<real_t>(-vMax, vMax), math::realRandom<real_t>(-vMax, vMax), math::realRandom<real_t>(-vMax, vMax)); + if (sp != NULL) sp->setLinearVel(rndVel); + if (sp != NULL) ++numParticles; + } + } + mpi::reduceInplace(numParticles, mpi::SUM); + WALBERLA_LOG_INFO_ON_ROOT("#particles created: " << numParticles); + + WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - END ***"); + + // synchronize particles + //! [TT Example] + //WcTimingTree tt; + + tt.start("Initial Sync"); + syncCallWithoutTT(); + syncCallWithoutTT(); + tt.stop("Initial Sync"); + //! [TT Example] + + WALBERLA_LOG_INFO_ON_ROOT("*** SIMULATION - START ***"); + WcTimingPool tp; + tt.start("Simulation Loop"); + tp["Total"].start(); + for (int i=0; i < simulationSteps; ++i) + { + if( i % 200 == 0 ) + { + WALBERLA_LOG_DEVEL_ON_ROOT( "Timestep " << i << " / " << simulationSteps ); + } + + tp["Solver"].start(); + cr.timestep( real_c(dt) ); + tp["Solver"].end(); + tp["Sync"].start(); + syncCall(); + tp["Sync"].end(); + + if( i % visSpacing == 0 ) + { + //! [VTK Output] + vtkDomainOutput->write( ); + vtkSphereOutput->write( ); + //! [VTK Output] + } + } + tp["Total"].end(); + tt.stop("Simulation Loop"); + WALBERLA_LOG_INFO_ON_ROOT("*** SIMULATION - END ***"); + + BodyStatistics bodyStats( forest, storageID ); + bodyStats(); + WALBERLA_LOG_INFO_ON_ROOT( bodyStats ); + integerProperties["numBodies"] = int64_c(bodyStats.numBodies()); + integerProperties["numShadowCopies"] = int64_c(bodyStats.numShadowCopies()); + + //! [TT Log] + auto temp = tt.getReduced( ); + WALBERLA_ROOT_SECTION() + { + std::cout << temp; + } + //! [TT Log] + + auto tpReduced = tp.getReduced(); + //! [SQL Save] + WALBERLA_ROOT_SECTION() + { + auto runId = postprocessing::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); + postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *tpReduced, "Timeloop" ); + postprocessing::storeTimingTreeInSqliteDB( sqlFile, runId, tt, "TimingTree" ); + } + //! [SQL Save] + + return EXIT_SUCCESS; +} diff --git a/apps/tutorials/pe/02_ConfinedGasExtended.dox b/apps/tutorials/pe/02_ConfinedGasExtended.dox new file mode 100644 index 0000000000000000000000000000000000000000..45c6cc4c9ca90be9cf6bb29f1026ab9df8640be2 --- /dev/null +++ b/apps/tutorials/pe/02_ConfinedGasExtended.dox @@ -0,0 +1,97 @@ +namespace walberla { +namespace pe { + +/** +\page tutorial_pe_02 Tutorial - Useful Features + +This tutorial will introduce some useful features of the waLBerla framework which can make your live easier. + +\section tutorial_pe_02_checkpointing Checkpointing +You can checkpoint the current state of your rigid body dynamics simulation at any point to restore it afterwards. +First you have to store the current domain partitioning using blockforest::BlockForest::saveToFile(). +\snippet SerializeDeserialize.cpp Dump Blockforest +Then you have to store the current simulation data using domain_decomposition::BlockStorage::saveBlockData(). +\snippet SerializeDeserialize.cpp Save Simulation Data +This will store all non global rigid bodies to the file system. + +To load everything again you start by creating the blockforest::BlockForest. This time you will use a different +constructor. +\snippet SerializeDeserialize.cpp Load Blockforest +Instead of initializing the Storage BlockDatum like you normally would +\snippet SerializeDeserialize.cpp Init Storage +you have to use domain_decomposition::BlockStorage::loadBlockData() +\snippet SerializeDeserialize.cpp Load Storage + +Unfortunately due to a misorder in the loading scheme you have to reload your coarse collision detection. +\snippet SerializeDeserialize.cpp Reload CCD +Hopefully this gets fixed in the future. ;) + +\attention This method does not save global bodies nor solver settings. You have to take care to restore these +settings on your own. + +A fully working example can be found in the SerializeDeserialize.cpp test of the pe module. + +\section tutorial_pe_02_vtk VTK Output +For VTK Output you have to create vtk::VTKOutput objects. +To output the domain partitioning use vtk::createVTKOutput_DomainDecomposition. +\snippet 02_ConfinedGasExtended.cpp VTK Domain Output +To output all sphere particles use vtk::createVTKOutput_PointData in conjunction with SphereVtkOutput: +\snippet 02_ConfinedGasExtended.cpp VTK Sphere Output +Currently only spheres are supported for VTK output but you can easily write your own SphereVtkOutput +and adapt it to the body you like. + +To actually write something to disc call vtk::VTKOutput::write(): +\snippet 02_ConfinedGasExtended.cpp VTK Output +You can call this every time step if you want. The files will be automatically numbered so that ParaView can +generate an animation. + +\section tutorial_pe_02_config Loading from Config +You can specify a config file as the first command line parameter. To access it you can use the +Environment::config() function. You can access subblocks of the config with config::Config::getBlock(). +\snippet LoadFromConfig.cpp Load Config +To get values from the config call config::Config::getParameter(): +\snippet LoadFromConfig.cpp Config Get Parameter +Certain task already have predefined loading functions. You can for example directly create a BlockForest +from the config file. +\snippet LoadFromConfig.cpp Config BlockForest +The corresponding block in the config file looks like: +\code +simulationCorner < -15, -15, 0 >; +simulationDomain < 12, 23, 34 >; +blocks < 3, 4, 5 >; +isPeriodic < 0, 1, 0 >; +\endcode +Also the HardContact solver can be configured directly from the config file: +\snippet LoadFromConfig.cpp Config HCSITS +The config file looks like: +\code +HCSITSmaxIterations 123; +HCSITSRelaxationParameter 0.123; +HCSITSErrorReductionParameter 0.123; +HCSITSRelaxationModelStr ApproximateInelasticCoulombContactByDecoupling; +globalLinearAcceleration < 1, -2, 3 >; +\endcode + +\section tutorial_pe_02_timing Timing +To get additional information where you application spends its time you can use the WcTimingTree. +It will give you a hirarchical view of the time used. +Usage example: +\snippet 02_ConfinedGasExtended.cpp TT Example +Before you output the information you should collect all the information from all the processes if you are running +in parallel. +\snippet 02_ConfinedGasExtended.cpp TT Log +Many build-in functions like solver or synchronization methods come with an additional parameter where you can +specify your timing tree. They will then include detailed information in your timing tree. + +\section tutorial_pe_02_sqlite SQLite Output +waLBerla also supports SQLite database for simulation data output. This can come in handy in parallel simulations +as well as in data analysis. To store information in a SQLite database you have to fill three property maps +depending on the type of information you want to store. +\snippet 02_ConfinedGasExtended.cpp SQLProperties +You can then dump the information to disc. timing::TimingPool and timing::TimingTree already have predefined save +functions so you do not have to extract all the information yourself and save it in the property array. +\snippet 02_ConfinedGasExtended.cpp SQL Save +*/ + +} +} diff --git a/apps/tutorials/pe/CMakeLists.txt b/apps/tutorials/pe/CMakeLists.txt index 1a236f44933c947cada7ee5a84c4a8e2a698063b..a41407eccd3e2306c1dda30e9bf6b65d3e534a29 100644 --- a/apps/tutorials/pe/CMakeLists.txt +++ b/apps/tutorials/pe/CMakeLists.txt @@ -1,5 +1,14 @@ waLBerla_link_files_to_builddir( *.cfg ) -waLBerla_add_executable ( NAME 01_Tutorial_ConfinedGas +waLBerla_add_executable ( NAME 01_Tutorial_ConfinedGas FILES 01_ConfinedGas.cpp DEPENDS blockforest core pe ) + +waLBerla_add_executable ( NAME 02_Tutorial_ConfinedGasExtended + FILES 02_ConfinedGasExtended.cpp + DEPENDS blockforest core pe postprocessing vtk ) + +waLBerla_execute_test( NO_MODULE_LABEL NAME 01_Tutorial_ConfinedGas PROCESSES 8 ) +waLBerla_execute_test( NO_MODULE_LABEL NAME 02_Tutorial_ConfinedGasExtended + COMMAND $<TARGET_FILE:02_Tutorial_ConfinedGasExtended> 02_ConfinedGasExtended.cfg + PROCESSES 8 ) diff --git a/doc/Mainpage.dox b/doc/Mainpage.dox index e89de056aaae2f09261def5c76c827e6eea398ca..45cdae39e2bbef607d0bdc90bee56a23ebec60db 100644 --- a/doc/Mainpage.dox +++ b/doc/Mainpage.dox @@ -23,6 +23,7 @@ all the basic data strcutures and concepts of the framework. \subsection pe Rigid Body Dynamics (pe) - \ref tutorial_pe_01 \n +- \ref tutorial_pe_02 \n \subsection pdes Solving Partial Differential Equations diff --git a/src/pe/pe_module.dox b/src/pe/pe_module.dox index 65f330c7052fe3e6276be5e44e343283f42a3fb6..0982c2dbbd087f09e3518505c699a789eaacedb1 100644 --- a/src/pe/pe_module.dox +++ b/src/pe/pe_module.dox @@ -25,7 +25,7 @@ Please check the documentation of each individual function for more information. \subsection FCD What fine collision detection functions are available? Please check the documentation of each individual function for more information. - - fcd::SimpleFCD create via createSimpleFCDDataHandling() (analytic contact detection) + - fcd::GenericFCD create via createGenericFCDDataHandling() (for analytic contact detection use fcd::AnalyticCollideFunctor) \subsection CRSolvers What collision resolution solvers are available? Please check the documentation of each individual solver for more information. @@ -71,6 +71,10 @@ of every collision partner by retrieving the material of Contact::getBody1(), Co you should call RigidBody::getTopSuperBody() and then RigidBody::getMass() (RigidBody::getInertia()) using the returned rigid body. +\subsection SyncCalls How can I dynamically switch between sync calls? +You can bind the function to a boost function and call this one. +\snippet 02_ConfinedGasExtended.cpp Bind Sync Call + \section CommonFunctions Important Classes and Functions **/ diff --git a/tests/pe/LoadFromConfig.cpp b/tests/pe/LoadFromConfig.cpp index 7b8fdae29a7d2e02158b51e7899b99e5607b3af1..69ccad26cceb2e37ac1bd51801a098dde157dfb8 100644 --- a/tests/pe/LoadFromConfig.cpp +++ b/tests/pe/LoadFromConfig.cpp @@ -38,12 +38,23 @@ int main( int argc, char ** argv ) walberla::debug::enterTestMode(); Environment env(argc, argv); - const Config::BlockHandle configBlock = env.config()->getBlock( "LoadFromConfig" ); + //! [Load Config] + auto cfg = env.config(); + if (cfg == NULL) WALBERLA_ABORT("No config specified!"); + const Config::BlockHandle configBlock = cfg->getBlock( "LoadFromConfig" ); + //! [Load Config] + + //! [Config Get Parameter] + real_t radius = configBlock.getParameter<real_t>("radius", real_c(0.4) ); + //! [Config Get Parameter] + WALBERLA_UNUSED(radius); shared_ptr<BodyStorage> globalBodyStorage = make_shared<BodyStorage>(); // create blocks + //! [Config BlockForest] shared_ptr<BlockForest> forest = createBlockForestFromConfig( configBlock ); + //! [Config BlockForest] WALBERLA_CHECK_EQUAL( forest->getXSize(), 3 ); WALBERLA_CHECK_EQUAL( forest->getYSize(), 4 ); WALBERLA_CHECK_EQUAL( forest->getZSize(), 5 ); @@ -53,9 +64,11 @@ int main( int argc, char ** argv ) WALBERLA_CHECK_FLOAT_EQUAL( forest->getDomain().minCorner(), Vec3(-15, -15, 0) ); WALBERLA_CHECK_FLOAT_EQUAL( forest->getDomain().maxCorner(), Vec3(-3, 8, 34) ); + //! [Config HCSITS] BlockDataID blockDataID; cr::HCSITS hcsits( globalBodyStorage, forest, blockDataID, blockDataID, blockDataID); configure(configBlock, hcsits); + //! [Config HCSITS] WALBERLA_CHECK_EQUAL( hcsits.getRelaxationModel(), cr::HCSITS::RelaxationModel::ApproximateInelasticCoulombContactByDecoupling ); WALBERLA_CHECK_EQUAL( hcsits.getMaxIterations(), 123 ); WALBERLA_CHECK_FLOAT_EQUAL( hcsits.getRelaxationParameter(), real_t(0.123) ); diff --git a/tests/pe/SerializeDeserialize.cpp b/tests/pe/SerializeDeserialize.cpp index 5fc706632250d2b158a5f53f16dc0900896d567c..47e30000a79045a29094abbf62fa3c667538b41e 100644 --- a/tests/pe/SerializeDeserialize.cpp +++ b/tests/pe/SerializeDeserialize.cpp @@ -13,7 +13,7 @@ // 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 Synchronization.cpp +//! \file SerializeDeserialize.cpp //! \author Sebastian Eibl <sebastian.eibl@fau.de> // //====================================================================================================================== @@ -46,13 +46,17 @@ void createDump() shared_ptr<BodyStorage> globalBodyStorage = make_shared<BodyStorage>(); // create blocks - auto forest = createBlockForest( math::AABB(0,0,0,60,60,60), - Vector3<uint_t>(2,2,2), // number of blocks - Vector3<bool>(false, false, false)); // periodicity + //! [Dump Blockforest] + auto forest = createBlockForest( math::AABB(0,0,0,60,60,60), + Vector3<uint_t>(2,2,2), // number of blocks + Vector3<bool>(false, false, false)); // periodicity forest->saveToFile("SerializeDeserialize.sbf"); + //! [Dump Blockforest] - auto storageID = forest->addBlockData(createStorageDataHandling<BodyTuple>(), "Storage"); - auto ccdID = forest->addBlockData(ccd::createHashGridsDataHandling( globalBodyStorage, storageID ), "CCD"); + //! [Init Storage] + auto storageID = forest->addBlockData(createStorageDataHandling<BodyTuple>(), "Storage"); + //! [Init Storage] + auto ccdID = forest->addBlockData(ccd::createHashGridsDataHandling( globalBodyStorage, storageID ), "CCD"); for (auto it = SCIterator(forest->getDomain(), Vec3(-1,-1,-1), 3); it != SCIterator(); ++it) { @@ -60,7 +64,9 @@ void createDump() } WALBERLA_LOG_DEVEL_ON_ROOT("dumping body storage"); + //! [Save Simulation Data] forest->saveBlockData("SerializeDeserialize.dump", storageID); + //! [Save Simulation Data] for (auto blockIt = forest->begin(); blockIt != forest->end(); ++blockIt) { @@ -80,16 +86,22 @@ void checkDump() shared_ptr<BodyStorage> globalBodyStorage = make_shared<BodyStorage>(); // create blocks - auto forest = shared_ptr< BlockForest >( new BlockForest( uint_c( MPIManager::instance()->rank() ), "SerializeDeserialize.sbf", true, false ) ); + //! [Load Blockforest] + auto forest = make_shared< BlockForest >( uint_c( MPIManager::instance()->rank() ), "SerializeDeserialize.sbf", true, false ); + //! [Load Blockforest] + //! [Load Storage] auto storageID = forest->loadBlockData("SerializeDeserialize.dump", createStorageDataHandling<BodyTuple>(), "Storage"); + //! [Load Storage] auto ccdID = forest->addBlockData(ccd::createHashGridsDataHandling( globalBodyStorage, storageID ), "CCD"); + //! [Reload CCD] for (auto blockIt = forest->begin(); blockIt != forest->end(); ++blockIt) { ccd::ICCD* ccd = blockIt->getData< ccd::ICCD >( ccdID ); ccd->reloadBodies(); } + //! [Reload CCD] for (auto blockIt = forest->begin(); blockIt != forest->end(); ++blockIt) {