diff --git a/.clang-tidy b/.clang-tidy index 162294555c718c3912376188b34fa1fe3287c9ad..2c1bdff4123ca8349e16fedbd87d80064e1dfcd2 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,68 +1,54 @@ --- -Checks: '-*,bugprone-*,-bugprone-exception-escape,misc-*,-misc-misplaced-const,modernize-*,-modernize-use-auto,-modernize-loop-convert,-modernize-pass-by-value,-modernize-raw-string-literal,-modernize-use-using,-modernize-avoid-bind,-modernize-return-braced-init-list,-modernize-use-transparent-functors,-modernize-redundant-void-arg,performance-*' +Checks: ' + +-*, + +boost-*, + +bugprone-*, +-bugprone-exception-escape, + +misc-*, +-misc-misplaced-const, +-misc-non-private-member-variables-in-classes, + +modernize-*, +-modernize-use-auto, +-modernize-loop-convert, +-modernize-pass-by-value, +-modernize-raw-string-literal, +-modernize-use-using, +-modernize-avoid-bind, +-modernize-return-braced-init-list, +-modernize-use-transparent-functors, +-modernize-redundant-void-arg, +-modernize-avoid-c-arrays, + +mpi-*, + +openmp-*, + +performance-*, + +portability-*, + +readability-const-return-type, +readability-container-size-empty, +readability-delete-null-pointer, +readability-deleted-default, +readability-misleading-indentation, +readability-misplaced-array-index, +readability-redundant-control-flow, +readability-redundant-function-ptr-dereference, +readability-redundant-smartptr-get, +readability-redundant-string-cstr, +readability-static-accessed-through-instance, +readability-string-compare, +readability-uniqueptr-delete-release + +' WarningsAsErrors: '*' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false -FormatStyle: none -User: si11fita -CheckOptions: - - key: modernize-loop-convert.MaxCopySize - value: '16' - - key: modernize-loop-convert.MinConfidence - value: reasonable - - key: modernize-loop-convert.NamingStyle - value: CamelCase - - key: modernize-make-shared.IgnoreMacros - value: '1' - - key: modernize-make-shared.IncludeStyle - value: '0' - - key: modernize-make-shared.MakeSmartPtrFunction - value: 'std::make_shared' - - key: modernize-make-shared.MakeSmartPtrFunctionHeader - value: memory - - key: modernize-make-unique.IgnoreMacros - value: '1' - - key: modernize-make-unique.IncludeStyle - value: '0' - - key: modernize-make-unique.MakeSmartPtrFunction - value: 'std::make_unique' - - key: modernize-make-unique.MakeSmartPtrFunctionHeader - value: memory - - key: modernize-pass-by-value.IncludeStyle - value: llvm - - key: modernize-pass-by-value.ValuesOnly - value: '0' - - key: modernize-raw-string-literal.ReplaceShorterLiterals - value: '0' - - key: modernize-replace-auto-ptr.IncludeStyle - value: llvm - - key: modernize-replace-random-shuffle.IncludeStyle - value: llvm - - key: modernize-use-auto.RemoveStars - value: '0' - - key: modernize-use-default-member-init.IgnoreMacros - value: '1' - - key: modernize-use-default-member-init.UseAssignment - value: '0' - - key: modernize-use-emplace.ContainersWithPushBack - value: '::std::vector;::std::list;::std::deque' - - key: modernize-use-emplace.SmartPointers - value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' - - key: modernize-use-emplace.TupleMakeFunctions - value: '::std::make_pair;::std::make_tuple' - - key: modernize-use-emplace.TupleTypes - value: '::std::pair;::std::tuple' - - key: modernize-use-equals-default.IgnoreMacros - value: '1' - - key: modernize-use-noexcept.ReplacementString - value: '' - - key: modernize-use-noexcept.UseNoexceptFalse - value: '1' - - key: modernize-use-nullptr.NullMacros - value: 'NULL' - - key: modernize-use-transparent-functors.SafeMode - value: '0' - - key: modernize-use-using.IgnoreMacros - value: '1' ... diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e16e20a2378830a2a792c94db732fda93509120f..7c428c1dbf071d034dc1a633512d96ac529d19c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1399,7 +1399,7 @@ doc: ############################################################################### clang-tidy: - image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:7.0 + image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:8.0 script: - $CXX --version - clang-tidy -version @@ -1877,4 +1877,4 @@ benchmark_gcc8: benchmark_clang8: <<: *benchmark_definition - image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:8.0 \ No newline at end of file + image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:8.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 65157e23f67017e850658c2a7679834e05189719..6de20290cdb6eed443d70b9fe3a06974bf91dd7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,11 +95,13 @@ option ( WALBERLA_NO_OUTDATED_FEATURES "Show warning/errors when outdated f "(i.e. features that will be deprecated) are used" ) # Profile guided optimization -option ( WALBERLA_PROFILE_GENERATE "Generates Profile for Optimization" ) -option ( WALBERLA_PROFILE_USE "Uses Profile to optimize" ) +option ( WALBERLA_PROFILE_GENERATE "Generates Profile for Optimization" ) +option ( WALBERLA_PROFILE_USE "Uses Profile to optimize" ) # Compiler Optimization -option ( WALBERLA_OPTIMIZE_FOR_LOCALHOST "Enable compiler optimizations spcific to localhost" ) +option ( WALBERLA_OPTIMIZE_FOR_LOCALHOST "Enable compiler optimizations spcific to localhost" ) + +option ( WALBERLA_LOG_SKIPPED "Log skipped cmake targets" ON ) # Installation Directory set ( CMAKE_INSTALL_PREFIX /usr/local/waLBerla CACHE STRING "The default installation directory." ) diff --git a/apps/benchmarks/CouetteFlow/CMakeLists.txt b/apps/benchmarks/CouetteFlow/CMakeLists.txt index 90057e86e8286b869d6a25f72191416c4720128f..c4cb893e0e140e226ab4e75486a5fe36c714384f 100644 --- a/apps/benchmarks/CouetteFlow/CMakeLists.txt +++ b/apps/benchmarks/CouetteFlow/CMakeLists.txt @@ -1,7 +1,7 @@ waLBerla_link_files_to_builddir( "*.dat" ) -waLBerla_add_executable( NAME CouetteFlow DEPENDS blockforest boundary core field lbm postprocessing stencil timeloop vtk ) +waLBerla_add_executable( NAME CouetteFlow DEPENDS blockforest boundary core field lbm postprocessing stencil timeloop vtk sqlite ) ############## # Some tests # diff --git a/apps/benchmarks/CouetteFlow/CouetteFlow.cpp b/apps/benchmarks/CouetteFlow/CouetteFlow.cpp index 9ce1da4a306fdfc381026928ce534a14efd17ec3..4bedb57423a9e7f3f9e0267b05ec3d3f4dbf7d7b 100644 --- a/apps/benchmarks/CouetteFlow/CouetteFlow.cpp +++ b/apps/benchmarks/CouetteFlow/CouetteFlow.cpp @@ -79,7 +79,7 @@ #include "lbm/vtk/NonEquilibrium.h" #include "lbm/vtk/Velocity.h" -#include "postprocessing/sqlite/SQLite.h" +#include "sqlite/SQLite.h" #include "stencil/D3Q15.h" #include "stencil/D3Q19.h" @@ -871,10 +871,10 @@ void run( const shared_ptr< Config > & config, const LatticeModel_T & latticeMod realProperties[ "simulationProgress" ] = double_c( ( outerRun + uint_t(1) ) * innerTimeSteps ) / double_c( outerTimeSteps * innerTimeSteps ); - auto runId = postprocessing::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSTiming, "RefinementTimeStep" ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSLTiming, "RefinementTimeStepLevelwise" ); + auto runId = sqlite::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSTiming, "RefinementTimeStep" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSLTiming, "RefinementTimeStepLevelwise" ); } } } diff --git a/apps/benchmarks/FieldCommunication/CMakeLists.txt b/apps/benchmarks/FieldCommunication/CMakeLists.txt index beec451f98a7fc6ca5db23ee2e3c718b58ef18ee..35a2f698a4514de3e9309cce56d977a25c470230 100644 --- a/apps/benchmarks/FieldCommunication/CMakeLists.txt +++ b/apps/benchmarks/FieldCommunication/CMakeLists.txt @@ -4,4 +4,4 @@ waLBerla_link_files_to_builddir( "*.py" ) waLBerla_add_executable ( NAME FieldCommunication - DEPENDS blockforest core domain_decomposition field postprocessing ) + DEPENDS blockforest core domain_decomposition field postprocessing sqlite ) diff --git a/apps/benchmarks/FieldCommunication/FieldCommunication.cpp b/apps/benchmarks/FieldCommunication/FieldCommunication.cpp index 2273126b32cf7f9f8404098e6518c0b93d7fad62..fabde23e097a769713564842f3993baabe0a0fab 100644 --- a/apps/benchmarks/FieldCommunication/FieldCommunication.cpp +++ b/apps/benchmarks/FieldCommunication/FieldCommunication.cpp @@ -14,7 +14,7 @@ #include "field/communication/PackInfo.h" #include "field/communication/StencilRestrictedPackInfo.h" #include "field/communication/UniformMPIDatatypeInfo.h" -#include "postprocessing/sqlite/SQLite.h" +#include "sqlite/SQLite.h" #include "python_coupling/CreateConfig.h" #include "stencil/D3Q7.h" #include "stencil/D3Q19.h" @@ -414,9 +414,9 @@ int main( int argc, char **argv ) stringProperties["buildType"] = std::string( WALBERLA_BUILD_TYPE ); stringProperties["compilerFlags"] = std::string( WALBERLA_COMPILER_FLAGS ); - auto runId = postprocessing::storeRunInSqliteDB( databaseFile, integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( databaseFile, runId, timingPool, "TimingRoot" ); - postprocessing::storeTimingPoolInSqliteDB( databaseFile, runId, *reducedTimingPool, "TimingReduced" ); + auto runId = sqlite::storeRunInSqliteDB( databaseFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( databaseFile, runId, timingPool, "TimingRoot" ); + sqlite::storeTimingPoolInSqliteDB( databaseFile, runId, *reducedTimingPool, "TimingReduced" ); } } diff --git a/apps/benchmarks/GranularGas/CMakeLists.txt b/apps/benchmarks/GranularGas/CMakeLists.txt index 7a4ef8f3e7d19535eb16b7bcfcfa3b5f610ebb72..b017add69e5080df42ac236dd5670ffbe466c020 100644 --- a/apps/benchmarks/GranularGas/CMakeLists.txt +++ b/apps/benchmarks/GranularGas/CMakeLists.txt @@ -3,24 +3,24 @@ waLBerla_link_files_to_builddir( *.py ) waLBerla_add_executable ( NAME PE_GranularGas FILES PE_GranularGas.cpp SQLProperties.cpp Parameters.cpp NodeTimings.cpp - DEPENDS blockforest core pe postprocessing ) + DEPENDS blockforest core pe postprocessing sqlite ) waLBerla_add_executable ( NAME PE_LoadBalancing FILES PE_LoadBalancing.cpp SQLProperties.cpp Parameters.cpp NodeTimings.cpp - DEPENDS blockforest core pe postprocessing ) + DEPENDS blockforest core pe postprocessing sqlite ) waLBerla_add_executable ( NAME MESA_PD_LoadBalancing FILES MESA_PD_LoadBalancing.cpp SQLProperties.cpp Parameters.cpp NodeTimings.cpp sortParticleStorage.cpp CreateParticles.cpp - DEPENDS blockforest core pe mesa_pd postprocessing vtk ) + DEPENDS blockforest core pe mesa_pd postprocessing sqlite vtk ) waLBerla_add_executable ( NAME MESA_PD_GranularGas FILES MESA_PD_GranularGas.cpp SQLProperties.cpp Parameters.cpp NodeTimings.cpp sortParticleStorage.cpp CreateParticles.cpp - DEPENDS blockforest core pe mesa_pd postprocessing vtk ) + DEPENDS blockforest core pe mesa_pd postprocessing sqlite vtk ) waLBerla_add_executable ( NAME MESA_PD_KernelBenchmark FILES MESA_PD_KernelBenchmark.cpp SQLProperties.cpp Parameters.cpp NodeTimings.cpp sortParticleStorage.cpp CreateParticles.cpp - DEPENDS blockforest core pe mesa_pd postprocessing vtk ) + DEPENDS blockforest core pe mesa_pd postprocessing sqlite vtk ) waLBerla_add_executable ( NAME MESA_PD_KernelLoadBalancing FILES MESA_PD_KernelLoadBalancing.cpp SQLProperties.cpp Parameters.cpp NodeTimings.cpp sortParticleStorage.cpp CreateParticles.cpp - DEPENDS blockforest core pe mesa_pd postprocessing vtk ) + DEPENDS blockforest core pe mesa_pd postprocessing sqlite vtk ) diff --git a/apps/benchmarks/GranularGas/MESA_PD_GranularGas.cpp b/apps/benchmarks/GranularGas/MESA_PD_GranularGas.cpp index 01dab71ea3bd53df776639564753e9c499818115..7ff78bd1ae940ad0a3b88e0ae4fdb18e12babdf0 100644 --- a/apps/benchmarks/GranularGas/MESA_PD_GranularGas.cpp +++ b/apps/benchmarks/GranularGas/MESA_PD_GranularGas.cpp @@ -60,7 +60,7 @@ #include <core/OpenMP.h> #include <core/timing/Timer.h> #include <core/waLBerlaBuildInfo.h> -#include <postprocessing/sqlite/SQLite.h> +#include <sqlite/SQLite.h> #include <vtk/VTKOutput.h> #include <functional> @@ -380,14 +380,19 @@ int main( int argc, char ** argv ) integerProperties["RPReceives"] = RPReceives; realProperties["linkedCellsVolume"] = linkedCellsVolume; integerProperties["numLinkedCells"] = int64_c(numLinkedCells); + realProperties["PUpS"] = double_c(PUpS); + realProperties["timer_min"] = timer_reduced->min(); + realProperties["timer_max"] = timer_reduced->max(); + realProperties["timer_average"] = timer_reduced->average(); + realProperties["timer_total"] = timer_reduced->total(); addBuildInfoToSQL( integerProperties, realProperties, stringProperties ); saveToSQL(params, integerProperties, realProperties, stringProperties ); addDomainPropertiesToSQL(*forest, integerProperties, realProperties, stringProperties); addSlurmPropertiesToSQL(integerProperties, realProperties, stringProperties); - runId = postprocessing::storeRunInSqliteDB( params.sqlFile, integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( params.sqlFile, runId, *tp_reduced, "Timeloop" ); + runId = sqlite::storeRunInSqliteDB( params.sqlFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( params.sqlFile, runId, *tp_reduced, "Timeloop" ); } if (params.storeNodeTimings) diff --git a/apps/benchmarks/GranularGas/MESA_PD_KernelBenchmark.cpp b/apps/benchmarks/GranularGas/MESA_PD_KernelBenchmark.cpp index 669da955b8ac17cd29216f5208ab1b65ba0cf349..4b4d24256b3e0ab6916c155bb7f25bf22cc1fad3 100644 --- a/apps/benchmarks/GranularGas/MESA_PD_KernelBenchmark.cpp +++ b/apps/benchmarks/GranularGas/MESA_PD_KernelBenchmark.cpp @@ -64,7 +64,7 @@ #include <core/timing/Timer.h> #include <core/timing/TimingPool.h> #include <core/waLBerlaBuildInfo.h> -#include <postprocessing/sqlite/SQLite.h> +#include <sqlite/SQLite.h> #include <vtk/VTKOutput.h> #include <functional> @@ -378,8 +378,8 @@ int main( int argc, char ** argv ) addDomainPropertiesToSQL(*forest, integerProperties, realProperties, stringProperties); addSlurmPropertiesToSQL(integerProperties, realProperties, stringProperties); - runId = postprocessing::storeRunInSqliteDB( params.sqlFile, integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( params.sqlFile, runId, *tp_reduced, "Timeloop" ); + runId = sqlite::storeRunInSqliteDB( params.sqlFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( params.sqlFile, runId, *tp_reduced, "Timeloop" ); } if (params.storeNodeTimings) { diff --git a/apps/benchmarks/GranularGas/NodeTimings.cpp b/apps/benchmarks/GranularGas/NodeTimings.cpp index 01b1a354b97acd3226f805ff8bd39b938a04cdd7..8336d7981753c0b5939c2b572daf5651bff20780 100644 --- a/apps/benchmarks/GranularGas/NodeTimings.cpp +++ b/apps/benchmarks/GranularGas/NodeTimings.cpp @@ -63,7 +63,7 @@ void storeNodeTimings( const uint_t runId, realProperties[v.first] = v.second.average(); } - postprocessing::storeAdditionalRunInfoInSqliteDB( runId, + sqlite::storeAdditionalRunInfoInSqliteDB( runId, dbFile, tableName, integerProperties, diff --git a/apps/benchmarks/GranularGas/NodeTimings.h b/apps/benchmarks/GranularGas/NodeTimings.h index cbe074b1b085b81822f652e805bc67c9c84f5f55..cc59fd547add479c4c8c0605dda66b698d4d79ed 100644 --- a/apps/benchmarks/GranularGas/NodeTimings.h +++ b/apps/benchmarks/GranularGas/NodeTimings.h @@ -23,7 +23,7 @@ #include <core/mpi/Gatherv.h> #include <core/logging/Logging.h> #include <core/timing/TimingPool.h> -#include <postprocessing/sqlite/SQLite.h> +#include <sqlite/SQLite.h> namespace walberla { namespace mesa_pd { diff --git a/apps/benchmarks/GranularGas/PE_GranularGas.cpp b/apps/benchmarks/GranularGas/PE_GranularGas.cpp index be3fd973c4e5dcfacbee18e2ea6ae1ca893b793d..dd75b61287aa8195495ab4d2e7160f3830674aca 100644 --- a/apps/benchmarks/GranularGas/PE_GranularGas.cpp +++ b/apps/benchmarks/GranularGas/PE_GranularGas.cpp @@ -34,7 +34,7 @@ #include <core/OpenMP.h> #include <core/timing/TimingTree.h> #include <core/waLBerlaBuildInfo.h> -#include <postprocessing/sqlite/SQLite.h> +#include <sqlite/SQLite.h> #include <vtk/VTKOutput.h> #include <functional> @@ -325,8 +325,8 @@ int main( int argc, char ** argv ) mesa_pd::addDomainPropertiesToSQL(*forest, integerProperties, realProperties, stringProperties); mesa_pd::addSlurmPropertiesToSQL(integerProperties, realProperties, stringProperties); - runId = postprocessing::storeRunInSqliteDB( params.sqlFile, integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( params.sqlFile, runId, *tp_reduced, "Timeloop" ); + runId = sqlite::storeRunInSqliteDB( params.sqlFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( params.sqlFile, runId, *tp_reduced, "Timeloop" ); } if (params.storeNodeTimings) { diff --git a/apps/benchmarks/MotionSingleHeavySphere/MotionSingleHeavySphere.cpp b/apps/benchmarks/MotionSingleHeavySphere/MotionSingleHeavySphere.cpp index 9c7b016f0d059a1f471df256c884b04055fe9e16..ddd54546f7bc8451b3353c2e1a7cf76b40961142 100644 --- a/apps/benchmarks/MotionSingleHeavySphere/MotionSingleHeavySphere.cpp +++ b/apps/benchmarks/MotionSingleHeavySphere/MotionSingleHeavySphere.cpp @@ -198,11 +198,11 @@ BoundaryHandling_T * MyBoundaryHandling::operator()( IBlock * const block, const const auto fluid = flagField->flagExists( Fluid_Flag ) ? flagField->getFlag( Fluid_Flag ) : flagField->registerFlag( Fluid_Flag ); BoundaryHandling_T * handling = new BoundaryHandling_T( "moving obstacle boundary handling", flagField, fluid, - UBB_T( "UBB", UBB_Flag, pdfField, velocity_), - Outlet_T( "Outlet", Outlet_Flag, pdfField, real_t(1) ), - MEM_BB_T ( "MEM_BB", MEM_BB_Flag, pdfField, flagField, bodyField, fluid, *storage, *block ), - MEM_CLI_T( "MEM_CLI", MEM_CLI_Flag, pdfField, flagField, bodyField, fluid, *storage, *block ), - MEM_MR_T ( "MEM_MR", MEM_MR_Flag, pdfField, flagField, bodyField, fluid, *storage, *block, pdfFieldPreCol ) ); + UBB_T( "UBB", UBB_Flag, pdfField, velocity_), + Outlet_T( "Outlet", Outlet_Flag, pdfField, real_t(1) ), + MEM_BB_T ( "MEM_BB", MEM_BB_Flag, pdfField, flagField, bodyField, fluid, *storage, *block ), + MEM_CLI_T( "MEM_CLI", MEM_CLI_Flag, pdfField, flagField, bodyField, fluid, *storage, *block ), + MEM_MR_T ( "MEM_MR", MEM_MR_Flag, pdfField, flagField, bodyField, fluid, *storage, *block, pdfFieldPreCol ) ); const auto ubb = flagField->getFlag( UBB_Flag ); const auto outlet = flagField->getFlag( Outlet_Flag ); @@ -522,7 +522,7 @@ public: VTKInfoLogger( SweepTimeloop* timeloop, const shared_ptr< StructuredBlockStorage > & blocks, const ConstBlockDataID & bodyStorageID, const std::string & baseFolder, const Vector3<real_t>& u_infty ) : - timeloop_( timeloop ), blocks_( blocks ), bodyStorageID_( bodyStorageID ), baseFolder_( baseFolder ), u_infty_( u_infty ) + timeloop_( timeloop ), blocks_( blocks ), bodyStorageID_( bodyStorageID ), baseFolder_( baseFolder ), u_infty_( u_infty ) { } void operator()() @@ -677,16 +677,44 @@ int main( int argc, char **argv ) for( int i = 1; i < argc; ++i ) { - if( std::strcmp( argv[i], "--Galileo" ) == 0 ) Galileo = real_c( std::atof( argv[++i] ) ); // targeted Galileo number - else if( std::strcmp( argv[i], "--diameter" ) == 0 ) diameter = real_c( std::atof( argv[++i] ) ); // diameter of the spheres, in cells per diameter - else if( std::strcmp( argv[i], "--noViscosityIterations" ) == 0 ) noViscosityIterations = true; // keep viscosity unchanged in initial simulation - else if( std::strcmp( argv[i], "--vtkIOInit" ) == 0 ) vtkIOInit = true; // write vtk IO for initial simulation - else if( std::strcmp( argv[i], "--longDom" ) == 0 ) longDom = true; // use a domain that is extended by the original domain length in positive and negative streamwise direction - else if( std::strcmp( argv[i], "--useMEM" ) == 0 ) // use momentum exchange coupling and the given MEM variant (BB, CLI, or MR) - { useMEM = true; memVariant = to_MEMVariant( argv[++i] ); } - else if( std::strcmp( argv[i], "--usePSM" ) == 0 ) // use partially saturated cells method and the given PSM variant (SC1W1, SC1W2, SC2W1, SC2W2, SC3W1, or SC3W2) - { usePSM = true; psmVariant = to_PSMVariant( argv[++i] ); } - else WALBERLA_ABORT("command line argument unknown: " << argv[i] ); + if( std::strcmp( argv[i], "--Galileo" ) == 0 ) + { + Galileo = real_c( std::atof( argv[++i] ) ); // targeted Galileo number + continue; + } + if( std::strcmp( argv[i], "--diameter" ) == 0 ) + { + diameter = real_c( std::atof( argv[++i] ) ); // diameter of the spheres, in cells per diameter + continue; + } + if( std::strcmp( argv[i], "--noViscosityIterations" ) == 0 ) + { + noViscosityIterations = true; // keep viscosity unchanged in initial simulation + continue; + } + if( std::strcmp( argv[i], "--vtkIOInit" ) == 0 ) + { + vtkIOInit = true; // write vtk IO for initial simulation + continue; + } + if( std::strcmp( argv[i], "--longDom" ) == 0 ) + { + longDom = true; // use a domain that is extended by the original domain length in positive and negative streamwise direction + continue; + } + if( std::strcmp( argv[i], "--useMEM" ) == 0 ) // use momentum exchange coupling and the given MEM variant (BB, CLI, or MR) + { + useMEM = true; + memVariant = to_MEMVariant( argv[++i] ); + continue; + } + if( std::strcmp( argv[i], "--usePSM" ) == 0 ) // use partially saturated cells method and the given PSM variant (SC1W1, SC1W2, SC2W1, SC2W2, SC3W1, or SC3W2) + { + usePSM = true; + psmVariant = to_PSMVariant( argv[++i] ); + continue; + } + WALBERLA_ABORT("command line argument unknown: " << argv[i] ); } if( !useMEM && !usePSM ) WALBERLA_ABORT("No coupling method chosen via command line!\nChose either \"--useMEM MEMVARIANT\" or \"--usePSM PSMVARIANT\"!"); @@ -712,24 +740,24 @@ int main( int argc, char **argv ) // values are taken from the original simulation of Uhlmann, Dusek switch( int(Galileo) ) { - case 144: - Re_target = real_t(185.08); - timestepsNonDim = real_t(100); - break; - case 178: - Re_target = real_t(243.01); - timestepsNonDim = real_t(250); - break; - case 190: - Re_target = real_t(262.71); - timestepsNonDim = real_t(250); - break; - case 250: - Re_target = real_t(365.10); - timestepsNonDim = real_t(510); - break; - default: - WALBERLA_ABORT("Galileo number is different from the usual ones (144, 178, 190, or 250). No estimate of the Reynolds number available. Add this case manually!"); + case 144: + Re_target = real_t(185.08); + timestepsNonDim = real_t(100); + break; + case 178: + Re_target = real_t(243.01); + timestepsNonDim = real_t(250); + break; + case 190: + Re_target = real_t(262.71); + timestepsNonDim = real_t(250); + break; + case 250: + Re_target = real_t(365.10); + timestepsNonDim = real_t(510); + break; + default: + WALBERLA_ABORT("Galileo number is different from the usual ones (144, 178, 190, or 250). No estimate of the Reynolds number available. Add this case manually!"); } // estimate fitting inflow velocity (diffusive scaling, viscosity is fixed) @@ -890,7 +918,7 @@ int main( int argc, char **argv ) // add boundary handling & initialize outer domain boundaries BlockDataID boundaryHandlingID = blocks->addStructuredBlockData< BoundaryHandling_T >( - MyBoundaryHandling( flagFieldID, pdfFieldID, bodyFieldID, pdfFieldPreColID, uInfty ), "boundary handling" ); + MyBoundaryHandling( flagFieldID, pdfFieldID, bodyFieldID, pdfFieldPreColID, uInfty ), "boundary handling" ); if( usePSM ){ // mapping of sphere required by PSM methods @@ -969,8 +997,8 @@ int main( int argc, char **argv ) // add LBM communication function and boundary handling sweep timeloopInit.add() - << BeforeFunction( commFunction, "LBM Communication" ) - << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" ); + << BeforeFunction( commFunction, "LBM Communication" ) + << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" ); // LBM stream collide sweep if( psmVariant == PSMVariant::SC1W1 ) @@ -1079,8 +1107,8 @@ int main( int argc, char **argv ) } WALBERLA_LOG_INFO_ON_ROOT("Initial simulation has ended.") - //evaluate the gravitational force necessary to keep the sphere at a approximately fixed position - gravity = forceEval->getForce() / ( (densityRatio - real_t(1) ) * diameter * diameter * diameter * math::pi / real_t(6) ); + //evaluate the gravitational force necessary to keep the sphere at a approximately fixed position + gravity = forceEval->getForce() / ( (densityRatio - real_t(1) ) * diameter * diameter * diameter * math::pi / real_t(6) ); GalileoSim = std::sqrt( ( densityRatio - real_t(1) ) * gravity * diameter * diameter * diameter ) / viscosity; ReynoldsSim = uIn * diameter / viscosity; u_ref = std::sqrt( std::fabs(densityRatio - real_t(1)) * gravity * diameter ); @@ -1113,9 +1141,9 @@ int main( int argc, char **argv ) // iterate all blocks with an iterator 'block' and change the collision model for( auto block = blocks->begin(); block != blocks->end(); ++block ) { - // get the field data out of the block - auto pdf = block->getData< PdfField_T > ( pdfFieldID ); - pdf->latticeModel().collisionModel().resetWithMagicNumber( newOmega, magicNumberTRT ); + // get the field data out of the block + auto pdf = block->getData< PdfField_T > ( pdfFieldID ); + pdf->latticeModel().collisionModel().resetWithMagicNumber( newOmega, magicNumberTRT ); } WALBERLA_LOG_INFO_ON_ROOT("==> Adapting viscosity:"); @@ -1157,8 +1185,8 @@ int main( int argc, char **argv ) // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment) timeloop.add() - << BeforeFunction( commFunction, "LBM Communication" ) - << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" ); + << BeforeFunction( commFunction, "LBM Communication" ) + << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" ); // LBM stream collide sweep if( psmVariant == PSMVariant::SC1W1 ) diff --git a/apps/benchmarks/NonUniformGrid/CMakeLists.txt b/apps/benchmarks/NonUniformGrid/CMakeLists.txt index 61e9e321e18584351f7e08839a94bceca92fe1a1..4e118017bfca8e2fb6dc90903870063f2c692c42 100644 --- a/apps/benchmarks/NonUniformGrid/CMakeLists.txt +++ b/apps/benchmarks/NonUniformGrid/CMakeLists.txt @@ -3,4 +3,4 @@ waLBerla_link_files_to_builddir( "*.dat" ) waLBerla_add_executable ( NAME NonUniformGridBenchmark - DEPENDS blockforest boundary core domain_decomposition field lbm postprocessing timeloop vtk ) \ No newline at end of file + DEPENDS blockforest boundary core domain_decomposition field lbm postprocessing timeloop vtk sqlite) \ No newline at end of file diff --git a/apps/benchmarks/NonUniformGrid/NonUniformGrid.cpp b/apps/benchmarks/NonUniformGrid/NonUniformGrid.cpp index f4528cbf65db1514641c07921a1e71d3fcbacc91..618f29af9f70ce8e81ea1223734622116a6449d4 100644 --- a/apps/benchmarks/NonUniformGrid/NonUniformGrid.cpp +++ b/apps/benchmarks/NonUniformGrid/NonUniformGrid.cpp @@ -64,7 +64,7 @@ #include "lbm/vtk/Density.h" #include "lbm/vtk/Velocity.h" -#include "postprocessing/sqlite/SQLite.h" +#include "sqlite/SQLite.h" #include "stencil/D3Q19.h" #include "stencil/D3Q27.h" @@ -1034,12 +1034,12 @@ void run( const shared_ptr< Config > & config, const LatticeModel_T & latticeMod } } - auto runId = postprocessing::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSTiming, "RefinementTimeStep" ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSLTiming, "RefinementTimeStepLevelwise" ); + auto runId = sqlite::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSTiming, "RefinementTimeStep" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSLTiming, "RefinementTimeStepLevelwise" ); if( blockStructureRefreshDuringMeasurement ) - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRefreshTiming, "BlockForestRefresh" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRefreshTiming, "BlockForestRefresh" ); } } } diff --git a/apps/benchmarks/PoiseuilleChannel/CMakeLists.txt b/apps/benchmarks/PoiseuilleChannel/CMakeLists.txt index 29c47a03635c1f90ce8ec33d902c0866a40c9beb..b5d3d42a7fc0cc67f07307324c198144dad97a99 100644 --- a/apps/benchmarks/PoiseuilleChannel/CMakeLists.txt +++ b/apps/benchmarks/PoiseuilleChannel/CMakeLists.txt @@ -1,7 +1,7 @@ waLBerla_link_files_to_builddir( "*.dat" ) -waLBerla_add_executable( NAME PoiseuilleChannel DEPENDS blockforest boundary core field lbm postprocessing stencil timeloop vtk ) +waLBerla_add_executable( NAME PoiseuilleChannel DEPENDS blockforest boundary core field lbm postprocessing stencil timeloop vtk sqlite) ############## # Some tests # diff --git a/apps/benchmarks/PoiseuilleChannel/PoiseuilleChannel.cpp b/apps/benchmarks/PoiseuilleChannel/PoiseuilleChannel.cpp index 1a0482f5781a272ae6159653f4e39435556f2aa7..b72efbdcbfb90a2502523e3fff73122f36f2f65c 100644 --- a/apps/benchmarks/PoiseuilleChannel/PoiseuilleChannel.cpp +++ b/apps/benchmarks/PoiseuilleChannel/PoiseuilleChannel.cpp @@ -78,7 +78,7 @@ #include "lbm/vtk/NonEquilibrium.h" #include "lbm/vtk/Velocity.h" -#include "postprocessing/sqlite/SQLite.h" +#include "sqlite/SQLite.h" #include "stencil/D3Q19.h" #include "stencil/D3Q27.h" @@ -553,12 +553,12 @@ void setFlags( shared_ptr< StructuredBlockForest > & blocks, const BlockDataID & { CurvedDeltaValueCalculation< LatticeModel_T > deltaCalculation( blocks, *block, channel ); - lbm::refinement::consistentlyForceBoundary< BoundaryHandling_T >( *blocks, dynamic_cast< blockforest::Block & >(*block.get()), + lbm::refinement::consistentlyForceBoundary< BoundaryHandling_T >( *blocks, dynamic_cast< blockforest::Block & >(*block), boundaryHandlingId, Curved_Flag, channel, deltaCalculation ); } else // staircase (1st order bounce back no-slip boundary condition) { - lbm::refinement::consistentlyForceBoundary< BoundaryHandling_T >( *blocks, dynamic_cast< blockforest::Block & >(*block.get()), + lbm::refinement::consistentlyForceBoundary< BoundaryHandling_T >( *blocks, dynamic_cast< blockforest::Block & >(*block), boundaryHandlingId, NoSlip_Flag, channel ); } @@ -995,10 +995,10 @@ void run( const shared_ptr< Config > & config, const LatticeModel_T & latticeMod realProperties[ "simulationProgress" ] = double_c( ( outerRun + uint_t(1) ) * innerTimeSteps ) / double_c( outerTimeSteps * innerTimeSteps ); - auto runId = postprocessing::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSTiming, "RefinementTimeStep" ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSLTiming, "RefinementTimeStepLevelwise" ); + auto runId = sqlite::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSTiming, "RefinementTimeStep" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSLTiming, "RefinementTimeStepLevelwise" ); } } } diff --git a/apps/benchmarks/ProbeVsExtraMessage/CMakeLists.txt b/apps/benchmarks/ProbeVsExtraMessage/CMakeLists.txt index a5cd53649d6ac5c011c026346d137dbcb862a20c..70ff75bc67f6f5992aadf87a5d6dbae75080cf58 100644 --- a/apps/benchmarks/ProbeVsExtraMessage/CMakeLists.txt +++ b/apps/benchmarks/ProbeVsExtraMessage/CMakeLists.txt @@ -4,4 +4,4 @@ waLBerla_add_executable ( NAME PackPerformance waLBerla_add_executable ( NAME ProbeVsExtraMessage FILES ProbeVsExtraMessage.cpp - DEPENDS core postprocessing stencil ) + DEPENDS core postprocessing stencil sqlite) diff --git a/apps/benchmarks/ProbeVsExtraMessage/ProbeVsExtraMessage.cpp b/apps/benchmarks/ProbeVsExtraMessage/ProbeVsExtraMessage.cpp index 960bf1e9e7efd662d867ffe5c952cdceec9ee776..bb9adfd177d72d0fd503dc62e896598353eebcfa 100644 --- a/apps/benchmarks/ProbeVsExtraMessage/ProbeVsExtraMessage.cpp +++ b/apps/benchmarks/ProbeVsExtraMessage/ProbeVsExtraMessage.cpp @@ -26,7 +26,7 @@ #include "core/mpi/BufferSystem.h" #include "core/mpi/MPIManager.h" #include "core/timing/TimingPool.h" -#include "postprocessing/sqlite/SQLite.h" +#include "sqlite/SQLite.h" #include "stencil/D3Q27.h" #include "stencil/D3Q19.h" #include "stencil/D3Q7.h" @@ -250,9 +250,9 @@ int main( int argc, char ** argv ) stringProperties["SLURM_NTASKS_PER_SOCKET"] = envToString(std::getenv( "SLURM_NTASKS_PER_SOCKET" )); stringProperties["SLURM_TASKS_PER_NODE"] = envToString(std::getenv( "SLURM_TASKS_PER_NODE" )); - auto runId = postprocessing::storeRunInSqliteDB( "ProbeVsTwoMessages.sqlite", integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( "ProbeVsTwoMessages.sqlite", runId, tp_twoMessages, "twoMessages" ); - postprocessing::storeTimingPoolInSqliteDB( "ProbeVsTwoMessages.sqlite", runId, tp_probe, "probe" ); + auto runId = sqlite::storeRunInSqliteDB( "ProbeVsTwoMessages.sqlite", integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( "ProbeVsTwoMessages.sqlite", runId, tp_twoMessages, "twoMessages" ); + sqlite::storeTimingPoolInSqliteDB( "ProbeVsTwoMessages.sqlite", runId, tp_probe, "probe" ); } return 0; diff --git a/apps/benchmarks/SchaeferTurek/CMakeLists.txt b/apps/benchmarks/SchaeferTurek/CMakeLists.txt index 67fe8d84c23f811c28f2fb99f07dc3073190e86a..88463aaf3860249071e395f74a1bd1765127ef17 100644 --- a/apps/benchmarks/SchaeferTurek/CMakeLists.txt +++ b/apps/benchmarks/SchaeferTurek/CMakeLists.txt @@ -1,6 +1,6 @@ waLBerla_link_files_to_builddir( "*.dat" ) -waLBerla_add_executable( NAME SchaeferTurek DEPENDS blockforest boundary core field lbm postprocessing stencil timeloop vtk ) +waLBerla_add_executable( NAME SchaeferTurek DEPENDS blockforest boundary core field lbm postprocessing stencil timeloop vtk sqlite ) waLBerla_execute_test( NO_MODULE_LABEL NAME SchaeferTurekTest COMMAND $<TARGET_FILE:SchaeferTurek> Test2D.dat PROCESSES 4 CONFIGURATIONS Release RelWithDbgInfo ) \ No newline at end of file diff --git a/apps/benchmarks/SchaeferTurek/SchaeferTurek.cpp b/apps/benchmarks/SchaeferTurek/SchaeferTurek.cpp index fcd8ba66932c1433b8c1ed4f7e11933982f277c3..50c3a9244da51ae3fa5dfec97d5b928ca8f56ced 100644 --- a/apps/benchmarks/SchaeferTurek/SchaeferTurek.cpp +++ b/apps/benchmarks/SchaeferTurek/SchaeferTurek.cpp @@ -93,7 +93,7 @@ #include "lbm/vtk/NonEquilibrium.h" #include "lbm/vtk/Velocity.h" -#include "postprocessing/sqlite/SQLite.h" +#include "sqlite/SQLite.h" #include "stencil/D3Q15.h" #include "stencil/D3Q19.h" @@ -961,12 +961,12 @@ void BoundarySetter< LatticeModel_T >::operator()() { CurvedDeltaValueCalculation< LatticeModel_T > deltaCalculation( blocks, *block, cylinder ); - lbm::refinement::consistentlyForceBoundary< BoundaryHandling_T >( *blocks, dynamic_cast< blockforest::Block & >(*block.get()), + lbm::refinement::consistentlyForceBoundary< BoundaryHandling_T >( *blocks, dynamic_cast< blockforest::Block & >(*block), boundaryHandlingId_, Curved_Flag, cylinder, deltaCalculation ); } else // staircase { - lbm::refinement::consistentlyForceBoundary< BoundaryHandling_T >( *blocks, dynamic_cast< blockforest::Block & >(*block.get()), + lbm::refinement::consistentlyForceBoundary< BoundaryHandling_T >( *blocks, dynamic_cast< blockforest::Block & >(*block), boundaryHandlingId_, Obstacle_Flag, cylinder ); } @@ -2787,12 +2787,12 @@ void run( const shared_ptr< Config > & config, const LatticeModel_T & latticeMod if( evaluationCheckFrequency != uint_t(0) ) evaluation->getResultsForSQLOnRoot( realProperties, integerProperties ); - auto runId = postprocessing::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSTiming, "RefinementTimeStep" ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSLTiming, "RefinementTimeStepLevelwise" ); + auto runId = sqlite::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSTiming, "RefinementTimeStep" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRTSLTiming, "RefinementTimeStepLevelwise" ); if( dynamicBlockStructure ) - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRefreshTiming, "BlockForestRefresh" ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedRefreshTiming, "BlockForestRefresh" ); } } } diff --git a/apps/benchmarks/UniformGrid/CMakeLists.txt b/apps/benchmarks/UniformGrid/CMakeLists.txt index 512aa80fcaa360b526c976648c995a9a397ebedd..2f75f12b5bc13630028bfcd3c63f036c94d16dab 100644 --- a/apps/benchmarks/UniformGrid/CMakeLists.txt +++ b/apps/benchmarks/UniformGrid/CMakeLists.txt @@ -3,4 +3,4 @@ waLBerla_link_files_to_builddir( "*.dat" ) waLBerla_add_executable ( NAME UniformGridBenchmark - DEPENDS blockforest boundary core domain_decomposition field lbm postprocessing timeloop vtk ) \ No newline at end of file + DEPENDS blockforest boundary core domain_decomposition field lbm postprocessing timeloop vtk sqlite ) \ No newline at end of file diff --git a/apps/benchmarks/UniformGrid/UniformGrid.cpp b/apps/benchmarks/UniformGrid/UniformGrid.cpp index 96e50eb129069daa7b5006ca9fe144eb005bdf7a..bd9efc7d40ead02f4e7e041e7f774cbfff4fdfa2 100644 --- a/apps/benchmarks/UniformGrid/UniformGrid.cpp +++ b/apps/benchmarks/UniformGrid/UniformGrid.cpp @@ -70,7 +70,7 @@ #include "lbm/vtk/Density.h" #include "lbm/vtk/Velocity.h" -#include "postprocessing/sqlite/SQLite.h" +#include "sqlite/SQLite.h" #include "stencil/D3Q19.h" #include "stencil/D3Q27.h" @@ -783,8 +783,8 @@ void run( const shared_ptr< Config > & config, const LatticeModel_T & latticeMod stringProperties[ "fullCommunication" ] = ( fullComm ? "yes" : "no" ); stringProperties[ "directComm"] = ( directComm ? "yes" : "no" ); - auto runId = postprocessing::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); - postprocessing::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); + auto runId = sqlite::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *reducedTimeloopTiming, "Timeloop" ); } } } diff --git a/apps/tutorials/pe/02_ConfinedGasExtended.cpp b/apps/tutorials/pe/02_ConfinedGasExtended.cpp index b39cf5f3e636b8a654bf1f078c32fe306a3021d6..69629eba1e82294ecaff18e5f25c540df05e5c91 100644 --- a/apps/tutorials/pe/02_ConfinedGasExtended.cpp +++ b/apps/tutorials/pe/02_ConfinedGasExtended.cpp @@ -32,7 +32,7 @@ #include <core/math/Random.h> #include <core/timing/TimingTree.h> #include <core/waLBerlaBuildInfo.h> -#include <postprocessing/sqlite/SQLite.h> +#include <sqlite/SQLite.h> #include <vtk/VTKOutput.h> #include <functional> @@ -289,9 +289,9 @@ int main( int argc, char ** argv ) //! [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" ); + auto runId = sqlite::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties ); + sqlite::storeTimingPoolInSqliteDB( sqlFile, runId, *tpReduced, "Timeloop" ); + sqlite::storeTimingTreeInSqliteDB( sqlFile, runId, tt, "TimingTree" ); } //! [SQL Save] diff --git a/apps/tutorials/pe/CMakeLists.txt b/apps/tutorials/pe/CMakeLists.txt index a41407eccd3e2306c1dda30e9bf6b65d3e534a29..4fda841130cfddf96965e100a1bdfa83d4df49ea 100644 --- a/apps/tutorials/pe/CMakeLists.txt +++ b/apps/tutorials/pe/CMakeLists.txt @@ -6,7 +6,7 @@ waLBerla_add_executable ( NAME 01_Tutorial_ConfinedGas waLBerla_add_executable ( NAME 02_Tutorial_ConfinedGasExtended FILES 02_ConfinedGasExtended.cpp - DEPENDS blockforest core pe postprocessing vtk ) + DEPENDS blockforest core pe postprocessing vtk sqlite ) waLBerla_execute_test( NO_MODULE_LABEL NAME 01_Tutorial_ConfinedGas PROCESSES 8 ) waLBerla_execute_test( NO_MODULE_LABEL NAME 02_Tutorial_ConfinedGasExtended diff --git a/cmake/waLBerlaFunctions.cmake b/cmake/waLBerlaFunctions.cmake index 091a9048bb9106c755d9aaf72fa3f96fc24bc2b3..841051458b2901be296cade1ad025c0e2b500757 100644 --- a/cmake/waLBerlaFunctions.cmake +++ b/cmake/waLBerlaFunctions.cmake @@ -193,7 +193,9 @@ function ( waLBerla_add_executable ) foreach ( depMod ${ARG_DEPENDS} ) get_module_library_name ( depModLibraryName ${depMod} ) if( NOT TARGET ${depModLibraryName} ) - message ( STATUS "Skipping ${ARG_NAME} since dependent module ${depMod} was not built" ) + if( WALBERLA_LOG_SKIPPED ) + message ( STATUS "Skipping ${ARG_NAME} since dependent module ${depMod} was not built" ) + endif() return() endif() endforeach() @@ -224,7 +226,9 @@ function ( waLBerla_add_executable ) codeGenRequired ${ARG_CODEGEN_CFG} ${sourceFiles} ) if( NOT WALBERLA_BUILD_WITH_CODEGEN AND codeGenRequired) - message(STATUS "Skipping ${ARG_NAME} since pystencils code generation is not enabled") + if( WALBERLA_LOG_SKIPPED ) + message(STATUS "Skipping ${ARG_NAME} since pystencils code generation is not enabled") + endif() return() endif() @@ -383,13 +387,17 @@ function ( waLBerla_execute_test ) endif() if( NOT ARG_COMMAND AND NOT TARGET ${ARG_NAME} ) - message ( STATUS "Skipping test ${ARG_NAME} since the corresponding target is not built" ) + if( WALBERLA_LOG_SKIPPED ) + message ( STATUS "Skipping test ${ARG_NAME} since the corresponding target is not built" ) + endif() return() endif() foreach( dependency_target ${ARG_DEPENDS_ON_TARGETS} ) if( NOT TARGET ${dependency_target} ) - message ( STATUS "Skipping test ${ARG_NAME} since the target ${dependency_target} is not built" ) + if( WALBERLA_LOG_SKIPPED ) + message ( STATUS "Skipping test ${ARG_NAME} since the target ${dependency_target} is not built" ) + endif() return() endif() endforeach( dependency_target ) diff --git a/python/mesa_pd.py b/python/mesa_pd.py index b010eb22336bdfcb44b709dc68b5288a10a88220..0ba17be6b76d5f60c39471b924b2fe6c95411471 100755 --- a/python/mesa_pd.py +++ b/python/mesa_pd.py @@ -94,7 +94,11 @@ if __name__ == '__main__': kernels.append( kernel.ExplicitEuler() ) kernels.append( kernel.ExplicitEulerWithShape() ) kernels.append( kernel.ForceLJ() ) + kernels.append( kernel.HCSITSRelaxationStep() ) kernels.append( kernel.HeatConduction() ) + kernels.append( kernel.InitParticlesForHCSITS() ) + kernels.append( kernel.InitContactsForHCSITS() ) + kernels.append( kernel.IntegrateParticlesHCSITS() ) kernels.append( kernel.InsertParticleIntoLinkedCells() ) kernels.append( kernel.InsertParticleIntoSparseLinkedCells() ) kernels.append( kernel.LinearSpringDashpot() ) diff --git a/python/mesa_pd/kernel/HCSITSRelaxationStep.py b/python/mesa_pd/kernel/HCSITSRelaxationStep.py new file mode 100644 index 0000000000000000000000000000000000000000..bb80d20ac32ea60971076fa75d351a7110fa5d84 --- /dev/null +++ b/python/mesa_pd/kernel/HCSITSRelaxationStep.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +from mesa_pd.accessor import Accessor +from ..Container import Container +from mesa_pd.utility import generateFile + +class HCSITSRelaxationStep(Container): + def __init__(self): + super().__init__() + self.addProperty("maxSubIterations", "size_t", defValue = "20") + self.addProperty("relaxationModel", "RelaxationModel", defValue = "InelasticFrictionlessContact") + self.addProperty("deltaMax", "real_t", defValue = "0") + self.addProperty("cor", "real_t", defValue = "real_t(0.2)") + + self.accessor = Accessor() + self.accessor.require("uid", "walberla::id_t", access="g") + self.accessor.require("position", "walberla::mesa_pd::Vec3", access="g") + self.accessor.require("linearVelocity", "walberla::mesa_pd::Vec3", access="g") + self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3", access="g") + self.accessor.require("invMass", "walberla::real_t", access="g" ) + self.accessor.require("invInertia", "walberla::mesa_pd::Mat3", access="g" ) + self.accessor.require("dv", "walberla::mesa_pd::Vec3", access="gr" ) + self.accessor.require("dw", "walberla::mesa_pd::Vec3", access="gr" ) + + def getRequirements(self): + return self.accessor + + def generate(self, path): + context = dict() + context["properties"] = self.properties + context["interface"] = self.accessor.properties + generateFile(path, 'kernel/HCSITSRelaxationStep.templ.h', context) diff --git a/python/mesa_pd/kernel/InitContactsForHCSITS.py b/python/mesa_pd/kernel/InitContactsForHCSITS.py new file mode 100644 index 0000000000000000000000000000000000000000..54da9536a4a2f3569a429357abd02bd7466974a3 --- /dev/null +++ b/python/mesa_pd/kernel/InitContactsForHCSITS.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from mesa_pd.accessor import Accessor +from ..Container import Container +from mesa_pd.utility import generateFile + +class InitContactsForHCSITS(Container): + def __init__(self): + super().__init__() + self.addProperty("erp", "real_t", defValue = "real_t(0.8)") + self.addProperty("maximumPenetration", "real_t", defValue ="0") + + self.paccessor = Accessor() + self.paccessor.require("uid", "walberla::id_t", access="g") + self.paccessor.require("position", "walberla::mesa_pd::Vec3", access="g") + self.paccessor.require("invInertia", "walberla::mesa_pd::Mat3", access="g" ) + self.paccessor.require("invMass", "walberla::real_t", access="g" ) + + + def getRequirements(self): + return self.paccessor + + def generate(self, path): + context = dict() + context["properties"] = self.properties + context["material_parameters"] = ["friction"] + context["interface"] = self.paccessor.properties + generateFile(path, 'kernel/InitContactsForHCSITS.templ.h', context) diff --git a/python/mesa_pd/kernel/InitParticlesForHCSITS.py b/python/mesa_pd/kernel/InitParticlesForHCSITS.py new file mode 100644 index 0000000000000000000000000000000000000000..ca0148cab20c55575aff787611076319ac2fe121 --- /dev/null +++ b/python/mesa_pd/kernel/InitParticlesForHCSITS.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from mesa_pd.accessor import Accessor +from ..Container import Container +from mesa_pd.utility import generateFile + +class InitParticlesForHCSITS(Container): + def __init__(self): + super().__init__() + self.addProperty("globalAcceleration", "walberla::mesa_pd::Vec3", defValue = "0") + + self.accessor = Accessor() + self.accessor.require("uid", "walberla::id_t", access="g") + self.accessor.require("linearVelocity", "walberla::mesa_pd::Vec3", access="gr") + self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3", access="gr") + self.accessor.require("invMass", "walberla::real_t", access="g" ) + self.accessor.require("inertia", "walberla::mesa_pd::Mat3", access="g" ) + self.accessor.require("invInertia", "walberla::mesa_pd::Mat3", access="g" ) + self.accessor.require("dv", "walberla::mesa_pd::Vec3", access="gr" ) + self.accessor.require("dw", "walberla::mesa_pd::Vec3", access="gr" ) + self.accessor.require("torque", "walberla::mesa_pd::Vec3", access="r" ) + self.accessor.require("force", "walberla::mesa_pd::Vec3", access="r" ) + + def getRequirements(self): + return self.accessor + + def generate(self, path): + context = dict() + context["properties"] = self.properties + context["interface"] = self.accessor.properties + generateFile(path, 'kernel/InitParticlesForHCSITS.templ.h', context) diff --git a/python/mesa_pd/kernel/IntegrateParticlesHCSITS.py b/python/mesa_pd/kernel/IntegrateParticlesHCSITS.py new file mode 100644 index 0000000000000000000000000000000000000000..b869d5decd30a3cf05a58981544d1309545f93f7 --- /dev/null +++ b/python/mesa_pd/kernel/IntegrateParticlesHCSITS.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from mesa_pd.accessor import Accessor +from ..Container import Container +from mesa_pd.utility import generateFile + +class IntegrateParticlesHCSITS(Container): + def __init__(self): + super().__init__() + self.addProperty("speedLimiterActive", "bool", defValue = "false") + self.addProperty("speedLimitFactor", "real_t", defValue ="real_t(1.0)") + + self.accessor = Accessor() + self.accessor.require("uid", "walberla::id_t", access="g") + self.accessor.require("position", "walberla::mesa_pd::Vec3", access="gr") + self.accessor.require("rotation", "walberla::mesa_pd::Rot3", access="gr") + self.accessor.require("linearVelocity", "walberla::mesa_pd::Vec3", access="gr") + self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3", access="gr") + self.accessor.require("dv", "walberla::mesa_pd::Vec3", access="g" ) + self.accessor.require("dw", "walberla::mesa_pd::Vec3", access="g" ) + self.accessor.require("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g") + + def getRequirements(self): + return self.accessor + + def generate(self, path): + context = dict() + context["properties"] = self.properties + context["interface"] = self.accessor.properties + generateFile(path, 'kernel/IntegrateParticlesHCSITS.templ.h', context) diff --git a/python/mesa_pd/kernel/__init__.py b/python/mesa_pd/kernel/__init__.py index 758512e84400e6a2442ffc568846942d33fb00b4..eef9907a547ca08e93d35a26eed6d89ef005580b 100644 --- a/python/mesa_pd/kernel/__init__.py +++ b/python/mesa_pd/kernel/__init__.py @@ -4,7 +4,11 @@ from .DoubleCast import DoubleCast from .ExplicitEuler import ExplicitEuler from .ExplicitEulerWithShape import ExplicitEulerWithShape from .ForceLJ import ForceLJ +from .HCSITSRelaxationStep import HCSITSRelaxationStep from .HeatConduction import HeatConduction +from .InitParticlesForHCSITS import InitParticlesForHCSITS +from .InitContactsForHCSITS import InitContactsForHCSITS +from .IntegrateParticlesHCSITS import IntegrateParticlesHCSITS from .InsertParticleIntoLinkedCells import InsertParticleIntoLinkedCells from .InsertParticleIntoSparseLinkedCells import InsertParticleIntoSparseLinkedCells from .LinearSpringDashpot import LinearSpringDashpot @@ -20,7 +24,11 @@ __all__ = ['DoubleCast', 'ExplicitEuler', 'ExplicitEulerWithShape', 'ForceLJ', + 'HCSITSRelaxationStep', 'HeatConduction', + 'InitParticlesForHCSITS', + 'InitContactsForHCSITS', + 'IntegrateParticlesHCSITS', 'InsertParticleIntoLinkedCells', 'InsertParticleIntoSparseLinkedCells', 'LinearSpringDashpot', diff --git a/python/mesa_pd/templates/data/ContactAccessor.templ.h b/python/mesa_pd/templates/data/ContactAccessor.templ.h index 50190bf636f3e81461634778c161d5abaafc6554..b51f5de7fd5f4f21d7f58f504da39043a652aeb0 100644 --- a/python/mesa_pd/templates/data/ContactAccessor.templ.h +++ b/python/mesa_pd/templates/data/ContactAccessor.templ.h @@ -26,7 +26,7 @@ #pragma once -#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/IContactAccessor.h> #include <mesa_pd/data/ContactStorage.h> #include <core/UniqueID.h> @@ -43,7 +43,7 @@ namespace data { * Provides get, set and getRef for all members of the ContactStorage. * Can be used as a basis class for a more advanced ContactAccessor. */ -class ContactAccessor : public IAccessor +class ContactAccessor : public IContactAccessor { public: ContactAccessor(const std::shared_ptr<data::ContactStorage>& ps) : ps_(ps) {} diff --git a/python/mesa_pd/templates/kernel/HCSITSRelaxationStep.templ.h b/python/mesa_pd/templates/kernel/HCSITSRelaxationStep.templ.h new file mode 100644 index 0000000000000000000000000000000000000000..1523a57b348524dba5dd3ce1edba892cb19d912c --- /dev/null +++ b/python/mesa_pd/templates/kernel/HCSITSRelaxationStep.templ.h @@ -0,0 +1,1049 @@ +//====================================================================================================================== +// +// 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 HCSITSRelaxationStep.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/IContactAccessor.h> +#include <mesa_pd/data/Flags.h> +#include <core/logging/Logging.h> +#include <core/math/Constants.h> +#include <core/math/Limits.h> + +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { +/** + * Apply a HCSITS (Hard-contact semi-implicit timestepping solver) relaxation to all contacts. + * Call this kernel on all bodies to perform on relaxation step of the solver. + * There exist different friction and relaxation models. See comments on their respective methods + * for an extended documentation. Use setRelaxationModel() to set it. + * getDeltaMax() can be used to query the maximum change in the impulses applied wrt to the last iteration. + * + * Use setMaxSubIterations() to set the maximum number of iterations performed to solve optimization problems arising + * during the relaxation (in InelasticGeneralizedMaximumDissipationContact and InelasticCoulombContactByDecoupling). + * + * Use setCor() to set the coefficient of restitution between 0 and 1 for the InelasticProjectedGaussSeidel model. + * All other models describe inelastic contacts. + * + * Example of Using the HCSITS kernels in a simulation: + * \code + * // ps: Particle storage + * // cs: Contact storage + * // pa: Particle accessor + * // ca: Contact accessor + * + * // Detect contacts first and fill contact storage + * + * mesa_pd::mpi::ReduceProperty reductionKernel; + * mesa_pd::mpi::BroadcastProperty broadcastKernel; + * + * kernel::IntegrateBodiesHCSITS integration; + * + * kernel::InitContactsForHCSITS initContacts(1); + * initContacts.setFriction(0,0,real_t(0.2)); + * kernel::InitBodiesForHCSITS initBodies; + * + * kernel::HCSITSRelaxationStep relaxationStep; + * relaxationStep.setCor(real_t(0.1)); // Only effective for PGSM + * + * // Init Contacts and Bodies (order is arbitrary) + * cs.forEachContact(false, kernel::SelectAll(), ca, initContacts, ca, pa); + * ps.forEachParticle(false, kernel::SelectAll(), pa, initBodies, pa, dt); + * + * // Reduce and Broadcast velocities with relaxation parameter 1 before the iteration + * VelocityUpdateNotification::Parameters::relaxationParam = real_t(1.0); + * reductionKernel.operator()<VelocityCorrectionNotification>(*ps); + * broadcastKernel.operator()<VelocityUpdateNotification>(*ps); + * + * VelocityUpdateNotification::Parameters::relaxationParam = real_t(0.8); + * for(int j = 0; j < 10; j++){ + * cs.forEachContact(false, kernel::SelectAll(), ca, relaxationStep, ca, pa, dt); + * reductionKernel.operator()<VelocityCorrectionNotification>(*ps); + * broadcastKernel.operator()<VelocityUpdateNotification>(*ps); + * } + * + * ps.forEachParticle(false, kernel::SelectAll(), pa, integration, pa, dt); + * \endcode + * \ingroup mesa_pd_kernel + * + * + */ +class HCSITSRelaxationStep +{ +public: + enum RelaxationModel { + InelasticFrictionlessContact, + ApproximateInelasticCoulombContactByDecoupling, + ApproximateInelasticCoulombContactByOrthogonalProjections, + InelasticCoulombContactByDecoupling, + InelasticCoulombContactByOrthogonalProjections, + InelasticGeneralizedMaximumDissipationContact, + InelasticProjectedGaussSeidel + }; + + // Default constructor sets the default values + HCSITSRelaxationStep() : + {%- for prop in properties %} + {{prop.name}}_( {{prop.defValue}} ){{ "," if not loop.last}} + {%- endfor %} + {} + + // Getter and Setter Functions + {%- for prop in properties %} + inline const {{prop.type}}& get{{prop.name | capFirst}}() const {return {{prop.name}}_;} + inline void set{{prop.name | capFirst}}({{prop.type}} v) { {{prop.name}}_ = v;} + {% endfor %} + + /** + * Perform relaxation for a single contact. + * \param cid The index of the contact with the accessor ca. + * \param ca The contact accessor. + * \param pa The particle accessor. + * \param dt The timestep size used. + */ + template <typename CAccessor, typename PAccessor> + void operator()(size_t cid, CAccessor &ca, PAccessor& pa, real_t dt); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticFrictionlessContacts(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa); + + template <typename CAccessor, typename PAccessor> + real_t relaxApproximateInelasticCoulombContactsByDecoupling(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticCoulombContactsByDecoupling(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticCoulombContactsByOrthogonalProjections(size_t cid, real_t dtinv, bool approximate, CAccessor &ca, PAccessor& pa ); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticGeneralizedMaximumDissipationContacts(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticContactsByProjectedGaussSeidel(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ); + +private: + // List properties + {% for prop in properties %} + {{prop.type}} {{prop.name }}_; + {%- endfor %} +}; + + +template <typename CAccessor, typename PAccessor> +inline void HCSITSRelaxationStep::operator()(size_t cid, CAccessor &ca, PAccessor& pa, real_t dt) +{ + static_assert(std::is_base_of<data::IContactAccessor, CAccessor>::value, "please provide a valid contact accessor"); + static_assert(std::is_base_of<data::IAccessor, PAccessor>::value, "please provide a valid accessor"); + + real_t dtinv(real_t(1.0)/dt); + switch( getRelaxationModel() ) { + case InelasticFrictionlessContact: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticFrictionlessContacts(cid, dtinv, ca, pa ))); + break; + + case ApproximateInelasticCoulombContactByDecoupling: + setDeltaMax(std::max( getDeltaMax(), relaxApproximateInelasticCoulombContactsByDecoupling( cid, dtinv, ca, pa ))); + break; + + case ApproximateInelasticCoulombContactByOrthogonalProjections: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticCoulombContactsByOrthogonalProjections( cid, dtinv, true, ca, pa ))); + break; + + case InelasticCoulombContactByDecoupling: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticCoulombContactsByDecoupling( cid, dtinv, ca, pa ))); + break; + + case InelasticCoulombContactByOrthogonalProjections: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticCoulombContactsByOrthogonalProjections( cid, dtinv, false, ca, pa ))); + break; + + case InelasticGeneralizedMaximumDissipationContact: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticGeneralizedMaximumDissipationContacts( cid, dtinv, ca, pa ))); + break; + + case InelasticProjectedGaussSeidel: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticContactsByProjectedGaussSeidel( cid, dtinv, ca, pa ))); + break; + + default: + throw std::runtime_error( "Unsupported relaxation model." ); +} + +WALBERLA_LOG_DETAIL("Delta Max: " << getDeltaMax()); + +} + +//************************************************************************************************* +/*!\brief Relaxes The contact with ID cid once. The contact model is for inelastic unilateral contacts without friction. + * + * \param cid The index of the contact + * \param ca The contact accessor + * \param pa The particle accessor + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). Separating contacts are preferred over + * persisting solutions if valid. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticFrictionlessContacts(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + if( gdot_nto[0] >= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + + // No need to apply zero impulse. + } + else { + // Contact is persisting. + + // Calculate the impulse necessary for a static contact expressed as components in the contact frame. + Vec3 p_wf( ca.getNormal(cid) * ( -ca.getDiag_n_inv(cid) * gdot_nto[0] ) ); + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + + return delta_max; +} +//************************************************************************************************* + + +//************************************************************************************************* +/*!\brief Relaxes all contacts once. The contact model is for inelastic unilateral contacts with approximate Coulomb friction. + * + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). Separating contacts are preferred over + * other solutions if valid. Static solutions are preferred over dynamic solutions. Dynamic + * solutions are computed by decoupling the normal from the frictional components. That is + * for a dynamic contact the normal component is relaxed first followed by the frictional + * components. The determination of the frictional components does not perform any subiterations + * and guarantees that the friction partially opposes slip. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxApproximateInelasticCoulombContactsByDecoupling(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + + if( gdot_nto[0] >= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + + // No need to apply zero impulse. + } + else { + // Contact is persisting (either static or dynamic). + + // Calculate the impulse necessary for a static contact expressed as components in the contact frame. + //Vec3 p_cf( -( contactCache.diag_nto_inv_[i] * gdot_nto ) ); + Vec3 p_cf( -( ca.getDiag_nto_inv(cid) * gdot_nto ) ); + + // Can p_cf[0] be negative even though -gdot_nto[0] > 0? Yes! Try: + // A = [0.5 -0.1 +0.1; -0.1 0.5 -0.1; +0.1 -0.1 1]; + // b = [0.01 -1 -1]'; + // A\b \approx [-0.19 -2.28 -1.21]' + // eig(A) \approx [ 0.40 0.56 1.04]' + + //real_t flimit( contactCache.mu_[i] * p_cf[0] ); + real_t flimit( ca.getMu(cid) * p_cf[0] ); + real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); + if( fsq > flimit * flimit || p_cf[0] < 0 ) { + // Contact cannot be static so it must be dynamic. + // => Complementarity condition on normal reaction now turns into an equation since we know that the normal reaction is definitely not zero. + + // For simplicity we change to a simpler relaxation scheme here: + // 1. Relax normal reaction with the tangential components equal to the previous values + // 2. Relax tangential components with the newly relaxed normal reaction + // Note: The better approach would be to solve the true 3x3 block problem! + // Warning: Simply projecting the frictional components is wrong since then the normal action is no longer 0 and simulations break. + + // Add the action of the frictional reactions from the last iteration to the relative contact velocity in normal direction so we can relax it separately. + // TODO This can be simplified: + //p_cf = trans( contactframe ) * contactCache.p_[i]; + //p_cf[0] = 0; + //p_[i] = contactframe * p_cf; + + + //Vec3 p_tmp = ( contactCache.t_[i] * contactCache.p_[i] ) * contactCache.t_[i] + ( contactCache.o_[i] * contactCache.p_[i] ) * contactCache.o_[i]; + Vec3 p_tmp = ( ca.getT(cid) * ca.getP(cid) ) * ca.getT(cid) + ( ca.getO(cid) * ca.getP(cid) ) * ca.getO(cid); + + + //real_t gdot_n = gdot_nto[0] + contactCache.n_[i] * ( ( contactCache.body1_[i]->getInvInertia() * ( contactCache.r1_[i] % p_tmp ) ) % contactCache.r1_[i] + ( contactCache.body2_[i]->getInvInertia() * ( contactCache.r2_[i] % p_tmp ) ) % contactCache.r2_[i] /* + diag_[i] * p */ ); + real_t gdot_n = gdot_nto[0] + ca.getNormal(cid) * ( ( pa.getInvInertia(bId1) * ( ca.getR1(cid) % p_tmp ) ) % ca.getR1(cid) + ( pa.getInvInertia(bId2) * ( ca.getR2(cid) % p_tmp ) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + //p_cf[0] = -( contactCache.diag_n_inv_[i] * gdot_n ); + p_cf[0] = -( ca.getDiag_n_inv(cid) * gdot_n ); + + // We cannot be sure that gdot_n <= 0 here and thus p_cf[0] >= 0 since we just modified it with the old values of the tangential reactions! => Project + p_cf[0] = std::max( real_c( 0 ), p_cf[0] ); + + // Now add the action of the normal reaction to the relative contact velocity in the tangential directions so we can relax the frictional components separately. + p_tmp = ca.getNormal(cid) * p_cf[0]; + Vec3 gdot2 = gdot + ( pa.getInvInertia(bId1) * ( ca.getR1(cid) % p_tmp ) ) % ca.getR1(cid) + ( pa.getInvInertia(bId2)* ( ca.getR2(cid) % p_tmp ) ) % ca.getR2(cid); + Vec2 gdot_to; + gdot_to[0] = ca.getT(cid) * gdot2; + gdot_to[1] = ca.getO(cid) * gdot2; + + //Vec2 ret = -( contactCache.diag_to_inv_[i] * gdot_to ); + Vec2 ret = -( ca.getDiag_to_inv(cid) * gdot_to ); + p_cf[1] = ret[0]; + p_cf[2] = ret[1]; + + flimit = ca.getMu(cid) * p_cf[0]; + fsq = p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2]; + if( fsq > flimit * flimit ) { + const real_t f( flimit / std::sqrt( fsq ) ); + p_cf[1] *= f; + p_cf[2] *= f; + } + } + else { + // Contact is static. + } + Vec3 p_wf( contactframe * p_cf ); + + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + return delta_max; +} +//************************************************************************************************* + + +//************************************************************************************************* + +/*!\brief Relaxes all contacts once. The contact model is for inelastic unilateral contacts with Coulomb friction. + * + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). Separating contacts are preferred over + * other solutions if valid. Static solutions are preferred over dynamic solutions. Dynamic + * solutions are computed by decoupling the normal from the frictional components. That is + * for a dynamic contact the normal component is relaxed first followed by the frictional + * components. How much the frictional components directly oppose slip as required by the Coulomb + * friction model depends on the number of subiterations performed. If no subiterations are + * performed the friction is guaranteed to be at least partially dissipative. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticCoulombContactsByDecoupling(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + //WALBERLA_LOG_WARNING( "Contact #" << i << " is\nA = \n" << contactCache.diag_nto_[i] << "\nb = \n" << gdot_nto << "\nmu = " << contactCache.mu_[i] ); + + if( gdot_nto[0] >= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + //WALBERLA_LOG_WARNING( "Contact #" << i << " is separating." ); + + // No need to apply zero impulse. + } + else { + // Contact is persisting (either static or dynamic). + + // Calculate the impulse necessary for a static contact expressed as components in the contact frame. + Vec3 p_cf( -( ca.getDiag_nto_inv(cid) * gdot_nto ) ); + + // Can p_cf[0] be negative even though -gdot_nto[0] > 0? Yes! Try: + // A = [0.5 -0.1 +0.1; -0.1 0.5 -0.1; +0.1 -0.1 1]; + // b = [0.01 -1 -1]'; + // A\b \approx [-0.19 -2.28 -1.21]' + // eig(A) \approx [ 0.40 0.56 1.04]' + + real_t flimit( ca.getMu(cid) * p_cf[0] ); + real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); + if( fsq > flimit * flimit || p_cf[0] < 0 ) { + // Contact cannot be static so it must be dynamic. + // => Complementarity condition on normal reaction now turns into an equation since we know that the normal reaction is definitely not zero. + + for (int j = 0; j < 20; ++j) { + // For simplicity we change to a simpler relaxation scheme here: + // 1. Relax normal reaction with the tangential components equal to the previous values + // 2. Relax tangential components with the newly relaxed normal reaction + // Note: The better approach would be to solve the true 3x3 block problem! + // Warning: Simply projecting the frictional components is wrong since then the normal action is no longer 0 and simulations break. + + Vec3 gdotCorrected; + real_t gdotCorrected_n; + Vec2 gdotCorrected_to; + + // Calculate the relative contact velocity in the global world frame (if no normal contact reaction is present at contact i) + p_cf[0] = 0; + // |<- p_cf is orthogonal to the normal and drops out in next line ->| + gdotCorrected = /* ( contactCache.body1_[i]->getInvMass() + contactCache.body2_[i]->getInvMass() ) * p_cf */ gdot + ( pa.getInvInertia(bId1) * ( ca.getR1(cid) % ( ca.getT(cid) * p_cf[1] + ca.getO(cid) * p_cf[2] ) ) ) % ca.getR1(cid) + ( pa.getInvInertia(bId2) * ( ca.getR2(cid) % ( ca.getT(cid) * p_cf[1] + ca.getO(cid) * p_cf[2] ) ) ) % ca.getR2(cid); + gdotCorrected_n = ca.getNormal(cid) * gdotCorrected + ca.getDistance(cid) * dtinv; + + // Relax normal component. + p_cf[0] = std::max( real_c( 0 ), -( ca.getDiag_n_inv(cid) * gdotCorrected_n ) ); + + // Calculate the relative contact velocity in the global world frame (if no frictional contact reaction is present at contact i) + p_cf[1] = p_cf[2] = real_c( 0 ); + // |<- p_cf is orthogonal to the tangential plane and drops out ->| + gdotCorrected = /* ( contactCache.body1_[i]->getInvMass() + contactCache.body2_[i]->getInvMass() ) * p_cf */ gdot + ( pa.getInvInertia(bId1) * ( ca.getR1(cid) % ( ca.getNormal(cid) * p_cf[0] ) ) ) % ca.getR1(cid) + ( pa.getInvInertia(bId2) * ( ca.getR2(cid) % ( ca.getNormal(cid) * p_cf[0] ) ) ) % ca.getR2(cid); + gdotCorrected_to[0] = ca.getT(cid) * gdotCorrected; + gdotCorrected_to[1] = ca.getO(cid) * gdotCorrected; + + // Relax frictional components. + Vec2 ret = -( ca.getDiag_to_inv(cid) * gdotCorrected_to ); + p_cf[1] = ret[0]; + p_cf[2] = ret[1]; + + flimit = ca.getMu(cid) * p_cf[0]; + fsq = p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2]; + if( fsq > flimit * flimit ) { + // 3.2.1 Decoupling + // \tilde{x}^0 = p_cf[1..2] + + // Determine \tilde{A} + Mat2 diag_to( ca.getDiag_nto(cid)(1, 1), ca.getDiag_nto(cid)(1, 2), ca.getDiag_nto(cid)(2, 1), ca.getDiag_nto(cid)(2, 2) ); + + const real_t f( flimit / std::sqrt( fsq ) ); + //p_cf[1] *= f; + //p_cf[2] *= f; + + // Determine search interval for Golden Section Search + const real_t phi( real_c(0.5) * ( real_c(1) + std::sqrt( real_c( 5 ) ) ) ); + real_t shift( std::atan2( -p_cf[2], p_cf[1] ) ); + real_t acos_f( std::acos( f ) ); + + //WALBERLA_LOG_WARNING( acos_f << " " << shift ); + + real_t alpha_left( -acos_f - shift ); + //Vec2 x_left( flimit * std::cos( alpha_left ), flimit * std::sin( alpha_left ) ); + //real_t f_left( 0.5 * trans( x_left ) * ( diag_to * x_left ) - trans( x_left ) * ( -gdot_to ) ); + + real_t alpha_right( acos_f - shift ); + //Vec2 x_right( flimit * std::cos( alpha_right ), flimit * std::sin( alpha_right ) ); + //real_t f_right( 0.5 * trans( x_right ) * ( diag_to * x_right ) - trans( x_right ) * ( -gdot_to ) ); + + real_t alpha_mid( ( alpha_right + alpha_left * phi ) / ( 1 + phi ) ); + Vec2 x_mid( flimit * std::cos( alpha_mid ), flimit * std::sin( alpha_mid ) ); + real_t f_mid( real_c(0.5) * x_mid * ( diag_to * x_mid ) - x_mid * ( -gdotCorrected_to ) ); + + bool leftlarger = false; + for( size_t k = 0; k < getMaxSubIterations(); ++k ) { + real_t alpha_next( alpha_left + ( alpha_right - alpha_mid ) ); + Vec2 x_next( flimit * std::cos( alpha_next ), flimit * std::sin( alpha_next ) ); + real_t f_next( real_c(0.5) * x_next * ( diag_to * x_next ) - x_next * ( -gdotCorrected_to ) ); + //WALBERLA_LOG_WARNING( "[(" << alpha_left << ", ?); (" << alpha_mid << ", " << f_mid << "); (" << alpha_right << ", ?)] <- (" << alpha_next << ", " << f_next << ")" ); + //WALBERLA_LOG_WARNING( "left: " << alpha_mid - alpha_left << " right: " << alpha_right - alpha_mid << " ll: " << leftlarger ); + //WALBERLA_ASSERT(leftlarger ? (alpha_mid - alpha_left > alpha_right - alpha_mid) : (alpha_mid - alpha_left < alpha_right - alpha_mid), "ll inconsistent!" ); + + if (leftlarger) { + // left interval larger + if( f_next < f_mid ) { + alpha_right = alpha_mid; + alpha_mid = alpha_next; + x_mid = x_next; + f_mid = f_next; + leftlarger = true; + } + else { + alpha_left = alpha_next; + leftlarger = false; + } + } + else { + // right interval larger + if( f_next < f_mid ) { + alpha_left = alpha_mid; + alpha_mid = alpha_next; + x_mid = x_next; + f_mid = f_next; + leftlarger = false; + } + else { + alpha_right = alpha_next; + leftlarger = true; + } + } + } + //WALBERLA_LOG_WARNING( "dalpha = " << alpha_right - alpha_left ); + + p_cf[1] = x_mid[0]; + p_cf[2] = x_mid[1]; + } + } + //WALBERLA_LOG_WARNING( "Contact #" << i << " is dynamic." ); + } + else { + // Contact is static. + //WALBERLA_LOG_WARNING( "Contact #" << i << " is static." ); + } + + //WALBERLA_LOG_WARNING( "Contact reaction in contact frame: " << p_cf << "\n" << ca.getDiag_nto(cid)*p_cf + gdot_nto ); + Vec3 p_wf( contactframe * p_cf ); + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + + return delta_max; +} +//************************************************************************************************* + + +//************************************************************************************************* +/*!\brief Relaxes all contacts once. The contact model is for inelastic unilateral contacts with Coulomb friction. + * + * \param dtinv The inverse of the current time step. + * \param approximate Use the approximate model showing bouncing. + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). The iterative method to solve the contact + * problem is e.g. described in the article "A matrix-free cone complementarity approach for + * solving large-scale, nonsmooth, rigid body dynamics" by A. Tasora and M. Anitescu in Computer + * Methods in Applied Mechanics and Engineering (Volume 200, Issues 5–8, 15 January 2011, + * Pages 439-453). The contact model is purely quadratic and convergence should be good but depends + * on a parameter. The one-contact problem has a unique solution. The frictional reactions + * for a dynamic contact converge to those that directly oppose slip. However, the contact is + * not perfectly inelastic for dynamic contacts but bounces. These vertical motions tend to + * go to zero for smaller time steps and can be interpreted as exaggerated vertical motions + * coming from micro asperities (see "Optimization-based simulation of nonsmooth rigid multibody + * dynamics" by M. Anitescu in Mathematical Programming (Volume 105, Issue 1, January 2006, Pages + * 113-143). These motions can be prevented by a small change in the iteration proposed in "The + * bipotential method: a constructive approach to design the complete contact law with friction and + * improved numerical algorithms" by G. De Saxce and Z-Q. Feng in Mathematical and Computer + * Modelling (Volume 28, Issue 4, 1998, Pages 225-245). Which iteration is used is controlled with + * the approximate parameter. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticCoulombContactsByOrthogonalProjections(size_t cid, real_t dtinv, bool approximate, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + + const real_t w( 1 ); // w > 0 + Vec3 p_cf( contactframe.getTranspose() * ca.getP(cid) ); + if( approximate ) { + // Calculate next iterate (Anitescu/Tasora). + p_cf = p_cf - w * ( ca.getDiag_nto(cid) * p_cf + gdot_nto ); + } + else { + // Calculate next iterate (De Saxce/Feng). + Vec3 tmp( ca.getDiag_nto(cid) * p_cf + gdot_nto ); + tmp[0] += std::sqrt( math::sq( tmp[1] ) + math::sq( tmp[2] ) ) * ca.getMu(cid); + p_cf = p_cf - w * tmp; + } + + // Project. + real_t flimit( ca.getMu(cid) * p_cf[0] ); + real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); + if( p_cf[0] > 0 && fsq < flimit * flimit ) { + // Unconstrained minimum is in cone leading to a static contact and no projection + // is necessary. + } + else if( p_cf[0] < 0 && fsq < math::sq( p_cf[0] )/ math::sq( ca.getMu(cid) ) ) { + // Unconstrained minimum is in dual cone leading to a separating contact where no contact + // reaction is present (the unconstrained minimum is projected to the tip of the cone). + p_cf = Vec3(); + } + else { + // The contact is dynamic. + real_t f( std::sqrt( fsq ) ); + p_cf[0] = ( f * ca.getMu(cid) + p_cf[0] ) / ( math::sq( ca.getMu(cid) ) + 1 ); + real_t factor( ca.getMu(cid) * p_cf[0] / f ); + p_cf[1] *= factor; + p_cf[2] *= factor; + } + + Vec3 p_wf( contactframe * p_cf ); + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + return delta_max; +} +//************************************************************************************************* + + +//************************************************************************************************* + +/*!\brief Relaxes all contacts once. The contact model is for inelastic unilateral contacts with the generalized maximum dissipation principle for friction. + * + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). Dynamic solutions are computed by + * minimizing the kinetic energy along the intersection of the plane of maximum compression and + * the friction cone. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticGeneralizedMaximumDissipationContacts(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + + if( gdot_nto[0] >= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + + // No need to apply zero impulse. + } + else { + // Contact is persisting (either static or dynamic). + + // Calculate the impulse necessary for a static contact expressed as components in the contact frame. + Vec3 p_cf( -( ca.getDiag_nto_inv(cid) * gdot_nto ) ); + + // Can p_cf[0] be negative even though -gdot_nto[0] > 0? Yes! Try: + // A = [0.5 -0.1 +0.1; -0.1 0.5 -0.1; +0.1 -0.1 1]; + // b = [0.01 -1 -1]'; + // A\b \approx [-0.19 -2.28 -1.21]' + // eig(A) \approx [ 0.40 0.56 1.04]' + + real_t flimit( ca.getMu(cid) * p_cf[0] ); + real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); + if( fsq > flimit * flimit || p_cf[0] < 0 ) { + // Contact cannot be static so it must be dynamic. + // => Complementarity condition on normal reaction now turns into an equation since we know that the normal reaction is definitely not zero. + + // \breve{x}^0 = p_cf[1..2] + + // Eliminate normal component from 3x3 system: ca.getDiag_nto(cid)*p_cf + gdot_nto => \breve{A} \breve{x} - \breve{b} + const real_t invA_nn( math::inv( ca.getDiag_nto(cid)(0, 0) ) ); + const real_t offdiag( ca.getDiag_nto(cid)(1, 2) - invA_nn * ca.getDiag_nto(cid)(0, 1) * ca.getDiag_nto(cid)(0, 2) ); + Mat2 A_breve( ca.getDiag_nto(cid)(1, 1) - invA_nn *math::sq( ca.getDiag_nto(cid)(0, 1) ), offdiag, offdiag, ca.getDiag_nto(cid)(2, 2) - invA_nn *math::sq( ca.getDiag_nto(cid)(0, 2) ) ); + Vec2 b_breve( -gdot_nto[1] + invA_nn * ca.getDiag_nto(cid)(0, 1) * gdot_nto[0], -gdot_nto[2] + invA_nn * ca.getDiag_nto(cid)(0, 2) * gdot_nto[0] ); + + const real_t shiftI( std::atan2( -ca.getDiag_nto(cid)(0, 2), ca.getDiag_nto(cid)(0, 1) ) ); + const real_t shiftJ( std::atan2( -p_cf[2], p_cf[1] ) ); + const real_t a3( std::sqrt(math::sq( ca.getDiag_nto(cid)(0, 1) ) +math::sq( ca.getDiag_nto(cid)(0, 2) ) ) ); + const real_t fractionI( -ca.getDiag_nto(cid)(0, 0) / ( ca.getMu(cid) * a3 ) ); + const real_t fractionJ( std::min( invA_nn * ca.getMu(cid) * ( ( -gdot_nto[0] ) / std::sqrt( fsq ) - a3 * std::cos( shiftI - shiftJ ) ), real_c( 1 ) ) ); + + // Search interval determination. + real_t alpha_left, alpha_right; + if( fractionJ < -1 ) { + // J is complete + const real_t angleI( std::acos( fractionI ) ); + alpha_left = -angleI - shiftI; + alpha_right = +angleI - shiftI; + if( alpha_left < 0 ) { + alpha_left += 2 * math::pi; + alpha_right += 2 * math::pi; + } + } + else if( ca.getDiag_nto(cid)(0, 0) > ca.getMu(cid) * a3 ) { + // I is complete + const real_t angleJ( std::acos( fractionJ ) ); + alpha_left = -angleJ - shiftJ; + alpha_right = +angleJ - shiftJ; + if( alpha_left < 0 ) { + alpha_left += 2 * math::pi; + alpha_right += 2 * math::pi; + } + } + else { + // neither I nor J is complete + const real_t angleJ( std::acos( fractionJ ) ); + real_t alpha1_left( -angleJ - shiftJ ); + real_t alpha1_right( +angleJ - shiftJ ); + if( alpha1_left < 0 ) { + alpha1_left += 2 * math::pi; + alpha1_right += 2 * math::pi; + } + const real_t angleI( std::acos( fractionI ) ); + real_t alpha2_left( -angleI - shiftI ); + real_t alpha2_right( +angleI - shiftI ); + if( alpha2_left < 0 ) { + alpha2_left += 2 * math::pi; + alpha2_right += 2 * math::pi; + } + + // Swap intervals if second interval does not start right of the first interval. + if( alpha1_left > alpha2_left ) { + std::swap( alpha1_left, alpha2_left ); + std::swap( alpha1_right, alpha2_right ); + } + + if( alpha2_left > alpha1_right ) { + alpha2_right -= 2*math::pi; + if( alpha2_right > alpha1_right ) { + // [alpha1_left; alpha1_right] \subset [alpha2_left; alpha2_right] + } + else { + // [alpha2_left; alpha2_right] intersects the left end of [alpha1_left; alpha1_right] + alpha1_right = alpha2_right; + } + } + else { + alpha1_left = alpha2_left; + if( alpha2_right > alpha1_right ) { + // [alpha2_left; alpha2_right] intersects the right end of [alpha1_left; alpha1_right] + } + else { + // [alpha2_left; alpha2_right] \subset [alpha1_left; alpha1_right] + alpha1_right = alpha2_right; + } + } + + alpha_left = alpha1_left; + alpha_right = alpha1_right; + } + + const real_t phi( real_c(0.5) * ( real_c(1) + std::sqrt( real_c( 5 ) ) ) ); + real_t alpha_mid( ( alpha_right + alpha_left * phi ) / ( 1 + phi ) ); + Vec2 x_mid; + real_t f_mid; + + { + real_t r_ub = ca.getMu(cid) * ( -gdot_nto[0] ) / ( ca.getDiag_nto(cid)(0, 0) + ca.getMu(cid) * a3 * std::cos( alpha_mid + shiftI ) ); + if( r_ub < 0 ) + r_ub = math::Limits<real_t>::inf(); + x_mid = Vec2( r_ub * std::cos( alpha_mid ), r_ub * std::sin( alpha_mid ) ); + f_mid = real_c(0.5) * x_mid * ( A_breve * x_mid ) - x_mid * b_breve; + } + + bool leftlarger = false; + for( size_t k = 0; k < maxSubIterations_; ++k ) { + real_t alpha_next( alpha_left + ( alpha_right - alpha_mid ) ); + real_t r_ub = ca.getMu(cid) * ( -gdot_nto[0] ) / ( ca.getDiag_nto(cid)(0, 0) + ca.getMu(cid) * a3 * std::cos( alpha_next + shiftI ) ); + if( r_ub < 0 ) + r_ub = math::Limits<real_t>::inf(); + Vec2 x_next( r_ub * std::cos( alpha_next ), r_ub * std::sin( alpha_next ) ); + real_t f_next( real_c(0.5) * x_next * ( A_breve * x_next ) - x_next * b_breve ); + + //WALBERLA_LOG_WARNING( "[(" << alpha_left << ", ?); (" << alpha_mid << ", " << f_mid << "); (" << alpha_right << ", ?)] <- (" << alpha_next << ", " << f_next << ")" ); + //WALBERLA_LOG_WARNING( "left: " << alpha_mid - alpha_left << " right: " << alpha_right - alpha_mid << " ll: " << leftlarger ); + //WALBERLA_ASSERT(leftlarger ? (alpha_mid - alpha_left > alpha_right - alpha_mid) : (alpha_mid - alpha_left < alpha_right - alpha_mid), "ll inconsistent!" ); + + if (leftlarger) { + // left interval larger + if( f_next < f_mid ) { + alpha_right = alpha_mid; + alpha_mid = alpha_next; + x_mid = x_next; + f_mid = f_next; + leftlarger = true; + } + else { + alpha_left = alpha_next; + leftlarger = false; + } + } + else { + // right interval larger + if( f_next < f_mid ) { + alpha_left = alpha_mid; + alpha_mid = alpha_next; + x_mid = x_next; + f_mid = f_next; + leftlarger = false; + } + else { + alpha_right = alpha_next; + leftlarger = true; + } + } + } + //WALBERLA_LOG_DETAIL( "dalpha = " << alpha_right - alpha_left << "\n"); + { + real_t alpha_init( std::atan2( p_cf[2], p_cf[1] ) ); + real_t r_ub = ca.getMu(cid) * ( -gdot_nto[0] ) / ( ca.getDiag_nto(cid)(0, 0) + ca.getMu(cid) * a3 * std::cos( alpha_init + shiftI ) ); + if( r_ub < 0 ) + r_ub = math::Limits<real_t>::inf(); + Vec2 x_init( r_ub * std::cos( alpha_init ), r_ub * std::sin( alpha_init ) ); + real_t f_init( real_c(0.5) * x_init * ( A_breve * x_init ) - x_init * b_breve ); + + if( f_init < f_mid ) + { + x_mid = x_init; + WALBERLA_LOG_DETAIL( "Replacing solution by primitive dissipative solution (" << f_init << " < " << f_mid << " at " << alpha_init << " vs. " << alpha_mid << ").\n"); + } + } + + p_cf[0] = invA_nn * ( -gdot_nto[0] - ca.getDiag_nto(cid)(0, 1) * x_mid[0] - ca.getDiag_nto(cid)(0, 2) * x_mid[1] ); + p_cf[1] = x_mid[0]; + p_cf[2] = x_mid[1]; + //WALBERLA_LOG_DETAIL( "Contact #" << i << " is dynamic." ); + } + else { + // Contact is static. + //WALBERLA_LOG_DETAIL( "Contact #" << i << " is static." ); + } + Vec3 p_wf( contactframe * p_cf ); + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + //WALBERLA_LOG_DETAIL( "Contact reaction in contact frame: " << p_cf << "\nContact action in contact frame: " << ca.getDiag_nto(cid)*p_cf + gdot_nto ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + return delta_max; +} +//************************************************************************************************* + +//************************************************************************************************* +//************************************************************************************************* +/*!\brief Relaxes all contacts once. The contact model is from the paper by Tschisgale et al. + * "A constraint-based collision model for Cosserat rods" and works with a coefficient of + * restitution cor with 0 < cor <= 1. + * + * \return The largest variation of contact impulses in the L-infinity norm. + * + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticContactsByProjectedGaussSeidel(size_t cid, real_t /*dtinv*/, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + // This is the negative contact velocity as for the other methods + Vec3 gdot ( ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) - + ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) + + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) - + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) /* + diag_[i] * p */ ); + + // Use restitution hypothesis + gdot = gdot + getCor()*(gdot*ca.getNormal(cid))*ca.getNormal(cid); + + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + + // + // Turn system matrix back to the world frame. + Mat3 K = contactframe * ca.getDiag_nto(cid) * contactframe.getTranspose(); + Mat3 Kinv = contactframe * ca.getDiag_nto_inv(cid) * contactframe.getTranspose(); + + // Compute impulse necessary in the world frame + Vec3 p_wf( - Kinv * gdot ); + + + + if( gdot * ca.getNormal(cid) <= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + // No need to apply zero impulse. + } + else { + // Dissect the impuls in a tangetial and normal directions + // Use the inverted contact frame with -n as in the publication + Mat3 reversedContactFrame( -ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 p_rcf( reversedContactFrame.getTranspose() * p_wf ); + + // Check the frictional limit + real_t flimit( ca.getMu(cid) * p_rcf[0] ); + + // Square of tangential components of the impulse + real_t fsq( p_rcf[1] * p_rcf[1] + p_rcf[2] * p_rcf[2] ); + + if( fsq > flimit * flimit || p_rcf[0] < 0 ) { + // Contact cannot be static so it must be dynamic. + // Calculate corrected contact reaction by the method of Tschigale et al. + // Normal component is close to 0 or negative, and we cannot asses a tangential direction + // Treat this case with the 0 impulse + if (fsq < real_t(1e-8)) { + delta_max = std::max(delta_max, std::max(std::abs(ca.getP(cid)[0]), + std::max(std::abs(ca.getP(cid)[1]), std::abs(ca.getP(cid)[2])))); + ca.getPRef(cid) = Vec3(); + } else { + // tangential direction of sliding + Vec3 tan_dir = ((p_rcf[1] * ca.getT(cid)) + (p_rcf[2] * ca.getO(cid))).getNormalized(); + // scalar magnitude of pv. + real_t abspv = ((K * p_wf) * ca.getNormal(cid)) / + ((K * (-ca.getNormal(cid) + ca.getMu(cid) * tan_dir)) * ca.getNormal(cid)); + p_wf = abspv * (-ca.getNormal(cid) + ca.getMu(cid) * tan_dir); + + } + } + // Calculate variation + Vec3 dp(ca.getP(cid) - p_wf); + delta_max = std::max(delta_max, std::max(std::abs(dp[0]), std::max(std::abs(dp[1]), std::abs(dp[2])))); + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + return delta_max; +} +//************************************************************************************************* + + +} //namespace kernel +} //namespace mesa_pd +} //namespace walberla diff --git a/python/mesa_pd/templates/kernel/InitContactsForHCSITS.templ.h b/python/mesa_pd/templates/kernel/InitContactsForHCSITS.templ.h new file mode 100644 index 0000000000000000000000000000000000000000..eb48c9a5199d5b54d50f2a52d14ac68f63c74a34 --- /dev/null +++ b/python/mesa_pd/templates/kernel/InitContactsForHCSITS.templ.h @@ -0,0 +1,180 @@ +//====================================================================================================================== +// +// 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 InitContactsForHCSITS.h +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/IContactAccessor.h> +#include <mesa_pd/data/Flags.h> +#include <core/logging/Logging.h> + +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { + +/** +* Init the datastructures for a contact for later use of the HCSITS-Solver. +* Call this kernel on all contacts that you want to treat with HCSITS before performing any relaxation timesteps. +* Use setErp() to set the error reduction parameter, which defines the share of the overlap that is resolved in one step +* (0 meaning after the relaxation the overlap is the same, 1 meaning the particles will have no overlap after relaxation). +* Use setFriction(a,b, cof) to define the coefficient of friction cof between materials a and b. It is assumed to be + * symmetric w.r.t. the materials. +* \ingroup mesa_pd_kernel +*/ +class InitContactsForHCSITS{ +public: + + // Default constructor sets the default values + explicit InitContactsForHCSITS(const uint_t numParticleTypes) : + numParticleTypes_ (numParticleTypes), + {%- for prop in properties %} + {{prop.name}}_( {{prop.defValue}} ){{ "," if not loop.last}} + {%- endfor %} + { + + {% for param in material_parameters %} + {{param}}_.resize(numParticleTypes * numParticleTypes, real_t(0)); + {%- endfor %} + } + + // Getter and Setter Functions + {%- for prop in properties %} + inline const {{prop.type}}& get{{prop.name | capFirst}}() const {return {{prop.name}}_;} + inline void set{{prop.name | capFirst}}({{prop.type}} v) { {{prop.name}}_ = v;} + {% endfor %} + + // Getter and Setter Functions for material parameter combinations (they are assumed symmetric). + {% for param in material_parameters %} + inline void set{{param | capFirst}}(const size_t type1, const size_t type2, const real_t& val) + { + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + {{param}}_[numParticleTypes_*type1 + type2] = val; + {{param}}_[numParticleTypes_*type2 + type1] = val; + } + {%- endfor %} + + {% for param in material_parameters %} + inline real_t get{{param | capFirst}}(const size_t type1, const size_t type2) const + { + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + WALBERLA_ASSERT_FLOAT_EQUAL( {{param}}_[numParticleTypes_*type1 + type2], + {{param}}_[numParticleTypes_*type2 + type1], + "parameter matrix for {{param}} not symmetric!"); + return {{param}}_[numParticleTypes_*type1 + type2]; + } + {%- endfor %} + + // List material parameters + {% for param in material_parameters %} + std::vector<real_t> {{param}}_ {}; + {%- endfor %} + + template<typename CAccessor, typename PAccessor> + void operator()(size_t c, CAccessor &ca, PAccessor &pa); + +private: + // List properties + const uint_t numParticleTypes_; + + {% for prop in properties %} + {{prop.type}} {{prop.name }}_; + {%- endfor %} + + {% for param in parameters %} + std::vector<real_t> {{param}}_ {}; + {%- endfor %} + +}; + + +template<typename CAccessor, typename PAccessor> +inline void InitContactsForHCSITS::operator()(size_t c, CAccessor &ca, PAccessor &pa) { + + static_assert(std::is_base_of<data::IContactAccessor, CAccessor>::value, "please provide a valid contact accessor"); + static_assert(std::is_base_of<data::IAccessor, PAccessor>::value, "please provide a valid particle accessor"); + + size_t bId1 = ca.getId1(c); + size_t bId2 = ca.getId2(c); + ca.setR1(c, ca.getPosition(c) - pa.getPosition(bId1)); + ca.setR2(c, ca.getPosition(c) - pa.getPosition(bId2)); + + + // construct vector perpendicular to the normal (cross product with cardinal basis vector where the 1 component is where the other vector has its component of smallest magnitude) + if (std::fabs(ca.getNormalRef(c)[0]) < std::fabs(ca.getNormalRef(c)[1])) { + if (std::fabs(ca.getNormalRef(c)[0]) < std::fabs(ca.getNormalRef(c)[2])) + ca.setT(c, Vec3(0, ca.getNormalRef(c)[2], -ca.getNormalRef(c)[1])); // = n x (1, 0, 0)^T + else + ca.setT(c, Vec3(ca.getNormalRef(c)[1], -ca.getNormalRef(c)[0], 0)); // = n x (0, 0, 1)^T + } else { + if (std::fabs(ca.getNormalRef(c)[1]) < std::fabs(ca.getNormalRef(c)[2])) + ca.setT(c, Vec3(-ca.getNormalRef(c)[2], 0, ca.getNormalRef(c)[0])); // = n x (0, 1, 0)^T + else + ca.setT(c, Vec3(ca.getNormalRef(c)[1], -ca.getNormalRef(c)[0], 0)); // = n x (0, 0, 1)^T + } + normalize(ca.getTRef(c)); + ca.setO(c, ca.getNormal(c) % ca.getT(c)); + + Mat3 contactframe(ca.getNormal(c), ca.getT(c), ca.getO(c)); + + // If the distance is negative then penetration is present. This is an error and should be corrected. + // Correcting the whole error is not recommended since due to the linearization the errors cannot + // completely fixed anyway and the error reduction will introduce artificial restitution. + // However, if the distance is positive then it is not about error correction but the distance that + // can still be overcome without penetration and thus no error correction parameter should be applied. + if (ca.getDistance(c) < real_t(0.0)) { + setMaximumPenetration(std::max(getMaximumPenetration(), -ca.getDistance(c))); + ca.getDistanceRef(c) *= erp_; + } + + ca.getMuRef(c) = getFriction(pa.getType(bId1), pa.getType(bId2)); + + Mat3 diag = -( + math::skewSymCrossProduct(ca.getR1(c), + math::skewSymCrossProduct(pa.getInvInertia(bId1), ca.getR1(c))) + + math::skewSymCrossProduct(ca.getR2(c), + math::skewSymCrossProduct(pa.getInvInertia(bId2), ca.getR2(c)))); + diag[0] += pa.getInvMass(bId1) + pa.getInvMass(bId2); + diag[4] += pa.getInvMass(bId1) + pa.getInvMass(bId2); + diag[8] += pa.getInvMass(bId1) + pa.getInvMass(bId2); + + diag = contactframe.getTranspose() * diag * contactframe; + + // Diagonal block is known to be positive-definite and thus an inverse always exists. + ca.getDiag_ntoRef(c) = diag; + ca.getDiag_nto_invRef(c) = diag.getInverse(); + ca.getDiag_n_invRef(c) = math::inv(diag[0]); + ca.getDiag_to_invRef(c) = Mat2(diag[4], diag[5], diag[7], diag[8]).getInverse(); +} + +} +} +} diff --git a/python/mesa_pd/templates/kernel/InitParticlesForHCSITS.templ.h b/python/mesa_pd/templates/kernel/InitParticlesForHCSITS.templ.h new file mode 100644 index 0000000000000000000000000000000000000000..1ef8698a49004622e5ddda143d07b70bc53edce3 --- /dev/null +++ b/python/mesa_pd/templates/kernel/InitParticlesForHCSITS.templ.h @@ -0,0 +1,123 @@ +//====================================================================================================================== +// +// 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 InitParticlesForHCSITS.h +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/Flags.h> +#include <core/logging/Logging.h> + +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { +/** + * Init the datastructures for the particles for later use of the HCSITS-Solver. + * Call this kernel on all particles that will be treated with HCSITS before performing any relaxation timesteps. + * Use setGlobalAcceleration() to set an accelleration action uniformly across all particles (e.g. gravity) + * \ingroup mesa_pd_kernel + */ +class InitParticlesForHCSITS +{ +public: + // Default constructor sets the default values + InitParticlesForHCSITS() : + {%- for prop in properties %} + {{prop.name}}_( {{prop.defValue}} ){{ "," if not loop.last}} + {%- endfor %} + {} + + // Getter and Setter Functions for properties + {%- for prop in properties %} + inline const {{prop.type}}& get{{prop.name | capFirst}}() const {return {{prop.name}}_;} + inline void set{{prop.name | capFirst}}({{prop.type}} v) { {{prop.name}}_ = v;} + {% endfor %} + + template <typename Accessor> + void operator()(size_t j, Accessor& ac, real_t dt); + + template <typename Accessor> + void initializeVelocityCorrections(Accessor& ac, size_t body, Vec3& dv, Vec3& dw, real_t dt ) const; + +private: + // List properties + {% for prop in properties %} + {{prop.type}} {{prop.name }}_; + {%- endfor %} + +}; + + +template <typename Accessor> +inline void InitParticlesForHCSITS::operator()(size_t j, Accessor& ac, real_t dt) +{ + using namespace data::particle_flags; + static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor"); + auto particle_flags = ac.getFlagsRef(j); + if (isSet(particle_flags, GLOBAL)){ + WALBERLA_CHECK( isSet(particle_flags, FIXED), "Global bodies need to have infinite mass as they are not communicated!" ); + initializeVelocityCorrections( ac, j, ac.getDvRef(j), ac.getDwRef(j), dt ); // use applied external forces to calculate starting velocity + }else{ + initializeVelocityCorrections( ac, j, ac.getDvRef(j), ac.getDwRef(j), dt ); // use applied external forces to calculate starting velocity + if(!isSet(particle_flags, FIXED)){ // Update velocities with global acceleration and angular velocity with euler eqn + ac.getLinearVelocityRef(j) = ac.getLinearVelocity(j) + getGlobalAcceleration() * dt; + ac.getAngularVelocityRef(j) = ac.getAngularVelocity(j) + dt * ( ac.getInvInertia(j) * ( ( ac.getInertia(j) * ac.getAngularVelocity(j) ) % ac.getAngularVelocity(j) ) ); + } + } +} + + +//************************************************************************************************* +/*!\brief Calculates the initial velocity corrections of a given body. + * \param ac The particle accessor + * \param body The body whose velocities to time integrate + * \param dv On return the initial linear velocity correction. + * \param w On return the initial angular velocity correction. + * \param dt The time step size. + * \return void + * + * Calculates the velocity corrections effected by external forces and torques in an explicit Euler + * time integration of the velocities of the given body. For fixed objects the velocity corrections + * are set to zero. External forces and torques are reset if indicated by the settings. + */ +template <typename Accessor> +inline void InitParticlesForHCSITS::initializeVelocityCorrections(Accessor& ac, size_t body, Vec3& dv, Vec3& dw, real_t dt ) const +{ + dv = ( ac.getInvMass(body) * dt ) * ac.getForce(body); + dw = dt * ( ac.getInvInertia(body) * ac.getTorque(body) ); + + ac.getForceRef(body) = Vec3(); + ac.getTorqueRef(body) = Vec3(); +} +//************************************************************************************************* + + +} //namespace kernel +} //namespace mesa_pd +} //namespace walberla diff --git a/python/mesa_pd/templates/kernel/IntegrateParticlesHCSITS.templ.h b/python/mesa_pd/templates/kernel/IntegrateParticlesHCSITS.templ.h new file mode 100644 index 0000000000000000000000000000000000000000..068b21e14f59d2c9a341a7f4899656dad054ec34 --- /dev/null +++ b/python/mesa_pd/templates/kernel/IntegrateParticlesHCSITS.templ.h @@ -0,0 +1,157 @@ +//====================================================================================================================== +// +// 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 IntegrateParticlesHCSITS.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/ContactStorage.h> +#include <mesa_pd/data/Flags.h> +#include <core/logging/Logging.h> +#include <core/math/Constants.h> + +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { + +/** + * Kernel performing integration of the particles after the HCSITS iteration is done. + * Call this kernel on all particles j to integrate them by a timestep of size dt. + * The Speed Limiter limits the number of body radii, a particle can travel in one timestep. + * The speed limit factor defines a number of radii that are allowed in a timestep, e.g. a factor of 1 means, that + * a particle can only travel one time its radius in each timestep. + * + * \ingroup mesa_pd_kernel + */ +class IntegrateParticlesHCSITS +{ + public: + // Default constructor sets the default values + IntegrateParticlesHCSITS() : + {%- for prop in properties %} + {{prop.name}}_( {{prop.defValue}} ){{ "," if not loop.last}} + {%- endfor %} + {} + + // Getter and Setter Functions + {%- for prop in properties %} + inline const {{prop.type}}& get{{prop.name | capFirst}}() const {return {{prop.name}}_;} + inline void set{{prop.name | capFirst}}({{prop.type}} v) { {{prop.name}}_ = v;} + {% endfor %} + + template <typename PAccessor> + void operator()(size_t j, PAccessor& ac, real_t dt); + + template <typename PAccessor> + void integratePositions(PAccessor& ac, size_t body, Vec3 v, Vec3 w, real_t dt ) const; + + + private: + // List properties + {% for prop in properties %} + {{prop.type}} {{prop.name }}_; + {%- endfor %} +}; + + +template <typename PAccessor> +inline void IntegrateParticlesHCSITS::operator()(size_t j, PAccessor& ac, real_t dt) +{ + using namespace data::particle_flags; + static_assert(std::is_base_of<data::IAccessor, PAccessor>::value, "please provide a valid accessor"); + + auto body_flags = ac.getFlagsRef(j); + if (isSet(body_flags, FIXED)){ + integratePositions(ac, j, ac.getLinearVelocity(j), ac.getAngularVelocity(j), dt ); + }else{ + integratePositions(ac, j, ac.getLinearVelocity(j) + ac.getDv(j), ac.getAngularVelocity(j) + ac.getDw(j), dt ); + } +} + + + + +//************************************************************************************************* +/*!\brief Time integration of the position and orientation of a given body. + * + * \param body The body whose position and orientation to time integrate + * \param v The linear velocity to use for time integration of the position. + * \param w The angular velocity to use for time integration of the orientation. + * \param dt The time step size. + * \return void + * + * Performs an Euler time integration of the positions of the given body. Velocities are damped if + * indicated by the settings and stored back in the body properties. The bounding box is + * recalculated and it is redetermined whether the body is awake or not. Also the data + * structure tracking the contacts attached to the body are cleared and + */ +template <typename PAccessor> +inline void IntegrateParticlesHCSITS::integratePositions(PAccessor& ac, size_t body, Vec3 v, Vec3 w, real_t dt ) const { + using namespace data::particle_flags; + auto body_flags = ac.getFlags(body); + if (isSet(body_flags, FIXED)) + return; + + + if (getSpeedLimiterActive()) { + const auto speed = v.length(); + const auto edge = ac.getInteractionRadius(body); + if (speed * dt > edge * getSpeedLimitFactor()) { + v = v * (edge * getSpeedLimitFactor() / dt / speed); + } + + const real_t maxPhi = real_t(2) * math::pi * getSpeedLimitFactor(); + const real_t phi = w.length() * dt; + if (phi > maxPhi) { + w = w / phi * maxPhi; + } + } + + // Calculating the translational displacement + ac.getPositionRef(body) = (ac.getPosition(body) + v * dt); + + // Calculating the rotation angle + const Vec3 phi(w * dt); + + // Calculating the new orientation + WALBERLA_ASSERT(!math::isnan(phi), " phi: " << phi << " w: " << w << " dt: " << dt ); + ac.getRotationRef(body).rotate(phi, phi.length()); + + // Storing the velocities back in the body properties + ac.getLinearVelocityRef(body) = v; + ac.getAngularVelocityRef(body) = w; +} +//************************************************************************************************* + + + +} //namespace kernel +} //namespace mesa_pd +} //namespace walberla diff --git a/src/blockforest/Initialization.cpp b/src/blockforest/Initialization.cpp index eaf25ffbe3176a7cc1fb4e2b1f7ac15d4ea2e83e..8771996fbe2715a09e1a8f39876ad85403435848 100644 --- a/src/blockforest/Initialization.cpp +++ b/src/blockforest/Initialization.cpp @@ -222,7 +222,7 @@ createBlockForest( const AABB& domainAABB, // if possible, create Cartesian MPI communicator - std::vector< uint_t >* processIdMap = nullptr; + std::vector< uint_t > processIdMap(0); WALBERLA_MPI_SECTION() { @@ -233,13 +233,13 @@ createBlockForest( const AABB& domainAABB, if ( mpiManager->isCartesianCommValid() ) { mpiManager->createCartesianComm( numberOfXProcesses, numberOfYProcesses, numberOfZProcesses, xPeriodic, yPeriodic, zPeriodic ); - processIdMap = new std::vector< uint_t >( 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 ) { - (*processIdMap)[ z * numberOfXProcesses * numberOfYProcesses + y * numberOfXProcesses + x ] = + processIdMap[ z * numberOfXProcesses * numberOfYProcesses + y * numberOfXProcesses + x ] = uint_c( MPIManager::instance()->cartesianRank(x,y,z) ); } } @@ -256,11 +256,9 @@ createBlockForest( const AABB& domainAABB, // calculate process distribution - sforest.balanceLoad( blockforest::CartesianDistribution( numberOfXProcesses, numberOfYProcesses, numberOfZProcesses, processIdMap ), + sforest.balanceLoad( blockforest::CartesianDistribution( numberOfXProcesses, numberOfYProcesses, numberOfZProcesses, &processIdMap ), numberOfXProcesses * numberOfYProcesses * numberOfZProcesses ); - if( processIdMap != nullptr ) delete processIdMap; - // create StructuredBlockForest (encapsulates a newly created BlockForest) return std::make_shared< BlockForest >( uint_c( MPIManager::instance()->rank() ), sforest, keepGlobalBlockInformation ); diff --git a/src/blockforest/SetupBlockForest.cpp b/src/blockforest/SetupBlockForest.cpp index a6c2e9a0a1e8ba22cae9c48d9b24bb8dfe442281..93281d111ead0972ff25c497e750a2f48d5f8a93 100644 --- a/src/blockforest/SetupBlockForest.cpp +++ b/src/blockforest/SetupBlockForest.cpp @@ -713,7 +713,7 @@ void SetupBlockForest::init( const AABB& domain, const uint_t xSize, const uint_ RootBlockAABB rootBlockAABB( domain, rootBlockSize_[0], rootBlockSize_[1], rootBlockSize_[2], xSize, ySize, zSize ); - if( rootBlockExclusionFunctions.size() > 0 ) + if( !rootBlockExclusionFunctions.empty() ) WALBERLA_LOG_PROGRESS( "Initializing SetupBlockForest: Calling root block exclusion callback functions ..." ); for( uint_t i = 0; i != rootBlockExclusionFunctions.size(); ++i ) diff --git a/src/blockforest/loadbalancing/Cartesian.cpp b/src/blockforest/loadbalancing/Cartesian.cpp index 13dbf50b7dba2147d7988683f58a0f45088b9fd4..4645286d55e015d92a79486f45c7dd24279b9390 100644 --- a/src/blockforest/loadbalancing/Cartesian.cpp +++ b/src/blockforest/loadbalancing/Cartesian.cpp @@ -48,7 +48,7 @@ uint_t CartesianDistribution::operator()( SetupBlockForest & forest, const uint_ WALBERLA_ABORT( "Load balancing failed: \'Number of processes in z-direction\' must be in (0," << forest.getZSize() << "]. " "You specified \'" << numberOfZProcesses_ << "\'." ); - if( processIdMap_ != nullptr ) + if( !processIdMap_->empty() ) WALBERLA_CHECK_EQUAL( processIdMap_->size(), numberOfProcesses ); uint_t partitions[3]; @@ -84,7 +84,7 @@ uint_t CartesianDistribution::operator()( SetupBlockForest & forest, const uint_ { const uint_t index = z * partitions[0] * partitions[1] + y * partitions[0] + x; - (*block)->assignTargetProcess( ( processIdMap_ != nullptr ) ? (*processIdMap_)[ index ] : index ); + (*block)->assignTargetProcess( ( !processIdMap_->empty() ) ? (*processIdMap_)[ index ] : index ); } } } diff --git a/src/blockforest/loadbalancing/DynamicParMetis.cpp b/src/blockforest/loadbalancing/DynamicParMetis.cpp index ddc0cbe4cb67a9fc5f252897e1fddc90e7164724..94142b23766556f90fa5545b9414c9410ec52cc1 100644 --- a/src/blockforest/loadbalancing/DynamicParMetis.cpp +++ b/src/blockforest/loadbalancing/DynamicParMetis.cpp @@ -113,7 +113,7 @@ bool DynamicParMetis::operator()( std::vector< std::pair< const PhantomBlock *, MPI_Group allGroup, subGroup; MPI_Comm_group( MPIManager::instance()->comm(), &allGroup ); std::vector<int> ranks; - if (targetProcess.size() > 0) + if (!targetProcess.empty()) ranks.push_back( MPIManager::instance()->rank() ); ranks = mpi::allGatherv( ranks, MPIManager::instance()->comm() ); auto numSubProcesses = ranks.size(); diff --git a/src/core/cell/Cell.h b/src/core/cell/Cell.h index 11fa7d1d1470084c6a6701bb3e9011b771270b22..8fc8ef14e29f216f173604b667807801b49e4f83 100644 --- a/src/core/cell/Cell.h +++ b/src/core/cell/Cell.h @@ -381,3 +381,15 @@ namespace mpi } // namespace walberla + +namespace std +{ + template<> + struct hash< walberla::Cell > + { + std::size_t operator()( walberla::Cell const & cell ) const noexcept + { + return walberla::cell::hash_value( cell ); + } + }; +} // namespace std diff --git a/src/core/config/Config.cpp b/src/core/config/Config.cpp index d850e6c5a3b51c8f6db443cabbe67c6e88e88922..dfd7a683484a605ce9c9b6e66b9752ff2c86d01f 100644 --- a/src/core/config/Config.cpp +++ b/src/core/config/Config.cpp @@ -271,16 +271,16 @@ void Config::parseFromFile( const char* filename, Block& block, unsigned int lev else if( std::getline( input, value, ';' ) && !input.eof() ) { input.ignore( 1 ); - while( (value.find("$(") != value.npos) && (value.find(')') != value.npos) ) { + while( (value.find("$(") != std::string::npos) && (value.find(')') != std::string::npos) ) { size_t s = value.find("$("); size_t e = value.find(')'); ValueReplacementMap::iterator mkey = valueReplacements_.find( value.substr( s+2, e-s+1-3 ) ); if(mkey != valueReplacements_.end()) { value.replace( s,e-s+1, mkey->second ); } else { - if(e!=value.npos) + if(e!=std::string::npos) value.erase(e,1); - if(s!=value.npos) + if(s!=std::string::npos) value.erase(s,2); } } @@ -404,16 +404,16 @@ void Config::extractBlock( const char* filename, std::stringstream& input, Block else if( std::getline( input, value, ';' ) && !input.eof() ) { input.ignore( 1 ); - while( (value.find("$(") != value.npos) && (value.find(')') != value.npos) ) { + while( (value.find("$(") != std::string::npos) && (value.find(')') != std::string::npos) ) { size_t s = value.find("$("); size_t e = value.find(')'); ValueReplacementMap::iterator mkey = valueReplacements_.find( value.substr( s+2, e-s+1-3 ) ); if(mkey != valueReplacements_.end()) { value.replace( s,e-s+1, mkey->second ); } else { - if(e!=value.npos) + if(e!=std::string::npos) value.erase(e,1); - if(s!=value.npos) + if(s!=std::string::npos) value.erase(s,2); } } @@ -428,7 +428,6 @@ void Config::extractBlock( const char* filename, std::stringstream& input, Block error_ << " Missing '}' for " << block.getKey() << " block starting in line " << lineNumber << "\n"; stateFlag_ = false; - return; } //********************************************************************************************************************** @@ -657,7 +656,7 @@ void Config::Block::getWritableBlocks( const std::string & key, std::vector<Bloc // \param value The value of the new parameter. // \return \a true if a new parameter was added, \a false if duplicate key is present. */ -bool Config::Block::addParameter( std::string key, const std::string& value ) +bool Config::Block::addParameter( const std::string& key, const std::string& value ) { return params_.insert( std::pair<Key,Value>(key,value) ).second; } diff --git a/src/core/config/Config.h b/src/core/config/Config.h index 88ef3ae20d9b0761ebb9e87b10bd76cbd2305efe..9837b870c7f0151aad400f509958c69d5290f171 100644 --- a/src/core/config/Config.h +++ b/src/core/config/Config.h @@ -225,7 +225,7 @@ public: //**Utility functions********************************************************************************************* /*! \name Utility functions */ //@{ - bool addParameter( std::string key, const std::string& value ); + bool addParameter( const std::string& key, const std::string& value ); void listParameters() const; //@} //**************************************************************************************************************** diff --git a/src/core/config/Create.cpp b/src/core/config/Create.cpp index 6bc78a70209c9aedb69d825531677fa3c3f5bbc0..1d3c7296540931f88508712b6dc58abded97bea1 100644 --- a/src/core/config/Create.cpp +++ b/src/core/config/Create.cpp @@ -83,7 +83,7 @@ namespace config { void createFromTextFile( Config & config, const std::string & pathToTextFile ) { config.readParameterFile( pathToTextFile.c_str() ); - if (config.error() != "" ) + if ( !config.error().empty() ) throw std::runtime_error( "FileReader returned an error reading the parameter file: \n" + config.error() ); } @@ -111,7 +111,7 @@ namespace config { vector< string > equalitySignSplitResult = string_split( *param, "=" ); std::string value; - if ( equalitySignSplitResult.size() == 0 ) + if ( equalitySignSplitResult.empty() ) { WALBERLA_LOG_WARNING( "Ignoring empty parameter"); continue; diff --git a/src/core/math/Parser.cpp b/src/core/math/Parser.cpp index a2d48044392490a797629e47ade6a4fc32ccc760..b3a4575677d319efd1cd2d198cbcedd228bb561e 100644 --- a/src/core/math/Parser.cpp +++ b/src/core/math/Parser.cpp @@ -111,7 +111,7 @@ void FunctionParser::parse( const std::string & eq ) // check if this expression evaluates to constant zero std::vector< std::string > variables; symbolTable_->get_variable_list( variables ); - isConstant_ = (variables.size() == 0); + isConstant_ = (variables.empty()); if (isConstant_) { const double value = evaluate(std::map<std::string, double>()); diff --git a/src/core/math/PhysicalCheck.cpp b/src/core/math/PhysicalCheck.cpp index 4800f192929bbcd0d60aa353e8955fae0f38d7ad..e6a3c26e8cf8c75b2d182a1323cb6b9640d40bb6 100644 --- a/src/core/math/PhysicalCheck.cpp +++ b/src/core/math/PhysicalCheck.cpp @@ -194,7 +194,7 @@ namespace math { if( i->second < 0 ) { - if( denom.str().size() > 0 ) + if( !denom.str().empty() ) denom << " * "; denom << i->first; } @@ -209,16 +209,16 @@ namespace math { num << " ^ " << i->second; } - if( num.str().size() == 0 && denom.str().size() == 0 ) + if( num.str().empty() && denom.str().empty() ) return std::string(); - if( denom.str().size() == 0 ) + if( denom.str().empty() ) { num << " *"; return num.str(); } - if( num.str().size() == 0 ) + if( num.str().empty() ) num << " * 1"; num << " / ( "; @@ -321,7 +321,7 @@ namespace math { if( !isDefined(varName) ) { WALBERLA_ABORT( "Error in PhysicalCheck::getVarUnit(). Variable not found: " << varName ); - return nullptr; + return std::string(); } std::stringstream num, denom; @@ -341,13 +341,13 @@ namespace math { num << '^' << i->second; } - if( num.str().size() == 0 && denom.str().size() == 0 ) + if( num.str().empty() && denom.str().empty() ) return std::string(); - if( denom.str().size() == 0 ) + if( denom.str().empty() ) return num.str(); - if( num.str().size() == 0 ) + if( num.str().empty() ) num << 1; num << '/'; diff --git a/src/core/math/Vector2.h b/src/core/math/Vector2.h index b62d58f4651a743e33a0b9c6f0e4f7c860d174dd..b0decf8ec0a2953fc2a2c3ab3a9e8c619de99e60 100644 --- a/src/core/math/Vector2.h +++ b/src/core/math/Vector2.h @@ -1689,3 +1689,15 @@ namespace walberla { }; } // namespace walberla + +namespace std +{ + template<typename T> + struct hash< walberla::Vector2<T> > + { + std::size_t operator()( walberla::Vector2<T> const & v ) const noexcept + { + return walberla::Vector2<T>::hash_value( v ); + } + }; +} // namespace std diff --git a/src/core/math/Vector3.h b/src/core/math/Vector3.h index 267e90c38f7b9752820a8397d7729c36134e3c96..7bc9e87af1f534aa694f1c8302dd4b9124b15a2b 100644 --- a/src/core/math/Vector3.h +++ b/src/core/math/Vector3.h @@ -1969,3 +1969,15 @@ inline bool check_float_equal_eps( const math::Vector3<real_t> & lhs, const math } } } + +namespace std +{ + template<typename T> + struct hash< walberla::Vector3<T> > + { + std::size_t operator()( walberla::Vector3<T> const & v ) const noexcept + { + return walberla::Vector3<T>::hash_value( v ); + } + }; +} // namespace std diff --git a/src/core/math/equation_system/EquationSystem.cpp b/src/core/math/equation_system/EquationSystem.cpp index 433a1283c2f9e6350c80184ff36af6279f1ed611..30a9394cfe22e7e7b4f117525ee47b3e00e1519a 100644 --- a/src/core/math/equation_system/EquationSystem.cpp +++ b/src/core/math/equation_system/EquationSystem.cpp @@ -171,7 +171,7 @@ size_t EquationSystem::getNumberOfEquations() const return eqMap_.size(); } -const std::string EquationSystem::writeEquations() const +std::string EquationSystem::writeEquations() const { std::stringstream ss; ss << "Equations to solve:" << std::endl; @@ -182,7 +182,7 @@ const std::string EquationSystem::writeEquations() const return ss.str(); } -const std::string EquationSystem::writeVariables() const +std::string EquationSystem::writeVariables() const { std::stringstream ss; ss << "Solution for each variable:" << std::endl; diff --git a/src/core/math/equation_system/EquationSystem.h b/src/core/math/equation_system/EquationSystem.h index 56a15bf8096b58fbd26dc007d7fb6522c4232166..e78df0345321a0e51a372941b0a96b46bfd88e29 100644 --- a/src/core/math/equation_system/EquationSystem.h +++ b/src/core/math/equation_system/EquationSystem.h @@ -100,8 +100,8 @@ namespace math { //**Output functions************************************************************************* /*! \name Output functions */ //@{ - const std::string writeEquations() const; - const std::string writeVariables() const; + std::string writeEquations() const; + std::string writeVariables() const; friend std::ostream& operator<<( std::ostream& os, EquationSystem& es ); //@} //**************************************************************************************************************** diff --git a/src/core/math/extern/exprtk.h b/src/core/math/extern/exprtk.h index f55657a617e1d7815b9f154fd073dcf843eed2ac..4c88bbdf6b501ccd95726cab45b0000597e49e49 100644 --- a/src/core/math/extern/exprtk.h +++ b/src/core/math/extern/exprtk.h @@ -2,7 +2,7 @@ ****************************************************************** * C++ Mathematical Expression Toolkit Library * * * - * Author: Arash Partow (1999-2018) * + * Author: Arash Partow (1999-2019) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * @@ -580,46 +580,64 @@ namespace exprtk template <typename Iterator, typename Compare> inline bool match_impl(const Iterator pattern_begin, - const Iterator pattern_end, - const Iterator data_begin, - const Iterator data_end, + const Iterator pattern_end , + const Iterator data_begin , + const Iterator data_end , const typename std::iterator_traits<Iterator>::value_type& zero_or_more, - const typename std::iterator_traits<Iterator>::value_type& zero_or_one) + const typename std::iterator_traits<Iterator>::value_type& zero_or_one ) { - Iterator d_itr = data_begin; - Iterator p_itr = pattern_begin; + const Iterator null_itr(0); - while ((p_itr != pattern_end) && (d_itr != data_end)) + Iterator d_itr = data_begin; + Iterator p_itr = pattern_begin; + Iterator tb_p_itr = null_itr; + Iterator tb_d_itr = null_itr; + + while (d_itr != data_end) { if (zero_or_more == *p_itr) { - while ((p_itr != pattern_end) && (*p_itr == zero_or_more || *p_itr == zero_or_one)) + while ((pattern_end != p_itr) && ((zero_or_more == *p_itr) || (zero_or_one == *p_itr))) { ++p_itr; } - if (p_itr == pattern_end) + if (pattern_end == p_itr) return true; - const typename std::iterator_traits<Iterator>::value_type c = *(p_itr++); + const typename std::iterator_traits<Iterator>::value_type c = *(p_itr); - while ((d_itr != data_end) && !Compare::cmp(c,*d_itr)) + while ((data_end != d_itr) && !Compare::cmp(c,*d_itr)) { ++d_itr; } - ++d_itr; + tb_p_itr = p_itr; + tb_d_itr = d_itr; + + continue; } - else if ((*p_itr == zero_or_one) || Compare::cmp(*p_itr, *d_itr)) + else if (!Compare::cmp(*p_itr, *d_itr) && (zero_or_one != *p_itr)) { - ++d_itr; - ++p_itr; + if (null_itr == tb_d_itr) + return false; + + d_itr = tb_d_itr++; + p_itr = tb_p_itr; + + continue; } - else - return false; + + ++p_itr; + ++d_itr; } - return (d_itr == data_end) && (p_itr == pattern_end); + while ((pattern_end != p_itr) && ((zero_or_more == *p_itr) || (zero_or_one == *p_itr))) + { + ++p_itr; + } + + return (pattern_end == p_itr); } inline bool wc_match(const std::string& wild_card, @@ -1828,7 +1846,7 @@ namespace exprtk bool instate = false; - static const char zero = static_cast<uchar_t>('0'); + static const char_t zero = static_cast<uchar_t>('0'); #define parse_digit_1(d) \ if ((digit = (*itr - zero)) < 10) \ @@ -2305,8 +2323,8 @@ namespace exprtk inline std::string substr(const std::size_t& begin, const std::size_t& end) { - details::char_cptr begin_itr = ((base_itr_ + begin) < s_end_) ? (base_itr_ + begin) : s_end_; - details::char_cptr end_itr = ((base_itr_ + end) < s_end_) ? (base_itr_ + end) : s_end_; + const details::char_cptr begin_itr = ((base_itr_ + begin) < s_end_) ? (base_itr_ + begin) : s_end_; + const details::char_cptr end_itr = ((base_itr_ + end) < s_end_) ? (base_itr_ + end) : s_end_; return std::string(begin_itr,end_itr); } @@ -3099,7 +3117,7 @@ namespace exprtk std::size_t changes = 0; - for (std::size_t i = 0; i < (g.token_list_.size() - 1); ++i) + for (int i = 0; i < static_cast<int>(g.token_list_.size() - 1); ++i) { token t; @@ -3110,6 +3128,9 @@ namespace exprtk g.token_list_.erase(g.token_list_.begin() + static_cast<diff_t>(i + 1)); ++changes; + + if (static_cast<std::size_t>(i + 1) >= g.token_list_.size()) + break; } } @@ -3125,7 +3146,7 @@ namespace exprtk std::size_t changes = 0; - for (std::size_t i = 0; i < (g.token_list_.size() - 2); ++i) + for (int i = 0; i < static_cast<int>(g.token_list_.size() - 2); ++i) { token t; @@ -3136,6 +3157,9 @@ namespace exprtk g.token_list_.erase(g.token_list_.begin() + static_cast<diff_t>(i + 1), g.token_list_.begin() + static_cast<diff_t>(i + 3)); ++changes; + + if (static_cast<std::size_t>(i + 2) >= g.token_list_.size()) + break; } } @@ -3213,6 +3237,7 @@ namespace exprtk else if ((t0.type == lexer::token::e_rbracket ) && (t1.type == lexer::token::e_symbol )) match = true; else if ((t0.type == lexer::token::e_rcrlbracket) && (t1.type == lexer::token::e_symbol )) match = true; else if ((t0.type == lexer::token::e_rsqrbracket) && (t1.type == lexer::token::e_symbol )) match = true; + else if ((t0.type == lexer::token::e_symbol ) && (t1.type == lexer::token::e_symbol )) match = true; return (match) ? 1 : -1; } @@ -3655,7 +3680,7 @@ namespace exprtk bool operator() (const lexer::token& t0, const lexer::token& t1) { - set_t::value_type p = std::make_pair(t0.type,t1.type); + const set_t::value_type p = std::make_pair(t0.type,t1.type); if (invalid_bracket_check(t0.type,t1.type)) { @@ -3669,7 +3694,7 @@ namespace exprtk return true; } - std::size_t error_count() + std::size_t error_count() const { return error_list_.size(); } @@ -3781,6 +3806,91 @@ namespace exprtk std::vector<std::pair<lexer::token,lexer::token> > error_list_; }; + class sequence_validator_3tokens : public lexer::token_scanner + { + private: + + typedef lexer::token::token_type token_t; + typedef std::pair<token_t,std::pair<token_t,token_t> > token_triplet_t; + typedef std::set<token_triplet_t> set_t; + + public: + + using lexer::token_scanner::operator(); + + sequence_validator_3tokens() + : lexer::token_scanner(3) + { + add_invalid(lexer::token::e_number , lexer::token::e_number , lexer::token::e_number); + add_invalid(lexer::token::e_string , lexer::token::e_string , lexer::token::e_string); + add_invalid(lexer::token::e_comma , lexer::token::e_comma , lexer::token::e_comma ); + + add_invalid(lexer::token::e_add ,lexer::token::e_add , lexer::token::e_add); + add_invalid(lexer::token::e_sub ,lexer::token::e_sub , lexer::token::e_sub); + add_invalid(lexer::token::e_div ,lexer::token::e_div , lexer::token::e_div); + add_invalid(lexer::token::e_mul ,lexer::token::e_mul , lexer::token::e_mul); + add_invalid(lexer::token::e_mod ,lexer::token::e_mod , lexer::token::e_mod); + add_invalid(lexer::token::e_pow ,lexer::token::e_pow , lexer::token::e_pow); + + add_invalid(lexer::token::e_add ,lexer::token::e_sub , lexer::token::e_add); + add_invalid(lexer::token::e_sub ,lexer::token::e_add , lexer::token::e_sub); + add_invalid(lexer::token::e_div ,lexer::token::e_mul , lexer::token::e_div); + add_invalid(lexer::token::e_mul ,lexer::token::e_div , lexer::token::e_mul); + add_invalid(lexer::token::e_mod ,lexer::token::e_pow , lexer::token::e_mod); + add_invalid(lexer::token::e_pow ,lexer::token::e_mod , lexer::token::e_pow); + } + + bool result() + { + return error_list_.empty(); + } + + bool operator() (const lexer::token& t0, const lexer::token& t1, const lexer::token& t2) + { + const set_t::value_type p = std::make_pair(t0.type,std::make_pair(t1.type,t2.type)); + + if (invalid_comb_.find(p) != invalid_comb_.end()) + { + error_list_.push_back(std::make_pair(t0,t1)); + } + + return true; + } + + std::size_t error_count() const + { + return error_list_.size(); + } + + std::pair<lexer::token,lexer::token> error(const std::size_t index) + { + if (index < error_list_.size()) + { + return error_list_[index]; + } + else + { + static const lexer::token error_token; + return std::make_pair(error_token,error_token); + } + } + + void clear_errors() + { + error_list_.clear(); + } + + private: + + void add_invalid(token_t t0, token_t t1, token_t t2) + { + invalid_comb_.insert(std::make_pair(t0,std::make_pair(t1,t2))); + } + + set_t invalid_comb_; + std::vector<std::pair<lexer::token,lexer::token> > error_list_; + }; + struct helper_assembly { inline bool register_scanner(lexer::token_scanner* scanner) @@ -4035,40 +4145,6 @@ namespace exprtk return true; } - inline bool token_is_then_assign(const token_t::token_type& ttype, - std::string& token, - const token_advance_mode mode = e_advance) - { - if (current_token_.type != ttype) - { - return false; - } - - token = current_token_.value; - - advance_token(mode); - - return true; - } - - template <typename Allocator, - template <typename,typename> class Container> - inline bool token_is_then_assign(const token_t::token_type& ttype, - Container<std::string,Allocator>& token_list, - const token_advance_mode mode = e_advance) - { - if (current_token_.type != ttype) - { - return false; - } - - token_list.push_back(current_token_.value); - - advance_token(mode); - - return true; - } - inline bool peek_token_is(const token_t::token_type& ttype) { return (lexer_.peek_next_token().type == ttype); @@ -4179,13 +4255,18 @@ namespace exprtk }; type_store() - : size(0), - data(0), + : data(0), + size(0), type(e_unknown) {} + union + { + void* data; + T* vec_data; + }; + std::size_t size; - void* data; store_type type; class parameter_list @@ -4585,16 +4666,16 @@ namespace exprtk {} control_block(const std::size_t& dsize) - : ref_count(1), + : ref_count(1 ), size (dsize), - data (0), - destruct (true) + data (0 ), + destruct (true ) { create_data(); } control_block(const std::size_t& dsize, data_t dptr, bool dstrct = false) - : ref_count(1), - size (dsize), - data (dptr ), + : ref_count(1 ), + size (dsize ), + data (dptr ), destruct (dstrct) {} @@ -4757,8 +4838,8 @@ namespace exprtk static inline std::size_t min_size(control_block* cb0, control_block* cb1) { - std::size_t size0 = cb0->size; - std::size_t size1 = cb1->size; + const std::size_t size0 = cb0->size; + const std::size_t size1 = cb1->size; if (size0 && size1) return std::min(size0,size1); @@ -4845,19 +4926,19 @@ namespace exprtk case e_ne : return std::not_equal_to<T>()(arg0,arg1) ? T(1) : T(0); case e_gte : return (arg0 >= arg1) ? T(1) : T(0); case e_gt : return (arg0 > arg1) ? T(1) : T(0); - case e_and : return and_opr<T> (arg0,arg1); + case e_and : return and_opr <T>(arg0,arg1); case e_nand : return nand_opr<T>(arg0,arg1); - case e_or : return or_opr<T> (arg0,arg1); - case e_nor : return nor_opr<T> (arg0,arg1); - case e_xor : return xor_opr<T> (arg0,arg1); + case e_or : return or_opr <T>(arg0,arg1); + case e_nor : return nor_opr <T>(arg0,arg1); + case e_xor : return xor_opr <T>(arg0,arg1); case e_xnor : return xnor_opr<T>(arg0,arg1); - case e_root : return root<T> (arg0,arg1); - case e_roundn : return roundn<T> (arg0,arg1); + case e_root : return root <T>(arg0,arg1); + case e_roundn : return roundn <T>(arg0,arg1); case e_equal : return equal (arg0,arg1); case e_nequal : return nequal (arg0,arg1); - case e_hypot : return hypot<T> (arg0,arg1); - case e_shr : return shr<T> (arg0,arg1); - case e_shl : return shl<T> (arg0,arg1); + case e_hypot : return hypot <T>(arg0,arg1); + case e_shr : return shr <T>(arg0,arg1); + case e_shl : return shl <T>(arg0,arg1); default : exprtk_debug(("numeric::details::process_impl<T> - Invalid binary operation.\n")); return std::numeric_limits<T>::quiet_NaN(); @@ -5179,7 +5260,7 @@ namespace exprtk template <typename T, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline bool all_nodes_valid(const Sequence<expression_node<T>*,Allocator>& b) { for (std::size_t i = 0; i < b.size(); ++i) @@ -5206,7 +5287,7 @@ namespace exprtk template <typename T, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline bool all_nodes_variables(Sequence<expression_node<T>*,Allocator>& b) { for (std::size_t i = 0; i < b.size(); ++i) @@ -5232,7 +5313,7 @@ namespace exprtk template <typename NodeAllocator, typename T, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline void free_all_nodes(NodeAllocator& node_allocator, Sequence<expression_node<T>*,Allocator>& b) { for (std::size_t i = 0; i < b.size(); ++i) @@ -5342,7 +5423,7 @@ namespace exprtk }; template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> class sequence_vector_impl : public vector_holder_base { public: @@ -5790,7 +5871,7 @@ namespace exprtk template <typename T, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> static inline void execute(Sequence<std::pair<expression_node<T>*,bool>,Allocator>& branch) { for (std::size_t i = 0; i < branch.size(); ++i) @@ -6618,7 +6699,7 @@ namespace exprtk typedef expression_node<T>* expression_ptr; template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> switch_node(const Sequence<expression_ptr,Allocator>& arg_list) { if (1 != (arg_list.size() & 1)) @@ -6696,7 +6777,7 @@ namespace exprtk typedef expression_node<T>* expression_ptr; template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> switch_n_node(const Sequence<expression_ptr,Allocator>& arg_list) : switch_node<T>(arg_list) {} @@ -6715,7 +6796,7 @@ namespace exprtk typedef expression_node<T>* expression_ptr; template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> multi_switch_node(const Sequence<expression_ptr,Allocator>& arg_list) { if (0 != (arg_list.size() & 1)) @@ -8677,7 +8758,7 @@ namespace exprtk range_t& range = str0_range_ptr_->range_ref(); - if (range(r0,r1,str0_base_ptr_->size())) + if (range(r0, r1, str0_base_ptr_->size())) { const std::size_t size = (r1 - r0) + 1; @@ -8695,7 +8776,7 @@ namespace exprtk range_t& range = str1_range_ptr_->range_ref(); - if (range(r0,r1,str1_base_ptr_->size())) + if (range(r0, r1, str1_base_ptr_->size())) { const std::size_t size = (r1 - r0) + 1; @@ -8815,7 +8896,7 @@ namespace exprtk std::size_t r0 = 0; std::size_t r1 = 0; - if (range(r0,r1,str0_base_ptr_->size())) + if (range(r0, r1, str0_base_ptr_->size())) { const std::size_t size = (r1 - r0) + 1; @@ -8889,7 +8970,7 @@ namespace exprtk typedef irange_t* irange_ptr; template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> str_vararg_node(const Sequence<expression_ptr,Allocator>& arg_list) : final_node_(arg_list.back()), final_deletable_(branch_deletable(final_node_)), @@ -9355,7 +9436,7 @@ namespace exprtk typedef expression_node<T>* expression_ptr; template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> vararg_node(const Sequence<expression_ptr,Allocator>& arg_list) { arg_list_ .resize(arg_list.size()); @@ -9415,7 +9496,7 @@ namespace exprtk typedef expression_node<T>* expression_ptr; template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> vararg_varnode(const Sequence<expression_ptr,Allocator>& arg_list) { arg_list_.resize(arg_list.size()); @@ -11486,6 +11567,7 @@ namespace exprtk ts.size = vi->size(); ts.data = vi->vds().data(); ts.type = type_store_t::e_vector; + vi->vec()->vec_holder().set_ref(&ts.vec_data); } #ifndef exprtk_disable_string_capabilities else if (is_generally_string_node(arg_list_[i])) @@ -11651,16 +11733,14 @@ namespace exprtk inline T value() const { - T result = std::numeric_limits<T>::quiet_NaN(); - if (gen_function_t::function_) { if (gen_function_t::populate_value_list()) { typedef typename StringFunction::parameter_list_t parameter_list_t; - result = (*gen_function_t::function_)(ret_string_, - parameter_list_t(gen_function_t::typestore_list_)); + const T result = (*gen_function_t::function_) + (ret_string_, parameter_list_t(gen_function_t::typestore_list_)); range_.n1_c.second = ret_string_.size() - 1; range_.cache.second = range_.n1_c.second; @@ -11669,7 +11749,7 @@ namespace exprtk } } - return result; + return std::numeric_limits<T>::quiet_NaN(); } inline typename expression_node<T>::node_type type() const @@ -11726,8 +11806,6 @@ namespace exprtk inline T value() const { - T result = std::numeric_limits<T>::quiet_NaN(); - if (gen_function_t::function_) { if (gen_function_t::populate_value_list()) @@ -11739,7 +11817,7 @@ namespace exprtk } } - return result; + return std::numeric_limits<T>::quiet_NaN(); } inline typename expression_node<T>::node_type type() const @@ -11770,17 +11848,15 @@ namespace exprtk inline T value() const { - T result = std::numeric_limits<T>::quiet_NaN(); - if (str_function_t::function_) { if (str_function_t::populate_value_list()) { typedef typename StringFunction::parameter_list_t parameter_list_t; - result = (*str_function_t::function_)(param_seq_index_, - str_function_t::ret_string_, - parameter_list_t(str_function_t::typestore_list_)); + const T result = (*str_function_t::function_)(param_seq_index_, + str_function_t::ret_string_, + parameter_list_t(str_function_t::typestore_list_)); str_function_t::range_.n1_c.second = str_function_t::ret_string_.size() - 1; str_function_t::range_.cache.second = str_function_t::range_.n1_c.second; @@ -11789,7 +11865,7 @@ namespace exprtk } } - return result; + return std::numeric_limits<T>::quiet_NaN(); } inline typename expression_node<T>::node_type type() const @@ -11994,20 +12070,21 @@ namespace exprtk template <typename T> struct opr_base { - typedef typename details::functor_t<T>::Type Type; + typedef typename details::functor_t<T>::Type Type; typedef typename details::functor_t<T>::RefType RefType; - typedef typename details::functor_t<T> functor_t; - typedef typename functor_t::qfunc_t quaternary_functor_t; - typedef typename functor_t::tfunc_t trinary_functor_t; - typedef typename functor_t::bfunc_t binary_functor_t; - typedef typename functor_t::ufunc_t unary_functor_t; + typedef typename details::functor_t<T> functor_t; + typedef typename functor_t::qfunc_t quaternary_functor_t; + typedef typename functor_t::tfunc_t trinary_functor_t; + typedef typename functor_t::bfunc_t binary_functor_t; + typedef typename functor_t::ufunc_t unary_functor_t; }; template <typename T> struct add_op : public opr_base<T> { - typedef typename opr_base<T>::Type Type; + typedef typename opr_base<T>::Type Type; typedef typename opr_base<T>::RefType RefType; + static inline T process(Type t1, Type t2) { return t1 + t2; } static inline T process(Type t1, Type t2, Type t3) { return t1 + t2 + t3; } static inline void assign(RefType t1, Type t2) { t1 += t2; } @@ -12018,8 +12095,9 @@ namespace exprtk template <typename T> struct mul_op : public opr_base<T> { - typedef typename opr_base<T>::Type Type; + typedef typename opr_base<T>::Type Type; typedef typename opr_base<T>::RefType RefType; + static inline T process(Type t1, Type t2) { return t1 * t2; } static inline T process(Type t1, Type t2, Type t3) { return t1 * t2 * t3; } static inline void assign(RefType t1, Type t2) { t1 *= t2; } @@ -12030,8 +12108,9 @@ namespace exprtk template <typename T> struct sub_op : public opr_base<T> { - typedef typename opr_base<T>::Type Type; + typedef typename opr_base<T>::Type Type; typedef typename opr_base<T>::RefType RefType; + static inline T process(Type t1, Type t2) { return t1 - t2; } static inline T process(Type t1, Type t2, Type t3) { return t1 - t2 - t3; } static inline void assign(RefType t1, Type t2) { t1 -= t2; } @@ -12042,8 +12121,9 @@ namespace exprtk template <typename T> struct div_op : public opr_base<T> { - typedef typename opr_base<T>::Type Type; + typedef typename opr_base<T>::Type Type; typedef typename opr_base<T>::RefType RefType; + static inline T process(Type t1, Type t2) { return t1 / t2; } static inline T process(Type t1, Type t2, Type t3) { return t1 / t2 / t3; } static inline void assign(RefType t1, Type t2) { t1 /= t2; } @@ -12054,8 +12134,9 @@ namespace exprtk template <typename T> struct mod_op : public opr_base<T> { - typedef typename opr_base<T>::Type Type; + typedef typename opr_base<T>::Type Type; typedef typename opr_base<T>::RefType RefType; + static inline T process(Type t1, Type t2) { return numeric::modulus<T>(t1,t2); } static inline void assign(RefType t1, Type t2) { t1 = numeric::modulus<T>(t1,t2); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_mod; } @@ -12065,8 +12146,9 @@ namespace exprtk template <typename T> struct pow_op : public opr_base<T> { - typedef typename opr_base<T>::Type Type; + typedef typename opr_base<T>::Type Type; typedef typename opr_base<T>::RefType RefType; + static inline T process(Type t1, Type t2) { return numeric::pow<T>(t1,t2); } static inline void assign(RefType t1, Type t2) { t1 = numeric::pow<T>(t1,t2); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_pow; } @@ -12077,6 +12159,7 @@ namespace exprtk struct lt_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return ((t1 < t2) ? T(1) : T(0)); } static inline T process(const std::string& t1, const std::string& t2) { return ((t1 < t2) ? T(1) : T(0)); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_lt; } @@ -12087,6 +12170,7 @@ namespace exprtk struct lte_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return ((t1 <= t2) ? T(1) : T(0)); } static inline T process(const std::string& t1, const std::string& t2) { return ((t1 <= t2) ? T(1) : T(0)); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_lte; } @@ -12097,6 +12181,7 @@ namespace exprtk struct gt_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return ((t1 > t2) ? T(1) : T(0)); } static inline T process(const std::string& t1, const std::string& t2) { return ((t1 > t2) ? T(1) : T(0)); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_gt; } @@ -12107,6 +12192,7 @@ namespace exprtk struct gte_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return ((t1 >= t2) ? T(1) : T(0)); } static inline T process(const std::string& t1, const std::string& t2) { return ((t1 >= t2) ? T(1) : T(0)); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_gte; } @@ -12127,6 +12213,7 @@ namespace exprtk struct equal_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return numeric::equal(t1,t2); } static inline T process(const std::string& t1, const std::string& t2) { return ((t1 == t2) ? T(1) : T(0)); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_eq; } @@ -12137,6 +12224,7 @@ namespace exprtk struct ne_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return (std::not_equal_to<T>()(t1,t2) ? T(1) : T(0)); } static inline T process(const std::string& t1, const std::string& t2) { return ((t1 != t2) ? T(1) : T(0)); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_ne; } @@ -12147,6 +12235,7 @@ namespace exprtk struct and_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return (details::is_true(t1) && details::is_true(t2)) ? T(1) : T(0); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_and; } static inline details::operator_type operation() { return details::e_and; } @@ -12156,6 +12245,7 @@ namespace exprtk struct nand_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return (details::is_true(t1) && details::is_true(t2)) ? T(0) : T(1); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_nand; } static inline details::operator_type operation() { return details::e_nand; } @@ -12165,6 +12255,7 @@ namespace exprtk struct or_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return (details::is_true(t1) || details::is_true(t2)) ? T(1) : T(0); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_or; } static inline details::operator_type operation() { return details::e_or; } @@ -12174,6 +12265,7 @@ namespace exprtk struct nor_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return (details::is_true(t1) || details::is_true(t2)) ? T(0) : T(1); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_nor; } static inline details::operator_type operation() { return details::e_nor; } @@ -12183,6 +12275,7 @@ namespace exprtk struct xor_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return numeric::xor_opr<T>(t1,t2); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_nor; } static inline details::operator_type operation() { return details::e_xor; } @@ -12192,6 +12285,7 @@ namespace exprtk struct xnor_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(Type t1, Type t2) { return numeric::xnor_opr<T>(t1,t2); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_nor; } static inline details::operator_type operation() { return details::e_xnor; } @@ -12201,6 +12295,7 @@ namespace exprtk struct in_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(const T&, const T&) { return std::numeric_limits<T>::quiet_NaN(); } static inline T process(const std::string& t1, const std::string& t2) { return ((std::string::npos != t2.find(t1)) ? T(1) : T(0)); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_in; } @@ -12211,6 +12306,7 @@ namespace exprtk struct like_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(const T&, const T&) { return std::numeric_limits<T>::quiet_NaN(); } static inline T process(const std::string& t1, const std::string& t2) { return (details::wc_match(t2,t1) ? T(1) : T(0)); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_like; } @@ -12221,6 +12317,7 @@ namespace exprtk struct ilike_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(const T&, const T&) { return std::numeric_limits<T>::quiet_NaN(); } static inline T process(const std::string& t1, const std::string& t2) { return (details::wc_imatch(t2,t1) ? T(1) : T(0)); } static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_ilike; } @@ -12231,6 +12328,7 @@ namespace exprtk struct inrange_op : public opr_base<T> { typedef typename opr_base<T>::Type Type; + static inline T process(const T& t0, const T& t1, const T& t2) { return ((t0 <= t1) && (t1 <= t2)) ? T(1) : T(0); } static inline T process(const std::string& t0, const std::string& t1, const std::string& t2) { @@ -12259,7 +12357,7 @@ namespace exprtk template <typename Type, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> static inline T process(const Sequence<Type,Allocator>& arg_list) { switch (arg_list.size()) @@ -12300,14 +12398,14 @@ namespace exprtk static inline T process_3(const Sequence& arg_list) { return value(arg_list[0]) + value(arg_list[1]) + - value(arg_list[2]); + value(arg_list[2]) ; } template <typename Sequence> static inline T process_4(const Sequence& arg_list) { return value(arg_list[0]) + value(arg_list[1]) + - value(arg_list[2]) + value(arg_list[3]); + value(arg_list[2]) + value(arg_list[3]) ; } template <typename Sequence> @@ -12315,7 +12413,7 @@ namespace exprtk { return value(arg_list[0]) + value(arg_list[1]) + value(arg_list[2]) + value(arg_list[3]) + - value(arg_list[4]); + value(arg_list[4]) ; } }; @@ -12326,7 +12424,7 @@ namespace exprtk template <typename Type, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> static inline T process(const Sequence<Type,Allocator>& arg_list) { switch (arg_list.size()) @@ -12367,14 +12465,14 @@ namespace exprtk static inline T process_3(const Sequence& arg_list) { return value(arg_list[0]) * value(arg_list[1]) * - value(arg_list[2]); + value(arg_list[2]) ; } template <typename Sequence> static inline T process_4(const Sequence& arg_list) { return value(arg_list[0]) * value(arg_list[1]) * - value(arg_list[2]) * value(arg_list[3]); + value(arg_list[2]) * value(arg_list[3]) ; } template <typename Sequence> @@ -12382,7 +12480,7 @@ namespace exprtk { return value(arg_list[0]) * value(arg_list[1]) * value(arg_list[2]) * value(arg_list[3]) * - value(arg_list[4]); + value(arg_list[4]) ; } }; @@ -12393,7 +12491,7 @@ namespace exprtk template <typename Type, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> static inline T process(const Sequence<Type,Allocator>& arg_list) { switch (arg_list.size()) @@ -12449,7 +12547,7 @@ namespace exprtk template <typename Type, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> static inline T process(const Sequence<Type,Allocator>& arg_list) { switch (arg_list.size()) @@ -12520,7 +12618,7 @@ namespace exprtk template <typename Type, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> static inline T process(const Sequence<Type,Allocator>& arg_list) { switch (arg_list.size()) @@ -12591,7 +12689,7 @@ namespace exprtk template <typename Type, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> static inline T process(const Sequence<Type,Allocator>& arg_list) { switch (arg_list.size()) @@ -12605,7 +12703,7 @@ namespace exprtk { for (std::size_t i = 0; i < arg_list.size(); ++i) { - if (std::equal_to<T>()(T(0),value(arg_list[i]))) + if (std::equal_to<T>()(T(0), value(arg_list[i]))) return T(0); } @@ -12618,15 +12716,15 @@ namespace exprtk static inline T process_1(const Sequence& arg_list) { return std::not_equal_to<T>() - (T(0),value(arg_list[0])) ? T(1) : T(0); + (T(0), value(arg_list[0])) ? T(1) : T(0); } template <typename Sequence> static inline T process_2(const Sequence& arg_list) { return ( - std::not_equal_to<T>()(T(0),value(arg_list[0])) && - std::not_equal_to<T>()(T(0),value(arg_list[1])) + std::not_equal_to<T>()(T(0), value(arg_list[0])) && + std::not_equal_to<T>()(T(0), value(arg_list[1])) ) ? T(1) : T(0); } @@ -12634,9 +12732,9 @@ namespace exprtk static inline T process_3(const Sequence& arg_list) { return ( - std::not_equal_to<T>()(T(0),value(arg_list[0])) && - std::not_equal_to<T>()(T(0),value(arg_list[1])) && - std::not_equal_to<T>()(T(0),value(arg_list[2])) + std::not_equal_to<T>()(T(0), value(arg_list[0])) && + std::not_equal_to<T>()(T(0), value(arg_list[1])) && + std::not_equal_to<T>()(T(0), value(arg_list[2])) ) ? T(1) : T(0); } @@ -12644,10 +12742,10 @@ namespace exprtk static inline T process_4(const Sequence& arg_list) { return ( - std::not_equal_to<T>()(T(0),value(arg_list[0])) && - std::not_equal_to<T>()(T(0),value(arg_list[1])) && - std::not_equal_to<T>()(T(0),value(arg_list[2])) && - std::not_equal_to<T>()(T(0),value(arg_list[3])) + std::not_equal_to<T>()(T(0), value(arg_list[0])) && + std::not_equal_to<T>()(T(0), value(arg_list[1])) && + std::not_equal_to<T>()(T(0), value(arg_list[2])) && + std::not_equal_to<T>()(T(0), value(arg_list[3])) ) ? T(1) : T(0); } @@ -12655,11 +12753,11 @@ namespace exprtk static inline T process_5(const Sequence& arg_list) { return ( - std::not_equal_to<T>()(T(0),value(arg_list[0])) && - std::not_equal_to<T>()(T(0),value(arg_list[1])) && - std::not_equal_to<T>()(T(0),value(arg_list[2])) && - std::not_equal_to<T>()(T(0),value(arg_list[3])) && - std::not_equal_to<T>()(T(0),value(arg_list[4])) + std::not_equal_to<T>()(T(0), value(arg_list[0])) && + std::not_equal_to<T>()(T(0), value(arg_list[1])) && + std::not_equal_to<T>()(T(0), value(arg_list[2])) && + std::not_equal_to<T>()(T(0), value(arg_list[3])) && + std::not_equal_to<T>()(T(0), value(arg_list[4])) ) ? T(1) : T(0); } }; @@ -12671,7 +12769,7 @@ namespace exprtk template <typename Type, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> static inline T process(const Sequence<Type,Allocator>& arg_list) { switch (arg_list.size()) @@ -12685,7 +12783,7 @@ namespace exprtk { for (std::size_t i = 0; i < arg_list.size(); ++i) { - if (std::not_equal_to<T>()(T(0),value(arg_list[i]))) + if (std::not_equal_to<T>()(T(0), value(arg_list[i]))) return T(1); } @@ -12698,15 +12796,15 @@ namespace exprtk static inline T process_1(const Sequence& arg_list) { return std::not_equal_to<T>() - (T(0),value(arg_list[0])) ? T(1) : T(0); + (T(0), value(arg_list[0])) ? T(1) : T(0); } template <typename Sequence> static inline T process_2(const Sequence& arg_list) { return ( - std::not_equal_to<T>()(T(0),value(arg_list[0])) || - std::not_equal_to<T>()(T(0),value(arg_list[1])) + std::not_equal_to<T>()(T(0), value(arg_list[0])) || + std::not_equal_to<T>()(T(0), value(arg_list[1])) ) ? T(1) : T(0); } @@ -12714,9 +12812,9 @@ namespace exprtk static inline T process_3(const Sequence& arg_list) { return ( - std::not_equal_to<T>()(T(0),value(arg_list[0])) || - std::not_equal_to<T>()(T(0),value(arg_list[1])) || - std::not_equal_to<T>()(T(0),value(arg_list[2])) + std::not_equal_to<T>()(T(0), value(arg_list[0])) || + std::not_equal_to<T>()(T(0), value(arg_list[1])) || + std::not_equal_to<T>()(T(0), value(arg_list[2])) ) ? T(1) : T(0); } @@ -12724,10 +12822,10 @@ namespace exprtk static inline T process_4(const Sequence& arg_list) { return ( - std::not_equal_to<T>()(T(0),value(arg_list[0])) || - std::not_equal_to<T>()(T(0),value(arg_list[1])) || - std::not_equal_to<T>()(T(0),value(arg_list[2])) || - std::not_equal_to<T>()(T(0),value(arg_list[3])) + std::not_equal_to<T>()(T(0), value(arg_list[0])) || + std::not_equal_to<T>()(T(0), value(arg_list[1])) || + std::not_equal_to<T>()(T(0), value(arg_list[2])) || + std::not_equal_to<T>()(T(0), value(arg_list[3])) ) ? T(1) : T(0); } @@ -12735,11 +12833,11 @@ namespace exprtk static inline T process_5(const Sequence& arg_list) { return ( - std::not_equal_to<T>()(T(0),value(arg_list[0])) || - std::not_equal_to<T>()(T(0),value(arg_list[1])) || - std::not_equal_to<T>()(T(0),value(arg_list[2])) || - std::not_equal_to<T>()(T(0),value(arg_list[3])) || - std::not_equal_to<T>()(T(0),value(arg_list[4])) + std::not_equal_to<T>()(T(0), value(arg_list[0])) || + std::not_equal_to<T>()(T(0), value(arg_list[1])) || + std::not_equal_to<T>()(T(0), value(arg_list[2])) || + std::not_equal_to<T>()(T(0), value(arg_list[3])) || + std::not_equal_to<T>()(T(0), value(arg_list[4])) ) ? T(1) : T(0); } }; @@ -12751,7 +12849,7 @@ namespace exprtk template <typename Type, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> static inline T process(const Sequence<Type,Allocator>& arg_list) { switch (arg_list.size()) @@ -13651,15 +13749,15 @@ namespace exprtk template <typename T, typename T0, typename T1> \ const typename expression_node<T>::node_type nodetype_T0oT1<T,T0_,T1_>::result = expression_node<T>:: v_; \ - synthesis_node_type_define(const T0&,const T1&, e_vov) - synthesis_node_type_define(const T0&,const T1 , e_voc) - synthesis_node_type_define(const T0 ,const T1&, e_cov) - synthesis_node_type_define( T0&, T1&,e_none) - synthesis_node_type_define(const T0 ,const T1 ,e_none) - synthesis_node_type_define( T0&,const T1 ,e_none) - synthesis_node_type_define(const T0 , T1&,e_none) - synthesis_node_type_define(const T0&, T1&,e_none) - synthesis_node_type_define( T0&,const T1&,e_none) + synthesis_node_type_define(const T0&, const T1&, e_vov) + synthesis_node_type_define(const T0&, const T1 , e_voc) + synthesis_node_type_define(const T0 , const T1&, e_cov) + synthesis_node_type_define( T0&, T1&, e_none) + synthesis_node_type_define(const T0 , const T1 , e_none) + synthesis_node_type_define( T0&, const T1 , e_none) + synthesis_node_type_define(const T0 , T1&, e_none) + synthesis_node_type_define(const T0&, T1&, e_none) + synthesis_node_type_define( T0&, const T1&, e_none) #undef synthesis_node_type_define template <typename T, typename T0, typename T1, typename T2> @@ -13673,15 +13771,15 @@ namespace exprtk template <typename T, typename T0, typename T1, typename T2> \ const typename expression_node<T>::node_type nodetype_T0oT1oT2<T,T0_,T1_,T2_>::result = expression_node<T>:: v_; \ - synthesis_node_type_define(const T0&,const T1&,const T2&, e_vovov) - synthesis_node_type_define(const T0&,const T1&,const T2 , e_vovoc) - synthesis_node_type_define(const T0&,const T1 ,const T2&, e_vocov) - synthesis_node_type_define(const T0 ,const T1&,const T2&, e_covov) - synthesis_node_type_define(const T0 ,const T1&,const T2 , e_covoc) - synthesis_node_type_define(const T0 ,const T1 ,const T2 , e_none ) - synthesis_node_type_define(const T0 ,const T1 ,const T2&, e_none ) - synthesis_node_type_define(const T0&,const T1 ,const T2 , e_none ) - synthesis_node_type_define( T0&, T1&, T2&, e_none ) + synthesis_node_type_define(const T0&, const T1&, const T2&, e_vovov) + synthesis_node_type_define(const T0&, const T1&, const T2 , e_vovoc) + synthesis_node_type_define(const T0&, const T1 , const T2&, e_vocov) + synthesis_node_type_define(const T0 , const T1&, const T2&, e_covov) + synthesis_node_type_define(const T0 , const T1&, const T2 , e_covoc) + synthesis_node_type_define(const T0 , const T1 , const T2 , e_none ) + synthesis_node_type_define(const T0 , const T1 , const T2&, e_none ) + synthesis_node_type_define(const T0&, const T1 , const T2 , e_none ) + synthesis_node_type_define( T0&, T1&, T2&, e_none ) #undef synthesis_node_type_define template <typename T, typename T0, typename T1, typename T2, typename T3> @@ -13695,22 +13793,22 @@ namespace exprtk template <typename T, typename T0, typename T1, typename T2, typename T3> \ const typename expression_node<T>::node_type nodetype_T0oT1oT2oT3<T,T0_,T1_,T2_,T3_>::result = expression_node<T>:: v_; \ - synthesis_node_type_define(const T0&,const T1&,const T2&, const T3&,e_vovovov) - synthesis_node_type_define(const T0&,const T1&,const T2&, const T3 ,e_vovovoc) - synthesis_node_type_define(const T0&,const T1&,const T2 , const T3&,e_vovocov) - synthesis_node_type_define(const T0&,const T1 ,const T2&, const T3&,e_vocovov) - synthesis_node_type_define(const T0 ,const T1&,const T2&, const T3&,e_covovov) - synthesis_node_type_define(const T0 ,const T1&,const T2 , const T3&,e_covocov) - synthesis_node_type_define(const T0&,const T1 ,const T2&, const T3 ,e_vocovoc) - synthesis_node_type_define(const T0 ,const T1&,const T2&, const T3 ,e_covovoc) - synthesis_node_type_define(const T0&,const T1 ,const T2 , const T3&,e_vococov) - synthesis_node_type_define(const T0 ,const T1 ,const T2 , const T3 ,e_none ) - synthesis_node_type_define(const T0 ,const T1 ,const T2 , const T3&,e_none ) - synthesis_node_type_define(const T0 ,const T1 ,const T2&, const T3 ,e_none ) - synthesis_node_type_define(const T0 ,const T1&,const T2 , const T3 ,e_none ) - synthesis_node_type_define(const T0&,const T1 ,const T2 , const T3 ,e_none ) - synthesis_node_type_define(const T0 ,const T1 ,const T2&, const T3&,e_none ) - synthesis_node_type_define(const T0&,const T1&,const T2 , const T3 ,e_none ) + synthesis_node_type_define(const T0&, const T1&, const T2&, const T3&, e_vovovov) + synthesis_node_type_define(const T0&, const T1&, const T2&, const T3 , e_vovovoc) + synthesis_node_type_define(const T0&, const T1&, const T2 , const T3&, e_vovocov) + synthesis_node_type_define(const T0&, const T1 , const T2&, const T3&, e_vocovov) + synthesis_node_type_define(const T0 , const T1&, const T2&, const T3&, e_covovov) + synthesis_node_type_define(const T0 , const T1&, const T2 , const T3&, e_covocov) + synthesis_node_type_define(const T0&, const T1 , const T2&, const T3 , e_vocovoc) + synthesis_node_type_define(const T0 , const T1&, const T2&, const T3 , e_covovoc) + synthesis_node_type_define(const T0&, const T1 , const T2 , const T3&, e_vococov) + synthesis_node_type_define(const T0 , const T1 , const T2 , const T3 , e_none ) + synthesis_node_type_define(const T0 , const T1 , const T2 , const T3&, e_none ) + synthesis_node_type_define(const T0 , const T1 , const T2&, const T3 , e_none ) + synthesis_node_type_define(const T0 , const T1&, const T2 , const T3 , e_none ) + synthesis_node_type_define(const T0&, const T1 , const T2 , const T3 , e_none ) + synthesis_node_type_define(const T0 , const T1 , const T2&, const T3&, e_none ) + synthesis_node_type_define(const T0&, const T1&, const T2 , const T3 , e_none ) #undef synthesis_node_type_define template <typename T, typename T0, typename T1> @@ -13766,7 +13864,7 @@ namespace exprtk bfunc_t p2) { return allocator - .template allocate_type<node_type,T0,T1,bfunc_t&> + .template allocate_type<node_type, T0, T1, bfunc_t&> (p0, p1, p2); } @@ -13812,7 +13910,7 @@ namespace exprtk inline T value() const { - return ProcessMode::process(t0_,t1_,t2_,f0_,f1_); + return ProcessMode::process(t0_, t1_, t2_, f0_, f1_); } inline T0 t0() const @@ -13854,7 +13952,7 @@ namespace exprtk static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, bfunc_t p3, bfunc_t p4) { return allocator - .template allocate_type<node_type,T0,T1,T2,bfunc_t,bfunc_t> + .template allocate_type<node_type, T0, T1, T2, bfunc_t, bfunc_t> (p0, p1, p2, p3, p4); } @@ -13942,7 +14040,7 @@ namespace exprtk static inline std::string id() { - return process_mode_t::template id<T0,T1,T2,T3>(); + return process_mode_t::template id<T0, T1, T2, T3>(); } template <typename Allocator> @@ -13951,7 +14049,7 @@ namespace exprtk bfunc_t p4, bfunc_t p5, bfunc_t p6) { return allocator - .template allocate_type<node_type,T0,T1,T2,T3,bfunc_t,bfunc_t> + .template allocate_type<node_type, T0, T1, T2, T3, bfunc_t, bfunc_t> (p0, p1, p2, p3, p4, p5, p6); } @@ -14036,7 +14134,7 @@ namespace exprtk static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, tfunc_t p3) { return allocator - .template allocate_type<node_type,T0,T1,T2,tfunc_t> + .template allocate_type<node_type, T0, T1, T2, tfunc_t> (p0, p1, p2, p3); } @@ -14127,7 +14225,7 @@ namespace exprtk static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2) { return allocator - .template allocate_type<node_type,T0,T1,T2> + .template allocate_type<node_type, T0, T1, T2> (p0, p1, p2); } @@ -14228,7 +14326,7 @@ namespace exprtk static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, T3 p3, qfunc_t p4) { return allocator - .template allocate_type<node_type,T0,T1,T2,T3,qfunc_t> + .template allocate_type<node_type, T0, T1, T2, T3, qfunc_t> (p0, p1, p2, p3, p4); } @@ -14311,7 +14409,7 @@ namespace exprtk static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, T3 p3) { return allocator - .template allocate_type<node_type,T0,T1,T2,T3> + .template allocate_type<node_type, T0, T1, T2, T3> (p0, p1, p2, p3); } @@ -14347,27 +14445,27 @@ namespace exprtk template <typename T, typename T0, typename T1> struct T0oT1_define { - typedef details::T0oT1<T,T0,T1> type0; + typedef details::T0oT1<T, T0, T1> type0; }; template <typename T, typename T0, typename T1, typename T2> struct T0oT1oT2_define { - typedef details::T0oT1oT2<T,T0,T1,T2,typename T0oT1oT2process<T>::mode0> type0; - typedef details::T0oT1oT2<T,T0,T1,T2,typename T0oT1oT2process<T>::mode1> type1; - typedef details::T0oT1oT2_sf3<T,T0,T1,T2> sf3_type; - typedef details::sf3ext_type_node<T,T0,T1,T2> sf3_type_node; + typedef details::T0oT1oT2<T, T0, T1, T2, typename T0oT1oT2process<T>::mode0> type0; + typedef details::T0oT1oT2<T, T0, T1, T2, typename T0oT1oT2process<T>::mode1> type1; + typedef details::T0oT1oT2_sf3<T, T0, T1, T2> sf3_type; + typedef details::sf3ext_type_node<T, T0, T1, T2> sf3_type_node; }; template <typename T, typename T0, typename T1, typename T2, typename T3> struct T0oT1oT2oT3_define { - typedef details::T0oT1oT2oT3<T,T0,T1,T2,T3,typename T0oT1oT20T3process<T>::mode0> type0; - typedef details::T0oT1oT2oT3<T,T0,T1,T2,T3,typename T0oT1oT20T3process<T>::mode1> type1; - typedef details::T0oT1oT2oT3<T,T0,T1,T2,T3,typename T0oT1oT20T3process<T>::mode2> type2; - typedef details::T0oT1oT2oT3<T,T0,T1,T2,T3,typename T0oT1oT20T3process<T>::mode3> type3; - typedef details::T0oT1oT2oT3<T,T0,T1,T2,T3,typename T0oT1oT20T3process<T>::mode4> type4; - typedef details::T0oT1oT2oT3_sf4<T,T0,T1,T2,T3> sf4_type; + typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode0> type0; + typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode1> type1; + typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode2> type2; + typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode3> type3; + typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode4> type4; + typedef details::T0oT1oT2oT3_sf4<T, T0, T1, T2, T3> sf4_type; }; template <typename T, typename Operation> @@ -15437,37 +15535,37 @@ namespace exprtk template <typename ResultNode, typename OpType, typename ExprNode> inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[1]) { - return allocate<ResultNode>(operation,branch[0]); + return allocate<ResultNode>(operation, branch[0]); } template <typename ResultNode, typename OpType, typename ExprNode> inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[2]) { - return allocate<ResultNode>(operation,branch[0],branch[1]); + return allocate<ResultNode>(operation, branch[0], branch[1]); } template <typename ResultNode, typename OpType, typename ExprNode> inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[3]) { - return allocate<ResultNode>(operation,branch[0],branch[1],branch[2]); + return allocate<ResultNode>(operation, branch[0], branch[1], branch[2]); } template <typename ResultNode, typename OpType, typename ExprNode> inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[4]) { - return allocate<ResultNode>(operation,branch[0],branch[1],branch[2],branch[3]); + return allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3]); } template <typename ResultNode, typename OpType, typename ExprNode> inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[5]) { - return allocate<ResultNode>(operation,branch[0],branch[1],branch[2],branch[3],branch[4]); + return allocate<ResultNode>(operation, branch[0],branch[1], branch[2], branch[3], branch[4]); } template <typename ResultNode, typename OpType, typename ExprNode> inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[6]) { - return allocate<ResultNode>(operation,branch[0],branch[1],branch[2],branch[3],branch[4],branch[5]); + return allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3], branch[4], branch[5]); } template <typename node_type> @@ -15479,7 +15577,7 @@ namespace exprtk template <typename node_type, typename Type, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node<typename node_type::value_type>* allocate(const Sequence<Type,Allocator>& seq) const { return (new node_type(seq)); @@ -15978,8 +16076,9 @@ namespace exprtk enum return_type { - e_rtrn_scalar = 0, - e_rtrn_string = 1 + e_rtrn_scalar = 0, + e_rtrn_string = 1, + e_rtrn_overload = 2 }; typedef T type; @@ -16030,20 +16129,20 @@ namespace exprtk typedef T (*ff00_functor)(); typedef T (*ff01_functor)(T); - typedef T (*ff02_functor)(T,T); - typedef T (*ff03_functor)(T,T,T); - typedef T (*ff04_functor)(T,T,T,T); - typedef T (*ff05_functor)(T,T,T,T,T); - typedef T (*ff06_functor)(T,T,T,T,T,T); - typedef T (*ff07_functor)(T,T,T,T,T,T,T); - typedef T (*ff08_functor)(T,T,T,T,T,T,T,T); - typedef T (*ff09_functor)(T,T,T,T,T,T,T,T,T); - typedef T (*ff10_functor)(T,T,T,T,T,T,T,T,T,T); - typedef T (*ff11_functor)(T,T,T,T,T,T,T,T,T,T,T); - typedef T (*ff12_functor)(T,T,T,T,T,T,T,T,T,T,T,T); - typedef T (*ff13_functor)(T,T,T,T,T,T,T,T,T,T,T,T,T); - typedef T (*ff14_functor)(T,T,T,T,T,T,T,T,T,T,T,T,T,T); - typedef T (*ff15_functor)(T,T,T,T,T,T,T,T,T,T,T,T,T,T,T); + typedef T (*ff02_functor)(T, T); + typedef T (*ff03_functor)(T, T, T); + typedef T (*ff04_functor)(T, T, T, T); + typedef T (*ff05_functor)(T, T, T, T, T); + typedef T (*ff06_functor)(T, T, T, T, T, T); + typedef T (*ff07_functor)(T, T, T, T, T, T, T); + typedef T (*ff08_functor)(T, T, T, T, T, T, T, T); + typedef T (*ff09_functor)(T, T, T, T, T, T, T, T, T); + typedef T (*ff10_functor)(T, T, T, T, T, T, T, T, T, T); + typedef T (*ff11_functor)(T, T, T, T, T, T, T, T, T, T, T); + typedef T (*ff12_functor)(T, T, T, T, T, T, T, T, T, T, T, T); + typedef T (*ff13_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T); + typedef T (*ff14_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T); + typedef T (*ff15_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T); protected: @@ -16631,11 +16730,12 @@ namespace exprtk #ifndef exprtk_disable_string_capabilities type_store<typename details::stringvar_node<T>,std::string> stringvar_store; #endif - type_store<ifunction<T>,ifunction<T> > function_store; + type_store<ifunction<T>,ifunction<T> > function_store; type_store<ivararg_function <T>,ivararg_function <T> > vararg_function_store; type_store<igeneric_function<T>,igeneric_function<T> > generic_function_store; type_store<igeneric_function<T>,igeneric_function<T> > string_function_store; - type_store<vector_holder_t,vector_holder_t> vector_store; + type_store<igeneric_function<T>,igeneric_function<T> > overload_function_store; + type_store<vector_holder_t,vector_holder_t> vector_store; st_data() { @@ -16759,7 +16859,7 @@ namespace exprtk return (*this); } - inline bool operator==(const symbol_table<T>& st) + inline bool operator==(const symbol_table<T>& st) const { return (this == &st) || (control_block_ == st.control_block_); } @@ -16906,6 +17006,16 @@ namespace exprtk return local_data().string_function_store.get(function_name); } + inline generic_function_ptr get_overload_function(const std::string& function_name) const + { + if (!valid()) + return reinterpret_cast<generic_function_ptr>(0); + else if (!valid_symbol(function_name)) + return reinterpret_cast<generic_function_ptr>(0); + else + return local_data().overload_function_store.get(function_name); + } + typedef vector_holder_t* vector_holder_ptr; inline vector_holder_ptr get_vector(const std::string& vector_name) const @@ -17071,14 +17181,33 @@ namespace exprtk return false; else if (symbol_exists(function_name)) return false; - else if (std::string::npos != function.parameter_sequence.find_first_not_of("STVZ*?|")) + else if ( + ( + (generic_function_t::e_rtrn_scalar == function.rtrn_type) || + (generic_function_t::e_rtrn_string == function.rtrn_type) + ) && + std::string::npos != function.parameter_sequence.find_first_not_of("STVZ*?|") + ) return false; - else if (generic_function_t::e_rtrn_scalar == function.rtrn_type) - return local_data().generic_function_store.add(function_name,function); - else if (generic_function_t::e_rtrn_string == function.rtrn_type) - return local_data().string_function_store.add(function_name, function); - else + else if ( + (generic_function_t::e_rtrn_overload == function.rtrn_type) && + std::string::npos != function.parameter_sequence.find_first_not_of("STVZ*?|:") + ) return false; + + switch (function.rtrn_type) + { + case generic_function_t::e_rtrn_scalar : + return local_data().generic_function_store.add(function_name,function); + + case generic_function_t::e_rtrn_string : + return local_data().string_function_store.add(function_name,function); + + case generic_function_t::e_rtrn_overload : + return local_data().overload_function_store.add(function_name,function); + } + + return false; } #define exprtk_define_freefunction(NN) \ @@ -17141,14 +17270,33 @@ namespace exprtk return false; else if (symbol_exists(function_name,false)) return false; - else if (std::string::npos != function.parameter_sequence.find_first_not_of("STV*?|")) + else if ( + ( + (generic_function_t::e_rtrn_scalar == function.rtrn_type) || + (generic_function_t::e_rtrn_string == function.rtrn_type) + ) && + std::string::npos != function.parameter_sequence.find_first_not_of("STV*?|") + ) return false; - else if (generic_function_t::e_rtrn_scalar == function.rtrn_type) - return local_data().generic_function_store.add(function_name,function); - else if (generic_function_t::e_rtrn_string == function.rtrn_type) - return local_data().string_function_store.add(function_name, function); - else + else if ( + generic_function_t::e_rtrn_overload && + std::string::npos != function.parameter_sequence.find_first_not_of("STV*?|:") + ) return false; + + switch (function.rtrn_type) + { + case generic_function_t::e_rtrn_scalar : + return local_data().generic_function_store.add(function_name,function); + + case generic_function_t::e_rtrn_string : + return local_data().string_function_store.add(function_name,function); + + case generic_function_t::e_rtrn_overload : + return local_data().overload_function_store.add(function_name,function); + } + + return false; } template <std::size_t N> @@ -17337,8 +17485,8 @@ namespace exprtk { /* Function will return true if symbol_name exists as either a - reserved symbol, variable, stringvar or function name in any - of the type stores. + reserved symbol, variable, stringvar, vector or function name + in any of the type stores. */ if (!valid()) return false; @@ -17348,6 +17496,8 @@ namespace exprtk else if (local_data().stringvar_store.symbol_exists(symbol_name)) return true; #endif + else if (local_data().vector_store.symbol_exists(symbol_name)) + return true; else if (local_data().function_store.symbol_exists(symbol_name)) return true; else if (check_reserved_symb && local_data().is_reserved_symbol(symbol_name)) @@ -17502,6 +17652,21 @@ namespace exprtk } } } + + { + std::vector<std::string> name_list; + + st.local_data().overload_function_store.get_list(name_list); + + if (!name_list.empty()) + { + for (std::size_t i = 0; i < name_list.size(); ++i) + { + exprtk::igeneric_function<T>& ifunc = *st.get_overload_function(name_list[i]); + add_function(name_list[i],ifunc); + } + } + } } private: @@ -17754,7 +17919,7 @@ namespace exprtk return *this; } - inline bool operator==(const expression<T>& e) + inline bool operator==(const expression<T>& e) const { return (this == &e); } @@ -17903,7 +18068,7 @@ namespace exprtk control_block_-> local_data_list.push_back( typename expression<T>::control_block:: - data_pack(reinterpret_cast<void*>(data),dt,size)); + data_pack(reinterpret_cast<void*>(data), dt, size)); } } } @@ -18454,6 +18619,10 @@ namespace exprtk inline void free_element(scope_element& se) { + #ifdef exprtk_enable_debugging + exprtk_debug(("free_element() - se[%s]\n", se.name.c_str())); + #endif + switch (se.type) { case scope_element::e_variable : if (se.data ) delete (T*) se.data; @@ -18541,7 +18710,7 @@ namespace exprtk { parser_.state_.scope_depth++; #ifdef exprtk_enable_debugging - std::string depth(2 * parser_.state_.scope_depth,'-'); + const std::string depth(2 * parser_.state_.scope_depth,'-'); exprtk_debug(("%s> Scope Depth: %02d\n", depth.c_str(), static_cast<int>(parser_.state_.scope_depth))); @@ -18553,7 +18722,7 @@ namespace exprtk parser_.sem_.deactivate(parser_.state_.scope_depth); parser_.state_.scope_depth--; #ifdef exprtk_enable_debugging - std::string depth(2 * parser_.state_.scope_depth,'-'); + const std::string depth(2 * parser_.state_.scope_depth,'-'); exprtk_debug(("<%s Scope Depth: %02d\n", depth.c_str(), static_cast<int>(parser_.state_.scope_depth))); @@ -18767,6 +18936,27 @@ namespace exprtk return result; } + inline generic_function_ptr get_overload_function(const std::string& function_name) const + { + if (!valid_function_name(function_name)) + return reinterpret_cast<generic_function_ptr>(0); + + generic_function_ptr result = reinterpret_cast<generic_function_ptr>(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else + result = + local_data(i).overload_function_store.get(function_name); + + if (result) break; + } + + return result; + } + inline vector_holder_ptr get_vector(const std::string& vector_name) const { if (!valid_symbol(vector_name)) @@ -19025,8 +19215,9 @@ namespace exprtk enum usr_symbol_type { - e_usr_variable_type = 0, - e_usr_constant_type = 1 + e_usr_unknown_type = 0, + e_usr_variable_type = 1, + e_usr_constant_type = 2 }; enum usr_mode @@ -19105,7 +19296,7 @@ namespace exprtk {} template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline std::size_t symbols(Sequence<symbol_t,Allocator>& symbols_list) { if (!collect_variables_ && !collect_functions_) @@ -19128,7 +19319,7 @@ namespace exprtk } template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline std::size_t assignment_symbols(Sequence<symbol_t,Allocator>& assignment_list) { if (!collect_assignments_) @@ -19461,7 +19652,7 @@ namespace exprtk bool rsrvd_sym_usr_disabled () const { return disable_rsrvd_sym_usr_; } bool zero_return_disabled () const { return disable_zero_return_; } - bool function_enabled(const std::string& function_name) + bool function_enabled(const std::string& function_name) const { if (disabled_func_set_.empty()) return true; @@ -19469,7 +19660,7 @@ namespace exprtk return (disabled_func_set_.end() == disabled_func_set_.find(function_name)); } - bool control_struct_enabled(const std::string& control_struct) + bool control_struct_enabled(const std::string& control_struct) const { if (disabled_ctrl_set_.empty()) return true; @@ -19477,7 +19668,7 @@ namespace exprtk return (disabled_ctrl_set_.end() == disabled_ctrl_set_.find(control_struct)); } - bool logic_enabled(const std::string& logic_operation) + bool logic_enabled(const std::string& logic_operation) const { if (disabled_logic_set_.empty()) return true; @@ -19485,7 +19676,7 @@ namespace exprtk return (disabled_logic_set_.end() == disabled_logic_set_.find(logic_operation)); } - bool arithmetic_enabled(const details::operator_type& arithmetic_operation) + bool arithmetic_enabled(const details::operator_type& arithmetic_operation) const { if (disabled_logic_set_.empty()) return true; @@ -19494,7 +19685,7 @@ namespace exprtk .find(arith_opr_to_string(arithmetic_operation)); } - bool assignment_enabled(const details::operator_type& assignment) + bool assignment_enabled(const details::operator_type& assignment) const { if (disabled_assignment_set_.empty()) return true; @@ -19503,7 +19694,7 @@ namespace exprtk .find(assign_opr_to_string(assignment)); } - bool inequality_enabled(const details::operator_type& inequality) + bool inequality_enabled(const details::operator_type& inequality) const { if (disabled_inequality_set_.empty()) return true; @@ -19512,7 +19703,7 @@ namespace exprtk .find(inequality_opr_to_string(inequality)); } - bool function_disabled(const std::string& function_name) + bool function_disabled(const std::string& function_name) const { if (disabled_func_set_.empty()) return false; @@ -19520,7 +19711,7 @@ namespace exprtk return (disabled_func_set_.end() != disabled_func_set_.find(function_name)); } - bool control_struct_disabled(const std::string& control_struct) + bool control_struct_disabled(const std::string& control_struct) const { if (disabled_ctrl_set_.empty()) return false; @@ -19528,7 +19719,7 @@ namespace exprtk return (disabled_ctrl_set_.end() != disabled_ctrl_set_.find(control_struct)); } - bool logic_disabled(const std::string& logic_operation) + bool logic_disabled(const std::string& logic_operation) const { if (disabled_logic_set_.empty()) return false; @@ -19536,7 +19727,7 @@ namespace exprtk return (disabled_logic_set_.end() != disabled_logic_set_.find(logic_operation)); } - bool assignment_disabled(const details::operator_type assignment_operation) + bool assignment_disabled(const details::operator_type assignment_operation) const { if (disabled_assignment_set_.empty()) return false; @@ -19545,7 +19736,7 @@ namespace exprtk .find(assign_opr_to_string(assignment_operation)); } - bool arithmetic_disabled(const details::operator_type arithmetic_operation) + bool arithmetic_disabled(const details::operator_type arithmetic_operation) const { if (disabled_arithmetic_set_.empty()) return false; @@ -19554,7 +19745,7 @@ namespace exprtk .find(arith_opr_to_string(arithmetic_operation)); } - bool inequality_disabled(const details::operator_type& inequality) + bool inequality_disabled(const details::operator_type& inequality) const { if (disabled_inequality_set_.empty()) return false; @@ -19768,7 +19959,7 @@ namespace exprtk disable_zero_return_ = (compile_options & e_disable_zero_return ) == e_disable_zero_return; } - std::string assign_opr_to_string(details::operator_type opr) + std::string assign_opr_to_string(details::operator_type opr) const { switch (opr) { @@ -19782,7 +19973,7 @@ namespace exprtk } } - std::string arith_opr_to_string(details::operator_type opr) + std::string arith_opr_to_string(details::operator_type opr) const { switch (opr) { @@ -19795,7 +19986,7 @@ namespace exprtk } } - std::string inequality_opr_to_string(details::operator_type opr) + std::string inequality_opr_to_string(details::operator_type opr) const { switch (opr) { @@ -19933,7 +20124,8 @@ namespace exprtk if (settings_.sequence_check_enabled()) { - helper_assembly_.register_scanner(&sequence_validator_); + helper_assembly_.register_scanner(&sequence_validator_ ); + helper_assembly_.register_scanner(&sequence_validator_3tkns_); } } } @@ -20021,15 +20213,15 @@ namespace exprtk exprtk_error_location)); } - dec_.clear (); - sem_.cleanup (); - return_cleanup(); - if ((0 != e) && branch_deletable(e)) { destroy_node(e); } + dec_.clear (); + sem_.cleanup (); + return_cleanup(); + return false; } } @@ -20109,9 +20301,10 @@ namespace exprtk { if (helper_assembly_.error_token_scanner) { - lexer::helper::bracket_checker* bracket_checker_ptr = 0; - lexer::helper::numeric_checker* numeric_checker_ptr = 0; - lexer::helper::sequence_validator* sequence_validator_ptr = 0; + lexer::helper::bracket_checker* bracket_checker_ptr = 0; + lexer::helper::numeric_checker* numeric_checker_ptr = 0; + lexer::helper::sequence_validator* sequence_validator_ptr = 0; + lexer::helper::sequence_validator_3tokens* sequence_validator3_ptr = 0; if (0 != (bracket_checker_ptr = dynamic_cast<lexer::helper::bracket_checker*>(helper_assembly_.error_token_scanner))) { @@ -20159,6 +20352,26 @@ namespace exprtk sequence_validator_ptr->clear_errors(); } } + else if (0 != (sequence_validator3_ptr = dynamic_cast<lexer::helper::sequence_validator_3tokens*>(helper_assembly_.error_token_scanner))) + { + for (std::size_t i = 0; i < sequence_validator3_ptr->error_count(); ++i) + { + std::pair<lexer::token,lexer::token> error_token = sequence_validator3_ptr->error(i); + + set_error( + make_error(parser_error::e_token, + error_token.first, + "ERR007 - Invalid token sequence: '" + + error_token.first.value + "' and '" + + error_token.second.value + "'", + exprtk_error_location)); + } + + if (sequence_validator3_ptr->error_count()) + { + sequence_validator3_ptr->clear_errors(); + } + } } return false; @@ -20173,7 +20386,7 @@ namespace exprtk return settings_; } - inline parser_error::type get_error(const std::size_t& index) + inline parser_error::type get_error(const std::size_t& index) const { if (index < error_list_.size()) return error_list_[index]; @@ -20244,7 +20457,7 @@ namespace exprtk private: - inline bool valid_base_operation(const std::string& symbol) + inline bool valid_base_operation(const std::string& symbol) const { const std::size_t length = symbol.size(); @@ -20258,7 +20471,7 @@ namespace exprtk (base_ops_map_.end() != base_ops_map_.find(symbol)); } - inline bool valid_vararg_operation(const std::string& symbol) + inline bool valid_vararg_operation(const std::string& symbol) const { static const std::string s_sum = "sum" ; static const std::string s_mul = "mul" ; @@ -20285,17 +20498,17 @@ namespace exprtk settings_.function_enabled(symbol); } - bool is_invalid_arithmetic_operation(const details::operator_type operation) + bool is_invalid_arithmetic_operation(const details::operator_type operation) const { return settings_.arithmetic_disabled(operation); } - bool is_invalid_assignment_operation(const details::operator_type operation) + bool is_invalid_assignment_operation(const details::operator_type operation) const { return settings_.assignment_disabled(operation); } - bool is_invalid_inequality_operation(const details::operator_type operation) + bool is_invalid_inequality_operation(const details::operator_type operation) const { return settings_.inequality_disabled(operation); } @@ -20303,9 +20516,9 @@ namespace exprtk #ifdef exprtk_enable_debugging inline void next_token() { - std::string ct_str = current_token().value; + const std::string ct_str = current_token().value; parser_helper::next_token(); - std::string depth(2 * state_.scope_depth,' '); + const std::string depth(2 * state_.scope_depth,' '); exprtk_debug(("%s" "prev[%s] --> curr[%s]\n", depth.c_str(), @@ -20341,7 +20554,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR007 - Invalid expression encountered", + "ERR008 - Invalid expression encountered", exprtk_error_location)); } @@ -20355,7 +20568,7 @@ namespace exprtk end_token = current_token(); - std::string sub_expr = construct_subexpr(begin_token,end_token); + const std::string sub_expr = construct_subexpr(begin_token, end_token); exprtk_debug(("parse_corpus(%02d) Subexpr: %s\n", static_cast<int>(arg_list.size() - 1), @@ -20564,7 +20777,7 @@ namespace exprtk else if (current_state.left < precedence) break; - lexer::token prev_token = current_token(); + const lexer::token prev_token = current_token(); next_token(); @@ -20578,7 +20791,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, prev_token, - "ERR008 - Invalid arithmetic operation '" + details::to_str(current_state.operation) + "'", + "ERR009 - Invalid arithmetic operation '" + details::to_str(current_state.operation) + "'", exprtk_error_location)); return error_node(); @@ -20590,7 +20803,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, prev_token, - "ERR009 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'", + "ERR010 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'", exprtk_error_location)); return error_node(); @@ -20602,7 +20815,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, prev_token, - "ERR010 - Invalid assignment operation '" + details::to_str(current_state.operation) + "'", + "ERR011 - Invalid assignment operation '" + details::to_str(current_state.operation) + "'", exprtk_error_location)); return error_node(); @@ -20615,13 +20828,13 @@ namespace exprtk details::is_return_node(right_branch) ) { - free_node(node_allocator_, expression); - free_node(node_allocator_,right_branch); + free_node(node_allocator_, expression); + free_node(node_allocator_, right_branch); set_error( make_error(parser_error::e_syntax, prev_token, - "ERR011 - Return statements cannot be part of sub-expressions", + "ERR012 - Return statements cannot be part of sub-expressions", exprtk_error_location)); return error_node(); @@ -20644,11 +20857,12 @@ namespace exprtk prev_token, !synthesis_error_.empty() ? synthesis_error_ : - "ERR012 - General parsing error at token: '" + prev_token.value + "'", + "ERR013 - General parsing error at token: '" + prev_token.value + "'", exprtk_error_location)); } - free_node(node_allocator_,expression); + free_node(node_allocator_, expression); + free_node(node_allocator_, right_branch); return error_node(); } @@ -20713,7 +20927,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR013 - Failed to find variable node in symbol table", + "ERR014 - Failed to find variable node in symbol table", exprtk_error_location)); free_node(node_allocator_,node); @@ -20893,7 +21107,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR014 - Invalid number of parameters for function: '" + function_name + "'", + "ERR015 - Invalid number of parameters for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -20907,7 +21121,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR015 - Failed to generate call to function: '" + function_name + "'", + "ERR016 - Failed to generate call to function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -20926,7 +21140,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR016 - Expecting ifunction '" + function_name + "' to have non-zero parameter count", + "ERR017 - Expecting ifunction '" + function_name + "' to have non-zero parameter count", exprtk_error_location)); return error_node(); @@ -20949,7 +21163,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR017 - Expecting argument list for function: '" + function_name + "'", + "ERR018 - Expecting argument list for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -20964,7 +21178,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR018 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'", + "ERR019 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -20976,7 +21190,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR019 - Invalid number of arguments for function: '" + function_name + "'", + "ERR020 - Invalid number of arguments for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -20989,7 +21203,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR020 - Invalid number of arguments for function: '" + function_name + "'", + "ERR021 - Invalid number of arguments for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -21018,7 +21232,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR021 - Expecting '()' to proceed call to function: '" + function_name + "'", + "ERR022 - Expecting '()' to proceed call to function: '" + function_name + "'", exprtk_error_location)); free_node(node_allocator_,result); @@ -21043,7 +21257,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR022 - Expected a '(' at start of function call to '" + function_name + + "ERR023 - Expected a '(' at start of function call to '" + function_name + "', instead got: '" + current_token().value + "'", exprtk_error_location)); @@ -21055,7 +21269,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR023 - Expected at least one input parameter for function call '" + function_name + "'", + "ERR024 - Expected at least one input parameter for function call '" + function_name + "'", exprtk_error_location)); return 0; @@ -21081,7 +21295,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR024 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'", + "ERR025 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'", exprtk_error_location)); return 0; @@ -21093,7 +21307,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR025 - Invalid number of input parameters passed to function '" + function_name + "'", + "ERR026 - Invalid number of input parameters passed to function '" + function_name + "'", exprtk_error_location)); return 0; @@ -21116,7 +21330,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, diagnostic_token, - "ERR026 - No entry found for base operation: " + operation_name, + "ERR027 - No entry found for base operation: " + operation_name, exprtk_error_location)); return error_node(); @@ -21131,7 +21345,7 @@ namespace exprtk { for (base_ops_map_t::iterator itr = itr_range.first; itr != itr_range.second; ++itr) { - details::base_operation_t& operation = itr->second; + const details::base_operation_t& operation = itr->second; if (operation.num_params == parameter_count) { @@ -21163,7 +21377,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, diagnostic_token, - "ERR027 - Invalid number of input parameters for call to function: '" + operation_name + "'", + "ERR028 - Invalid number of input parameters for call to function: '" + operation_name + "'", exprtk_error_location)); return error_node(); @@ -21183,7 +21397,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR028 - Expected ',' between if-statement condition and consequent", + "ERR029 - Expected ',' between if-statement condition and consequent", exprtk_error_location)); result = false; } @@ -21192,7 +21406,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR029 - Failed to parse consequent for if-statement", + "ERR030 - Failed to parse consequent for if-statement", exprtk_error_location)); result = false; } @@ -21201,7 +21415,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR030 - Expected ',' between if-statement consequent and alternative", + "ERR031 - Expected ',' between if-statement consequent and alternative", exprtk_error_location)); result = false; } @@ -21210,7 +21424,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR031 - Failed to parse alternative for if-statement", + "ERR032 - Failed to parse alternative for if-statement", exprtk_error_location)); result = false; } @@ -21219,7 +21433,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR032 - Expected ')' at the end of if-statement", + "ERR033 - Expected ')' at the end of if-statement", exprtk_error_location)); result = false; } @@ -21241,7 +21455,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR033 - Return types of ternary if-statement differ", + "ERR034 - Return types of ternary if-statement differ", exprtk_error_location)); result = false; @@ -21276,7 +21490,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR034 - Failed to parse body of consequent for if-statement", + "ERR035 - Failed to parse body of consequent for if-statement", exprtk_error_location)); result = false; @@ -21299,7 +21513,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR035 - Expected ';' at the end of the consequent for if-statement", + "ERR036 - Expected ';' at the end of the consequent for if-statement", exprtk_error_location)); result = false; @@ -21310,7 +21524,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR036 - Failed to parse body of consequent for if-statement", + "ERR037 - Failed to parse body of consequent for if-statement", exprtk_error_location)); result = false; @@ -21330,7 +21544,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR037 - Failed to parse body of the 'else' for if-statement", + "ERR038 - Failed to parse body of the 'else' for if-statement", exprtk_error_location)); result = false; @@ -21343,7 +21557,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR038 - Failed to parse body of if-else statement", + "ERR039 - Failed to parse body of if-else statement", exprtk_error_location)); result = false; @@ -21356,7 +21570,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR039 - Expected ';' at the end of the 'else-if' for the if-statement", + "ERR040 - Expected ';' at the end of the 'else-if' for the if-statement", exprtk_error_location)); result = false; @@ -21367,7 +21581,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR040 - Failed to parse body of the 'else' for if-statement", + "ERR041 - Failed to parse body of the 'else' for if-statement", exprtk_error_location)); result = false; @@ -21392,7 +21606,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR041 - Return types of ternary if-statement differ", + "ERR042 - Return types of ternary if-statement differ", exprtk_error_location)); result = false; @@ -21424,7 +21638,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR042 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'", + "ERR043 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'", exprtk_error_location)); return error_node(); @@ -21434,7 +21648,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR043 - Failed to parse condition for if-statement", + "ERR044 - Failed to parse condition for if-statement", exprtk_error_location)); return error_node(); @@ -21466,7 +21680,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR044 - Invalid if-statement", + "ERR045 - Invalid if-statement", exprtk_error_location)); free_node(node_allocator_,condition); @@ -21487,7 +21701,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR045 - Encountered invalid condition branch for ternary if-statement", + "ERR046 - Encountered invalid condition branch for ternary if-statement", exprtk_error_location)); return error_node(); @@ -21497,7 +21711,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR046 - Expected '?' after condition of ternary if-statement", + "ERR047 - Expected '?' after condition of ternary if-statement", exprtk_error_location)); result = false; @@ -21507,7 +21721,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR047 - Failed to parse consequent for ternary if-statement", + "ERR048 - Failed to parse consequent for ternary if-statement", exprtk_error_location)); result = false; @@ -21517,7 +21731,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR048 - Expected ':' between ternary if-statement consequent and alternative", + "ERR049 - Expected ':' between ternary if-statement consequent and alternative", exprtk_error_location)); result = false; @@ -21527,7 +21741,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR049 - Failed to parse alternative for ternary if-statement", + "ERR050 - Failed to parse alternative for ternary if-statement", exprtk_error_location)); result = false; @@ -21550,7 +21764,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR050 - Return types of ternary if-statement differ", + "ERR051 - Return types of ternary if-statement differ", exprtk_error_location)); result = false; @@ -21587,7 +21801,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR051 - Expected '(' at start of while-loop condition statement", + "ERR052 - Expected '(' at start of while-loop condition statement", exprtk_error_location)); return error_node(); @@ -21597,7 +21811,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR052 - Failed to parse condition for while-loop", + "ERR053 - Failed to parse condition for while-loop", exprtk_error_location)); return error_node(); @@ -21607,7 +21821,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR053 - Expected ')' at end of while-loop condition statement", + "ERR054 - Expected ')' at end of while-loop condition statement", exprtk_error_location)); result = false; @@ -21622,7 +21836,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR054 - Failed to parse body of while-loop")); + "ERR055 - Failed to parse body of while-loop")); result = false; } else if (0 == (result_node = expression_generator_.while_loop(condition, @@ -21632,7 +21846,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR055 - Failed to synthesize while-loop", + "ERR056 - Failed to synthesize while-loop", exprtk_error_location)); result = false; @@ -21674,7 +21888,7 @@ namespace exprtk } else { - token_t::token_type seperator = token_t::e_eof; + const token_t::token_type seperator = token_t::e_eof; scope_handler sh(*this); @@ -21700,15 +21914,15 @@ namespace exprtk break; } - bool is_next_until = peek_token_is(token_t::e_symbol) && - peek_token_is("until"); + const bool is_next_until = peek_token_is(token_t::e_symbol) && + peek_token_is("until"); if (!token_is(seperator) && is_next_until) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR056 - Expected '" + token_t::to_str(seperator) + "' in body of repeat until loop", + "ERR057 - Expected '" + token_t::to_str(seperator) + "' in body of repeat until loop", exprtk_error_location)); return error_node(); @@ -21732,7 +21946,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR057 - Failed to parse body of repeat until loop", + "ERR058 - Failed to parse body of repeat until loop", exprtk_error_location)); return error_node(); @@ -21746,7 +21960,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR058 - Expected '(' before condition statement of repeat until loop", + "ERR059 - Expected '(' before condition statement of repeat until loop", exprtk_error_location)); free_node(node_allocator_,branch); @@ -21760,7 +21974,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR059 - Failed to parse condition for repeat until loop", + "ERR060 - Failed to parse condition for repeat until loop", exprtk_error_location)); free_node(node_allocator_,branch); @@ -21772,7 +21986,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR060 - Expected ')' after condition of repeat until loop", + "ERR061 - Expected ')' after condition of repeat until loop", exprtk_error_location)); free_node(node_allocator_, branch); @@ -21793,7 +22007,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR061 - Failed to synthesize repeat until loop", + "ERR062 - Failed to synthesize repeat until loop", exprtk_error_location)); free_node(node_allocator_,condition); @@ -21818,7 +22032,6 @@ namespace exprtk scope_element* se = 0; bool result = true; - std::string loop_counter_symbol; next_token(); @@ -21829,7 +22042,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR062 - Expected '(' at start of for-loop", + "ERR063 - Expected '(' at start of for-loop", exprtk_error_location)); return error_node(); @@ -21849,7 +22062,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR063 - Expected a variable at the start of initialiser section of for-loop", + "ERR064 - Expected a variable at the start of initialiser section of for-loop", exprtk_error_location)); return error_node(); @@ -21859,13 +22072,13 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR064 - Expected variable assignment of initialiser section of for-loop", + "ERR065 - Expected variable assignment of initialiser section of for-loop", exprtk_error_location)); return error_node(); } - loop_counter_symbol = current_token().value; + const std::string loop_counter_symbol = current_token().value; se = &sem_.get_element(loop_counter_symbol); @@ -21874,7 +22087,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR065 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration", + "ERR066 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration", exprtk_error_location)); return error_node(); @@ -21906,7 +22119,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR066 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM", + "ERR067 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM", exprtk_error_location)); sem_.free_element(nse); @@ -21928,7 +22141,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR067 - Failed to parse initialiser of for-loop", + "ERR068 - Failed to parse initialiser of for-loop", exprtk_error_location)); result = false; @@ -21938,7 +22151,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR068 - Expected ';' after initialiser of for-loop", + "ERR069 - Expected ';' after initialiser of for-loop", exprtk_error_location)); result = false; @@ -21952,7 +22165,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR069 - Failed to parse condition of for-loop", + "ERR070 - Failed to parse condition of for-loop", exprtk_error_location)); result = false; @@ -21962,7 +22175,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR070 - Expected ';' after condition section of for-loop", + "ERR071 - Expected ';' after condition section of for-loop", exprtk_error_location)); result = false; @@ -21976,7 +22189,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR071 - Failed to parse incrementor of for-loop", + "ERR072 - Failed to parse incrementor of for-loop", exprtk_error_location)); result = false; @@ -21986,7 +22199,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR072 - Expected ')' after incrementor section of for-loop", + "ERR073 - Expected ')' after incrementor section of for-loop", exprtk_error_location)); result = false; @@ -22002,7 +22215,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR073 - Failed to parse body of for-loop", + "ERR074 - Failed to parse body of for-loop", exprtk_error_location)); result = false; @@ -22016,8 +22229,6 @@ namespace exprtk se->ref_count--; } - sem_.cleanup(); - free_node(node_allocator_, initialiser); free_node(node_allocator_, condition); free_node(node_allocator_, incrementor); @@ -22054,7 +22265,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR074 - Expected keyword 'switch'", + "ERR075 - Expected keyword 'switch'", exprtk_error_location)); return error_node(); @@ -22069,7 +22280,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR075 - Expected '{' for call to switch statement", + "ERR076 - Expected '{' for call to switch statement", exprtk_error_location)); return error_node(); @@ -22082,7 +22293,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR076 - Expected either a 'case' or 'default' statement", + "ERR077 - Expected either a 'case' or 'default' statement", exprtk_error_location)); return error_node(); @@ -22099,7 +22310,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR077 - Expected ':' for case of switch statement", + "ERR078 - Expected ':' for case of switch statement", exprtk_error_location)); return error_node(); @@ -22114,7 +22325,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR078 - Expected ';' at end of case for switch statement", + "ERR079 - Expected ';' at end of case for switch statement", exprtk_error_location)); return error_node(); @@ -22140,7 +22351,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR079 - Expected ':' for default of switch statement", + "ERR080 - Expected ':' for default of switch statement", exprtk_error_location)); return error_node(); @@ -22162,7 +22373,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR080 - Expected ';' at end of default for switch statement", + "ERR081 - Expected ';' at end of default for switch statement", exprtk_error_location)); return error_node(); @@ -22178,7 +22389,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR081 - Expected '}' at end of switch statement", + "ERR082 - Expected '}' at end of switch statement", exprtk_error_location)); return error_node(); @@ -22201,7 +22412,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR082 - Expected token '[*]'", + "ERR083 - Expected token '[*]'", exprtk_error_location)); return error_node(); @@ -22216,7 +22427,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR083 - Expected '{' for call to [*] statement", + "ERR084 - Expected '{' for call to [*] statement", exprtk_error_location)); return error_node(); @@ -22229,7 +22440,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR084 - Expected a 'case' statement for multi-switch", + "ERR085 - Expected a 'case' statement for multi-switch", exprtk_error_location)); return error_node(); @@ -22247,7 +22458,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR085 - Expected ':' for case of [*] statement", + "ERR086 - Expected ':' for case of [*] statement", exprtk_error_location)); return error_node(); @@ -22263,7 +22474,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR086 - Expected ';' at end of case for [*] statement", + "ERR087 - Expected ';' at end of case for [*] statement", exprtk_error_location)); return error_node(); @@ -22277,7 +22488,7 @@ namespace exprtk } else { - arg_list.push_back(condition); + arg_list.push_back( condition); arg_list.push_back(consequent); } @@ -22292,7 +22503,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR087 - Expected '}' at end of [*] statement", + "ERR088 - Expected '}' at end of [*] statement", exprtk_error_location)); return error_node(); @@ -22334,7 +22545,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR088 - Unsupported vararg function: " + symbol, + "ERR089 - Unsupported vararg function: " + symbol, exprtk_error_location)); return error_node(); @@ -22342,7 +22553,7 @@ namespace exprtk scoped_vec_delete<expression_node_t> sdd((*this),arg_list); - lodge_symbol(symbol,e_st_function); + lodge_symbol(symbol, e_st_function); next_token(); @@ -22351,7 +22562,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR089 - Expected '(' for call to vararg function: " + symbol, + "ERR090 - Expected '(' for call to vararg function: " + symbol, exprtk_error_location)); return error_node(); @@ -22373,7 +22584,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR090 - Expected ',' for call to vararg function: " + symbol, + "ERR091 - Expected ',' for call to vararg function: " + symbol, exprtk_error_location)); return error_node(); @@ -22394,7 +22605,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR091 - Expected '[' as start of string range definition", + "ERR092 - Expected '[' as start of string range definition", exprtk_error_location)); free_node(node_allocator_,expression); @@ -22422,7 +22633,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR092 - Failed to generate string range node", + "ERR093 - Failed to generate string range node", exprtk_error_location)); free_node(node_allocator_,expression); @@ -22461,7 +22672,7 @@ namespace exprtk template <typename Allocator1, typename Allocator2, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node_ptr simplify(Sequence<expression_node_ptr,Allocator1>& expression_list, Sequence<bool,Allocator2>& side_effect_list, const bool specialise_on_final_type = false) @@ -22558,7 +22769,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR093 - Expected '" + token_t::to_str(close_bracket) + "' for call to multi-sequence" + + "ERR094 - Expected '" + token_t::to_str(close_bracket) + "' for call to multi-sequence" + ((!source.empty()) ? std::string(" section of " + source): ""), exprtk_error_location)); @@ -22605,7 +22816,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR094 - Expected '" + details::to_str(seperator) + "' for call to multi-sequence section of " + source, + "ERR095 - Expected '" + details::to_str(seperator) + "' for call to multi-sequence section of " + source, exprtk_error_location)); return error_node(); @@ -22639,7 +22850,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR095 - Expected '[' for start of range", + "ERR096 - Expected '[' for start of range", exprtk_error_location)); return false; @@ -22660,11 +22871,10 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR096 - Failed parse begin section of range", + "ERR097 - Failed parse begin section of range", exprtk_error_location)); return false; - } else if (is_constant_node(r0)) { @@ -22684,7 +22894,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR097 - Range lower bound less than zero! Constraint: r0 >= 0", + "ERR098 - Range lower bound less than zero! Constraint: r0 >= 0", exprtk_error_location)); return false; @@ -22701,7 +22911,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR098 - Expected ':' for break in range", + "ERR099 - Expected ':' for break in range", exprtk_error_location)); rp.free(); @@ -22724,13 +22934,12 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR099 - Failed parse end section of range", + "ERR100 - Failed parse end section of range", exprtk_error_location)); rp.free(); return false; - } else if (is_constant_node(r1)) { @@ -22750,7 +22959,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR100 - Range upper bound less than zero! Constraint: r1 >= 0", + "ERR101 - Range upper bound less than zero! Constraint: r1 >= 0", exprtk_error_location)); return false; @@ -22767,7 +22976,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR101 - Expected ']' for start of range", + "ERR102 - Expected ']' for start of range", exprtk_error_location)); rp.free(); @@ -22788,7 +22997,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR102 - Invalid range, Constraint: r0 <= r1", + "ERR103 - Invalid range, Constraint: r0 <= r1", exprtk_error_location)); return false; @@ -22820,7 +23029,7 @@ namespace exprtk { se.active = true; result = se.str_node; - lodge_symbol(symbol,e_st_local_string); + lodge_symbol(symbol, e_st_local_string); } else { @@ -22829,7 +23038,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR103 - Unknown string symbol", + "ERR104 - Unknown string symbol", exprtk_error_location)); return error_node(); @@ -22843,7 +23052,7 @@ namespace exprtk result = expression_generator_(const_str_node->str()); } - lodge_symbol(symbol,e_st_string); + lodge_symbol(symbol, e_st_string); } if (peek_token_is(token_t::e_lsqrbracket)) @@ -22943,7 +23152,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR104 - Overflow in range for string: '" + const_str + "'[" + + "ERR105 - Overflow in range for string: '" + const_str + "'[" + (rp.n0_c.first ? details::to_str(static_cast<int>(rp.n0_c.second)) : "?") + ":" + (rp.n1_c.first ? details::to_str(static_cast<int>(rp.n1_c.second)) : "?") + "]", exprtk_error_location)); @@ -22987,7 +23196,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR105 - Symbol '" + symbol+ " not a vector", + "ERR106 - Symbol '" + symbol+ " not a vector", exprtk_error_location)); return error_node(); @@ -23013,7 +23222,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR106 - Failed to parse index for vector: '" + symbol + "'", + "ERR107 - Failed to parse index for vector: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -23023,7 +23232,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR107 - Expected ']' for index of vector: '" + symbol + "'", + "ERR108 - Expected ']' for index of vector: '" + symbol + "'", exprtk_error_location)); free_node(node_allocator_,index_expr); @@ -23042,7 +23251,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR108 - Index of " + details::to_str(index) + " out of range for " + "ERR109 - Index of " + details::to_str(index) + " out of range for " "vector '" + symbol + "' of size " + details::to_str(vec_size), exprtk_error_location)); @@ -23074,7 +23283,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR109 - Zero parameter call to vararg function: " + "ERR110 - Zero parameter call to vararg function: " + vararg_function_name + " not allowed", exprtk_error_location)); @@ -23099,7 +23308,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR110 - Expected ',' for call to vararg function: " + "ERR111 - Expected ',' for call to vararg function: " + vararg_function_name, exprtk_error_location)); @@ -23113,7 +23322,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR111 - Zero parameter call to vararg function: " + "ERR112 - Zero parameter call to vararg function: " + vararg_function_name + " not allowed", exprtk_error_location)); @@ -23125,7 +23334,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR112 - Invalid number of parameters to call to vararg function: " + "ERR113 - Invalid number of parameters to call to vararg function: " + vararg_function_name + ", require at least " + details::to_str(static_cast<int>(vararg_function->min_num_args())) + " parameters", exprtk_error_location)); @@ -23137,7 +23346,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR113 - Invalid number of parameters to call to vararg function: " + "ERR114 - Invalid number of parameters to call to vararg function: " + vararg_function_name + ", require no more than " + details::to_str(static_cast<int>(vararg_function->max_num_args())) + " parameters", exprtk_error_location)); @@ -23156,34 +23365,54 @@ namespace exprtk { public: + enum return_type_t + { + e_overload = ' ', + e_numeric = 'T', + e_string = 'S' + }; + + struct function_prototype_t + { + return_type_t return_type; + std::string param_seq; + }; + typedef parser<T> parser_t; - typedef std::vector<std::string> param_seq_list_t; + typedef std::vector<function_prototype_t> function_definition_list_t; type_checker(parser_t& p, const std::string& func_name, - const std::string& param_seq) + const std::string& func_prototypes, + const return_type_t default_return_type) : invalid_state_(true), parser_(p), - function_name_(func_name) + function_name_(func_name), + default_return_type_(default_return_type) + { + parse_function_prototypes(func_prototypes); + } + + void set_default_return_type(const std::string& return_type) { - split(param_seq); + default_return_type_ = return_type; } bool verify(const std::string& param_seq, std::size_t& pseq_index) { - if (param_seq_list_.empty()) + if (function_definition_list_.empty()) return true; std::vector<std::pair<std::size_t,char> > error_list; - for (std::size_t i = 0; i < param_seq_list_.size(); ++i) + for (std::size_t i = 0; i < function_definition_list_.size(); ++i) { details::char_t diff_value = 0; std::size_t diff_index = 0; - bool result = details::sequence_match(param_seq_list_[i], - param_seq, - diff_index,diff_value); + const bool result = details::sequence_match(function_definition_list_[i].param_seq, + param_seq, + diff_index, diff_value); if (result) { @@ -23191,7 +23420,7 @@ namespace exprtk return true; } else - error_list.push_back(std::make_pair(diff_index,diff_value)); + error_list.push_back(std::make_pair(diff_index, diff_value)); } if (1 == error_list.size()) @@ -23200,8 +23429,9 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, parser_.current_token(), - "ERR114 - Failed parameter type check for function '" + function_name_ + "', " - "Expected '" + param_seq_list_[0] + "' call set: '" + param_seq +"'", + "ERR115 - Failed parameter type check for function '" + function_name_ + "', " + "Expected '" + function_definition_list_[0].param_seq + + "' call set: '" + param_seq + "'", exprtk_error_location)); } else @@ -23221,8 +23451,9 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, parser_.current_token(), - "ERR115 - Failed parameter type check for function '" + function_name_ + "', " - "Best match: '" + param_seq_list_[max_diff_index] + "' call set: '" + param_seq +"'", + "ERR116 - Failed parameter type check for function '" + function_name_ + "', " + "Best match: '" + function_definition_list_[max_diff_index].param_seq + + "' call set: '" + param_seq + "'", exprtk_error_location)); } @@ -23231,12 +23462,17 @@ namespace exprtk std::size_t paramseq_count() const { - return param_seq_list_.size(); + return function_definition_list_.size(); } std::string paramseq(const std::size_t& index) const { - return param_seq_list_[index]; + return function_definition_list_[index].param_seq; + } + + return_type_t return_type(const std::size_t& index) const + { + return function_definition_list_[index].return_type; } bool invalid() const @@ -23246,93 +23482,143 @@ namespace exprtk bool allow_zero_parameters() const { - return - param_seq_list_.end() != std::find(param_seq_list_.begin(), - param_seq_list_.end(), - "Z"); + + for (std::size_t i = 0; i < function_definition_list_.size(); ++i) + { + if (std::string::npos != function_definition_list_[i].param_seq.find("Z")) + { + return true; + } + } + + return false; } private: - void split(const std::string& s) + std::vector<std::string> split_param_seq(const std::string& param_seq, const details::char_t delimiter = '|') const { - if (s.empty()) - return; + std::string::const_iterator current_begin = param_seq.begin(); + std::string::const_iterator iter = param_seq.begin(); + + std::vector<std::string> result; - std::size_t start = 0; - std::size_t end = 0; + while (iter != param_seq.end()) + { + if (*iter == delimiter) + { + result.push_back(std::string(current_begin, iter)); + ++iter; + current_begin = iter; + } + else + ++iter; + } - param_seq_list_t param_seq_list; + if (current_begin != iter) + { + result.push_back(std::string(current_begin, iter)); + } + + return result; + } + + inline bool is_valid_token(std::string param_seq, + function_prototype_t& funcproto) const + { + // Determine return type + funcproto.return_type = default_return_type_; - struct token_validator + if (param_seq.size() > 2) { - static inline bool process(const std::string& str, - std::size_t s, std::size_t e, - param_seq_list_t& psl) + if (':' == param_seq[1]) { - if ( - (e - s) && - (std::string::npos == str.find("?*")) && - (std::string::npos == str.find("**")) - ) + // Note: Only overloaded igeneric functions can have return + // type definitions. + if (type_checker::e_overload != default_return_type_) + return false; + + switch (param_seq[0]) { - const std::string curr_str = str.substr(s, e - s); + case 'T' : funcproto.return_type = type_checker::e_numeric; + break; - if ("Z" == curr_str) - { - psl.push_back(curr_str); - return true; - } - else if (std::string::npos == curr_str.find_first_not_of("STV*?|")) - { - psl.push_back(curr_str); - return true; - } + case 'S' : funcproto.return_type = type_checker::e_string; + break; + + default : return false; } - return false; + param_seq.erase(0,2); } - }; + } - while (std::string::npos != (end = s.find('|',start))) + if ( + (std::string::npos != param_seq.find("?*")) || + (std::string::npos != param_seq.find("**")) + ) { - if (!token_validator::process(s, start, end, param_seq_list)) + return false; + } + else if ( + (std::string::npos == param_seq.find_first_not_of("STV*?|")) || + ("Z" == param_seq) + ) + { + funcproto.param_seq = param_seq; + return true; + } + + return false; + } + + void parse_function_prototypes(const std::string& func_prototypes) + { + if (func_prototypes.empty()) + return; + + std::vector<std::string> param_seq_list = split_param_seq(func_prototypes); + + typedef std::map<std::string,std::size_t> param_seq_map_t; + param_seq_map_t param_seq_map; + + for (std::size_t i = 0; i < param_seq_list.size(); ++i) + { + function_prototype_t func_proto; + + if (!is_valid_token(param_seq_list[i], func_proto)) { invalid_state_ = false; - const std::string err_param_seq = s.substr(start, end - start); - parser_. set_error( make_error(parser_error::e_syntax, parser_.current_token(), - "ERR116 - Invalid parameter sequence of '" + err_param_seq + - "' for function: " + function_name_, + "ERR117 - Invalid parameter sequence of '" + param_seq_list[i] + + "' for function: " + function_name_, exprtk_error_location)); - return; } - else - start = end + 1; - } - if (start < s.size()) - { - if (token_validator::process(s, start, s.size(), param_seq_list)) - param_seq_list_ = param_seq_list; - else + param_seq_map_t::const_iterator seq_itr = param_seq_map.find(param_seq_list[i]); + + if (param_seq_map.end() != seq_itr) { - const std::string err_param_seq = s.substr(start, s.size() - start); + invalid_state_ = false; parser_. set_error( make_error(parser_error::e_syntax, parser_.current_token(), - "ERR117 - Invalid parameter sequence of '" + err_param_seq + - "' for function: " + function_name_, + "ERR118 - Function '" + function_name_ + "' has a parameter sequence conflict between " + + "pseq_idx[" + details::to_str(seq_itr->second) + "] and" + + "pseq_idx[" + details::to_str(i) + "] " + + "param seq: " + param_seq_list[i], exprtk_error_location)); return; } + + function_definition_list_.push_back(func_proto); } } @@ -23342,7 +23628,8 @@ namespace exprtk bool invalid_state_; parser_t& parser_; std::string function_name_; - param_seq_list_t param_seq_list_; + const return_type_t default_return_type_; + function_definition_list_t function_definition_list_; }; inline expression_node_ptr parse_generic_function_call(igeneric_function<T>* function, const std::string& function_name) @@ -23355,30 +23642,14 @@ namespace exprtk std::string param_type_list; - type_checker tc((*this), function_name, function->parameter_sequence); + type_checker tc((*this), function_name, function->parameter_sequence, type_checker::e_string); if (tc.invalid()) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR118 - Type checker instantiation failure for generic function: " + function_name, - exprtk_error_location)); - - return error_node(); - } - - if ( - !function->parameter_sequence.empty() && - function->allow_zero_parameters () && - !tc .allow_zero_parameters () - ) - { - set_error( - make_error(parser_error::e_syntax, - current_token(), - "ERR119 - Mismatch in zero parameter condition for generic function: " - + function_name, + "ERR119 - Type checker instantiation failure for generic function: " + function_name, exprtk_error_location)); return error_node(); @@ -23462,7 +23733,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR123 - Expected ',' for call to generic function: " + function_name, + "ERR123 - Invalid input parameter sequence for call to generic function: " + function_name, exprtk_error_location)); return error_node(); @@ -23482,37 +23753,39 @@ namespace exprtk return result; } - #ifndef exprtk_disable_string_capabilities - inline expression_node_ptr parse_string_function_call(igeneric_function<T>* function, const std::string& function_name) + inline bool parse_igeneric_function_params(std::string& param_type_list, + std::vector<expression_node_ptr>& arg_list, + const std::string function_name, + igeneric_function<T>* function, + const type_checker& tc) { - std::vector<expression_node_ptr> arg_list; - - scoped_vec_delete<expression_node_t> sdd((*this),arg_list); - - next_token(); - - std::string param_type_list; - - type_checker tc((*this), function_name, function->parameter_sequence); - - if ( - (!function->parameter_sequence.empty()) && - (0 == tc.paramseq_count()) - ) - { - return error_node(); - } - if (token_is(token_t::e_lbracket)) { - if (!token_is(token_t::e_rbracket)) + if (token_is(token_t::e_rbracket)) + { + if ( + !function->allow_zero_parameters() && + !tc .allow_zero_parameters() + ) + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR124 - Zero parameter call to generic function: " + + function_name + " not allowed", + exprtk_error_location)); + + return false; + } + } + else { for ( ; ; ) { expression_node_ptr arg = parse_expression(); if (0 == arg) - return error_node(); + return false; if (is_ivector_node(arg)) param_type_list += 'V'; @@ -23530,13 +23803,44 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR124 - Expected ',' for call to string function: " + function_name, + "ERR125 - Expected ',' for call to string function: " + function_name, exprtk_error_location)); - return error_node(); + return false; } } } + + return true; + } + else + return false; + } + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr parse_string_function_call(igeneric_function<T>* function, const std::string& function_name) + { + // Move pass the function name + next_token(); + + std::string param_type_list; + + type_checker tc((*this), function_name, function->parameter_sequence, type_checker::e_string); + + if ( + (!function->parameter_sequence.empty()) && + (0 == tc.paramseq_count()) + ) + { + return error_node(); + } + + std::vector<expression_node_ptr> arg_list; + scoped_vec_delete<expression_node_t> sdd((*this),arg_list); + + if (!parse_igeneric_function_params(param_type_list, arg_list, function_name, function, tc)) + { + return error_node(); } std::size_t param_seq_index = 0; @@ -23546,7 +23850,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR125 - Expected ',' for call to string function: " + function_name, + "ERR126 - Invalid input parameter sequence for call to string function: " + function_name, exprtk_error_location)); return error_node(); @@ -23565,6 +23869,77 @@ namespace exprtk return result; } + + inline expression_node_ptr parse_overload_function_call(igeneric_function<T>* function, const std::string& function_name) + { + // Move pass the function name + next_token(); + + std::string param_type_list; + + type_checker tc((*this), function_name, function->parameter_sequence, type_checker::e_overload); + + if ( + (!function->parameter_sequence.empty()) && + (0 == tc.paramseq_count()) + ) + { + return error_node(); + } + + std::vector<expression_node_ptr> arg_list; + scoped_vec_delete<expression_node_t> sdd((*this),arg_list); + + if (!parse_igeneric_function_params(param_type_list, arg_list, function_name, function, tc)) + { + return error_node(); + } + + std::size_t param_seq_index = 0; + + if (!tc.verify(param_type_list, param_seq_index)) + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR127 - Invalid input parameter sequence for call to overloaded function: " + function_name, + exprtk_error_location)); + + return error_node(); + } + + expression_node_ptr result = error_node(); + + if (type_checker::e_numeric == tc.return_type(param_seq_index)) + { + if (tc.paramseq_count() <= 1) + result = expression_generator_ + .generic_function_call(function, arg_list); + else + result = expression_generator_ + .generic_function_call(function, arg_list, param_seq_index); + } + else if (type_checker::e_string == tc.return_type(param_seq_index)) + { + if (tc.paramseq_count() <= 1) + result = expression_generator_ + .string_function_call(function, arg_list); + else + result = expression_generator_ + .string_function_call(function, arg_list, param_seq_index); + } + else + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR128 - Invalid return type for call to overloaded function: " + function_name, + exprtk_error_location)); + } + + sdd.delete_ptr = (0 == result); + return result; + } #endif template <typename Type, std::size_t NumberOfParameters> @@ -23586,7 +23961,7 @@ namespace exprtk p.set_error( make_error(parser_error::e_syntax, p.current_token(), - "ERR126 - Expected '(' for special function '" + sf_name + "'", + "ERR129 - Expected '(' for special function '" + sf_name + "'", exprtk_error_location)); return error_node(); @@ -23607,7 +23982,7 @@ namespace exprtk p.set_error( make_error(parser_error::e_syntax, p.current_token(), - "ERR127 - Expected ',' before next parameter of special function '" + sf_name + "'", + "ERR130 - Expected ',' before next parameter of special function '" + sf_name + "'", exprtk_error_location)); return p.error_node(); @@ -23620,7 +23995,7 @@ namespace exprtk p.set_error( make_error(parser_error::e_syntax, p.current_token(), - "ERR128 - Invalid number of parameters for special function '" + sf_name + "'", + "ERR131 - Invalid number of parameters for special function '" + sf_name + "'", exprtk_error_location)); return p.error_node(); @@ -23647,7 +24022,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, current_token(), - "ERR129 - Invalid special function[1]: " + sf_name, + "ERR132 - Invalid special function[1]: " + sf_name, exprtk_error_location)); return error_node(); @@ -23661,7 +24036,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, current_token(), - "ERR130 - Invalid special function[2]: " + sf_name, + "ERR133 - Invalid special function[2]: " + sf_name, exprtk_error_location)); return error_node(); @@ -23693,7 +24068,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR131 - Break call within a break call is not allowed", + "ERR134 - Break call within a break call is not allowed", exprtk_error_location)); return error_node(); @@ -23716,7 +24091,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR132 - Failed to parse return expression for 'break' statement", + "ERR135 - Failed to parse return expression for 'break' statement", exprtk_error_location)); return error_node(); @@ -23726,7 +24101,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR133 - Expected ']' at the completion of break's return expression", + "ERR136 - Expected ']' at the completion of break's return expression", exprtk_error_location)); free_node(node_allocator_,return_expr); @@ -23744,7 +24119,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR134 - Invalid use of 'break', allowed only in the scope of a loop", + "ERR137 - Invalid use of 'break', allowed only in the scope of a loop", exprtk_error_location)); } @@ -23767,7 +24142,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR135 - Invalid use of 'continue', allowed only in the scope of a loop", + "ERR138 - Invalid use of 'continue', allowed only in the scope of a loop", exprtk_error_location)); return error_node(); @@ -23784,7 +24159,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR136 - Expected '[' as part of vector size definition", + "ERR139 - Expected '[' as part of vector size definition", exprtk_error_location)); return error_node(); @@ -23794,7 +24169,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR137 - Failed to determine size of vector '" + vec_name + "'", + "ERR140 - Failed to determine size of vector '" + vec_name + "'", exprtk_error_location)); return error_node(); @@ -23806,7 +24181,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR138 - Expected a literal number as size of vector '" + vec_name + "'", + "ERR141 - Expected a literal number as size of vector '" + vec_name + "'", exprtk_error_location)); return error_node(); @@ -23816,16 +24191,19 @@ namespace exprtk free_node(node_allocator_,size_expr); + const T max_vector_size = T(2000000000.0); + if ( (vector_size <= T(0)) || std::not_equal_to<T>() - (T(0),vector_size - details::numeric::trunc(vector_size)) + (T(0),vector_size - details::numeric::trunc(vector_size)) || + (vector_size > max_vector_size) ) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR139 - Invalid vector size. Must be an integer greater than zero, size: " + + "ERR142 - Invalid vector size. Must be an integer in the range [0,2e9], size: " + details::to_str(details::numeric::to_int32(vector_size)), exprtk_error_location)); @@ -23845,7 +24223,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR140 - Expected ']' as part of vector size definition", + "ERR143 - Expected ']' as part of vector size definition", exprtk_error_location)); return error_node(); @@ -23857,7 +24235,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR141 - Expected ':=' as part of vector definition", + "ERR144 - Expected ':=' as part of vector definition", exprtk_error_location)); return error_node(); @@ -23871,7 +24249,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR142 - Failed to parse single vector initialiser", + "ERR145 - Failed to parse single vector initialiser", exprtk_error_location)); return error_node(); @@ -23884,7 +24262,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR143 - Expected ']' to close single value vector initialiser", + "ERR146 - Expected ']' to close single value vector initialiser", exprtk_error_location)); return error_node(); @@ -23912,7 +24290,7 @@ namespace exprtk // Are we dealing with a user defined vector? else if (symtab_store_.is_vector(current_token().value)) { - lodge_symbol(current_token().value,e_st_vector); + lodge_symbol(current_token().value, e_st_vector); if (0 != (initialiser = parse_expression())) vec_initilizer_list.push_back(initialiser); @@ -23931,7 +24309,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR144 - Expected '{' as part of vector initialiser list", + "ERR147 - Expected '{' as part of vector initialiser list", exprtk_error_location)); return error_node(); @@ -23951,7 +24329,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR145 - Expected '{' as part of vector initialiser list", + "ERR148 - Expected '{' as part of vector initialiser list", exprtk_error_location)); return error_node(); @@ -23969,7 +24347,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR146 - Expected ',' between vector initialisers", + "ERR149 - Expected ',' between vector initialisers", exprtk_error_location)); return error_node(); @@ -23981,9 +24359,9 @@ namespace exprtk } if ( - !token_is(token_t::e_rbracket ,prsrhlpr_t::e_hold) && - !token_is(token_t::e_rcrlbracket,prsrhlpr_t::e_hold) && - !token_is(token_t::e_rsqrbracket,prsrhlpr_t::e_hold) + !token_is(token_t::e_rbracket , prsrhlpr_t::e_hold) && + !token_is(token_t::e_rcrlbracket, prsrhlpr_t::e_hold) && + !token_is(token_t::e_rsqrbracket, prsrhlpr_t::e_hold) ) { if (!token_is(token_t::e_eof)) @@ -23991,7 +24369,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR147 - Expected ';' at end of vector definition", + "ERR150 - Expected ';' at end of vector definition", exprtk_error_location)); return error_node(); @@ -24003,7 +24381,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR148 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'", + "ERR151 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'", exprtk_error_location)); return error_node(); @@ -24023,7 +24401,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR149 - Illegal redefinition of local vector: '" + vec_name + "'", + "ERR152 - Illegal redefinition of local vector: '" + vec_name + "'", exprtk_error_location)); return error_node(); @@ -24057,7 +24435,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR150 - Failed to add new local vector '" + vec_name + "' to SEM", + "ERR153 - Failed to add new local vector '" + vec_name + "' to SEM", exprtk_error_location)); sem_.free_element(nse); @@ -24074,17 +24452,21 @@ namespace exprtk state_.activate_side_effect("parse_define_vector_statement()"); - lodge_symbol(vec_name,e_st_local_vector); + lodge_symbol(vec_name, e_st_local_vector); expression_node_ptr result = error_node(); if (null_initialisation) result = expression_generator_(T(0.0)); else if (vec_to_vec_initialiser) + { + expression_node_ptr vec_node = node_allocator_.allocate<vector_node_t>(vec_holder); + result = expression_generator_( details::e_assign, - node_allocator_.allocate<vector_node_t>(vec_holder), + vec_node, vec_initilizer_list[0]); + } else result = node_allocator_ .allocate<details::vector_assignment_node<T> >( @@ -24112,7 +24494,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR151 - Illegal redefinition of local variable: '" + str_name + "'", + "ERR154 - Illegal redefinition of local variable: '" + str_name + "'", exprtk_error_location)); free_node(node_allocator_,initialisation_expression); @@ -24144,7 +24526,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR152 - Failed to add new local string variable '" + str_name + "' to SEM", + "ERR155 - Failed to add new local string variable '" + str_name + "' to SEM", exprtk_error_location)); free_node(node_allocator_,initialisation_expression); @@ -24159,7 +24541,7 @@ namespace exprtk exprtk_debug(("parse_define_string_statement() - INFO - Added new local string variable: %s\n",nse.name.c_str())); } - lodge_symbol(str_name,e_st_local_string); + lodge_symbol(str_name, e_st_local_string); state_.activate_side_effect("parse_define_string_statement()"); @@ -24190,7 +24572,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR153 - Illegal variable definition", + "ERR156 - Illegal variable definition", exprtk_error_location)); return error_node(); @@ -24211,7 +24593,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR154 - Expected a symbol for variable definition", + "ERR157 - Expected a symbol for variable definition", exprtk_error_location)); return error_node(); @@ -24221,7 +24603,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR155 - Illegal redefinition of reserved keyword: '" + var_name + "'", + "ERR158 - Illegal redefinition of reserved keyword: '" + var_name + "'", exprtk_error_location)); return error_node(); @@ -24231,7 +24613,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR156 - Illegal redefinition of variable '" + var_name + "'", + "ERR159 - Illegal redefinition of variable '" + var_name + "'", exprtk_error_location)); return error_node(); @@ -24241,7 +24623,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR157 - Illegal redefinition of local variable: '" + var_name + "'", + "ERR160 - Illegal redefinition of local variable: '" + var_name + "'", exprtk_error_location)); return error_node(); @@ -24261,7 +24643,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR158 - Failed to parse initialisation expression", + "ERR161 - Failed to parse initialisation expression", exprtk_error_location)); return error_node(); @@ -24269,9 +24651,9 @@ namespace exprtk } if ( - !token_is(token_t::e_rbracket ,prsrhlpr_t::e_hold) && - !token_is(token_t::e_rcrlbracket,prsrhlpr_t::e_hold) && - !token_is(token_t::e_rsqrbracket,prsrhlpr_t::e_hold) + !token_is(token_t::e_rbracket , prsrhlpr_t::e_hold) && + !token_is(token_t::e_rcrlbracket, prsrhlpr_t::e_hold) && + !token_is(token_t::e_rsqrbracket, prsrhlpr_t::e_hold) ) { if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold)) @@ -24279,7 +24661,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR159 - Expected ';' after variable definition", + "ERR162 - Expected ';' after variable definition", exprtk_error_location)); free_node(node_allocator_,initialisation_expression); @@ -24307,7 +24689,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR160 - Illegal redefinition of local variable: '" + var_name + "'", + "ERR163 - Illegal redefinition of local variable: '" + var_name + "'", exprtk_error_location)); free_node(node_allocator_, initialisation_expression); @@ -24339,7 +24721,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR161 - Failed to add new local variable '" + var_name + "' to SEM", + "ERR164 - Failed to add new local variable '" + var_name + "' to SEM", exprtk_error_location)); free_node(node_allocator_, initialisation_expression); @@ -24356,7 +24738,7 @@ namespace exprtk state_.activate_side_effect("parse_define_var_statement()"); - lodge_symbol(var_name,e_st_local_variable); + lodge_symbol(var_name, e_st_local_variable); expression_node_ptr branch[2] = {0}; @@ -24376,7 +24758,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR162 - Expected a '{}' for uninitialised var definition", + "ERR165 - Expected a '{}' for uninitialised var definition", exprtk_error_location)); return error_node(); @@ -24386,7 +24768,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR163 - Expected ';' after uninitialised variable definition", + "ERR166 - Expected ';' after uninitialised variable definition", exprtk_error_location)); return error_node(); @@ -24403,7 +24785,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR164 - Illegal redefinition of local variable: '" + var_name + "'", + "ERR167 - Illegal redefinition of local variable: '" + var_name + "'", exprtk_error_location)); return error_node(); @@ -24433,7 +24815,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR165 - Failed to add new local variable '" + var_name + "' to SEM", + "ERR168 - Failed to add new local variable '" + var_name + "' to SEM", exprtk_error_location)); sem_.free_element(nse); @@ -24445,7 +24827,7 @@ namespace exprtk nse.name.c_str())); } - lodge_symbol(var_name,e_st_local_variable); + lodge_symbol(var_name, e_st_local_variable); state_.activate_side_effect("parse_uninitialised_var_statement()"); @@ -24466,7 +24848,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR166 - Expected '(' at start of swap statement", + "ERR169 - Expected '(' at start of swap statement", exprtk_error_location)); return error_node(); @@ -24485,7 +24867,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR167 - Expected a symbol for variable or vector element definition", + "ERR170 - Expected a symbol for variable or vector element definition", exprtk_error_location)); return error_node(); @@ -24497,7 +24879,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR168 - First parameter to swap is an invalid vector element: '" + var0_name + "'", + "ERR171 - First parameter to swap is an invalid vector element: '" + var0_name + "'", exprtk_error_location)); return error_node(); @@ -24523,14 +24905,14 @@ namespace exprtk variable0 = se.var_node; } - lodge_symbol(var0_name,e_st_variable); + lodge_symbol(var0_name, e_st_variable); if (0 == variable0) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR169 - First parameter to swap is an invalid variable: '" + var0_name + "'", + "ERR172 - First parameter to swap is an invalid variable: '" + var0_name + "'", exprtk_error_location)); return error_node(); @@ -24544,7 +24926,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR170 - Expected ',' between parameters to swap", + "ERR173 - Expected ',' between parameters to swap", exprtk_error_location)); if (variable0_generated) @@ -24562,7 +24944,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR171 - Expected a symbol for variable or vector element definition", + "ERR174 - Expected a symbol for variable or vector element definition", exprtk_error_location)); if (variable0_generated) @@ -24579,7 +24961,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR172 - Second parameter to swap is an invalid vector element: '" + var1_name + "'", + "ERR175 - Second parameter to swap is an invalid vector element: '" + var1_name + "'", exprtk_error_location)); if (variable0_generated) @@ -24610,14 +24992,14 @@ namespace exprtk variable1 = se.var_node; } - lodge_symbol(var1_name,e_st_variable); + lodge_symbol(var1_name, e_st_variable); if (0 == variable1) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR173 - Second parameter to swap is an invalid variable: '" + var1_name + "'", + "ERR176 - Second parameter to swap is an invalid variable: '" + var1_name + "'", exprtk_error_location)); if (variable0_generated) @@ -24636,7 +25018,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR174 - Expected ')' at end of swap statement", + "ERR177 - Expected ')' at end of swap statement", exprtk_error_location)); if (variable0_generated) @@ -24693,7 +25075,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR175 - Return call within a return call is not allowed", + "ERR178 - Return call within a return call is not allowed", exprtk_error_location)); return error_node(); @@ -24717,7 +25099,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR176 - Expected '[' at start of return statement", + "ERR179 - Expected '[' at start of return statement", exprtk_error_location)); return error_node(); @@ -24740,7 +25122,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR177 - Expected ',' between values during call to return", + "ERR180 - Expected ',' between values during call to return", exprtk_error_location)); return error_node(); @@ -24752,13 +25134,13 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR178 - Zero parameter return statement not allowed", + "ERR181 - Zero parameter return statement not allowed", exprtk_error_location)); return error_node(); } - lexer::token prev_token = current_token(); + const lexer::token prev_token = current_token(); if (token_is(token_t::e_rsqrbracket)) { @@ -24767,7 +25149,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, prev_token, - "ERR179 - Invalid ']' found during return call", + "ERR182 - Invalid ']' found during return call", exprtk_error_location)); return error_node(); @@ -24820,7 +25202,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR180 - Invalid sequence of variable '"+ symbol + "' and bracket", + "ERR183 - Invalid sequence of variable '"+ symbol + "' and bracket", exprtk_error_location)); return false; @@ -24868,7 +25250,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR181 - Invalid sequence of brackets", + "ERR184 - Invalid sequence of brackets", exprtk_error_location)); return false; @@ -24901,7 +25283,7 @@ namespace exprtk if (!post_variable_process(symbol)) return error_node(); - lodge_symbol(symbol,e_st_variable); + lodge_symbol(symbol, e_st_variable); next_token(); return variable; @@ -24917,7 +25299,7 @@ namespace exprtk if (scope_element::e_variable == se.type) { se.active = true; - lodge_symbol(symbol,e_st_local_variable); + lodge_symbol(symbol, e_st_local_variable); if (!post_variable_process(symbol)) return error_node(); @@ -24953,7 +25335,7 @@ namespace exprtk if (function) { - lodge_symbol(symbol,e_st_function); + lodge_symbol(symbol, e_st_function); expression_node_ptr func_node = parse_function_invocation(function,symbol); @@ -24965,7 +25347,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR182 - Failed to generate node for function: '" + symbol + "'", + "ERR185 - Failed to generate node for function: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -24979,7 +25361,7 @@ namespace exprtk if (vararg_function) { - lodge_symbol(symbol,e_st_function); + lodge_symbol(symbol, e_st_function); expression_node_ptr vararg_func_node = parse_vararg_function_call(vararg_function, symbol); @@ -24991,7 +25373,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR183 - Failed to generate node for vararg function: '" + symbol + "'", + "ERR186 - Failed to generate node for vararg function: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25005,7 +25387,7 @@ namespace exprtk if (generic_function) { - lodge_symbol(symbol,e_st_function); + lodge_symbol(symbol, e_st_function); expression_node_ptr genericfunc_node = parse_generic_function_call(generic_function, symbol); @@ -25017,7 +25399,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR184 - Failed to generate node for generic function: '" + symbol + "'", + "ERR187 - Failed to generate node for generic function: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25032,7 +25414,7 @@ namespace exprtk if (string_function) { - lodge_symbol(symbol,e_st_function); + lodge_symbol(symbol, e_st_function); expression_node_ptr stringfunc_node = parse_string_function_call(string_function, symbol); @@ -25044,7 +25426,33 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR185 - Failed to generate node for string function: '" + symbol + "'", + "ERR188 - Failed to generate node for string function: '" + symbol + "'", + exprtk_error_location)); + + return error_node(); + } + } + } + + { + // Are we dealing with a vararg overloaded scalar/string returning function? + igeneric_function<T>* overload_function = symtab_store_.get_overload_function(symbol); + + if (overload_function) + { + lodge_symbol(symbol, e_st_function); + + expression_node_ptr overloadfunc_node = + parse_overload_function_call(overload_function, symbol); + + if (overloadfunc_node) + return overloadfunc_node; + else + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR189 - Failed to generate node for overload function: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25056,7 +25464,7 @@ namespace exprtk // Are we dealing with a vector? if (symtab_store_.is_vector(symbol)) { - lodge_symbol(symbol,e_st_vector); + lodge_symbol(symbol, e_st_vector); return parse_vector(); } @@ -25070,7 +25478,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR186 - Invalid use of reserved symbol '" + symbol + "'", + "ERR190 - Invalid use of reserved symbol '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25090,7 +25498,7 @@ namespace exprtk { T default_value = T(0); - typename unknown_symbol_resolver::usr_symbol_type usr_symbol_type; + typename unknown_symbol_resolver::usr_symbol_type usr_symbol_type = unknown_symbol_resolver::e_usr_unknown_type; if (unknown_symbol_resolver_->process(symbol, usr_symbol_type, default_value, error_message)) { @@ -25118,7 +25526,7 @@ namespace exprtk var = expression_generator_(var->value()); } - lodge_symbol(symbol,e_st_variable); + lodge_symbol(symbol, e_st_variable); if (!post_variable_process(symbol)) return error_node(); @@ -25133,7 +25541,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token(), - "ERR187 - Failed to create variable: '" + symbol + "'" + + "ERR191 - Failed to create variable: '" + symbol + "'" + (error_message.empty() ? "" : " - " + error_message), exprtk_error_location)); @@ -25153,7 +25561,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token(), - "ERR188 - Failed to resolve symbol: '" + symbol + "'" + + "ERR192 - Failed to resolve symbol: '" + symbol + "'" + (error_message.empty() ? "" : " - " + error_message), exprtk_error_location)); } @@ -25165,7 +25573,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR189 - Undefined symbol: '" + symbol + "'", + "ERR193 - Undefined symbol: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25272,7 +25680,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token(), - "ERR190 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token().value, + "ERR194 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token().value, exprtk_error_location)); return error_node(); @@ -25296,7 +25704,7 @@ namespace exprtk set_error( make_error(parser_error::e_numeric, current_token(), - "ERR191 - Failed generate node for scalar: '" + current_token().value + "'", + "ERR195 - Failed generate node for scalar: '" + current_token().value + "'", exprtk_error_location)); return error_node(); @@ -25310,7 +25718,7 @@ namespace exprtk set_error( make_error(parser_error::e_numeric, current_token(), - "ERR192 - Failed to convert '" + current_token().value + "' to a number", + "ERR196 - Failed to convert '" + current_token().value + "' to a number", exprtk_error_location)); return error_node(); @@ -25337,7 +25745,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR193 - Expected ')' instead of: '" + current_token().value + "'", + "ERR197 - Expected ')' instead of: '" + current_token().value + "'", exprtk_error_location)); free_node(node_allocator_,branch); @@ -25362,7 +25770,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR194 - Expected ']' instead of: '" + current_token().value + "'", + "ERR198 - Expected ']' instead of: '" + current_token().value + "'", exprtk_error_location)); free_node(node_allocator_,branch); @@ -25387,7 +25795,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR195 - Expected '}' instead of: '" + current_token().value + "'", + "ERR199 - Expected '}' instead of: '" + current_token().value + "'", exprtk_error_location)); free_node(node_allocator_,branch); @@ -25427,7 +25835,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR196 - Premature end of expression[1]", + "ERR200 - Premature end of expression[1]", exprtk_error_location)); return error_node(); @@ -25437,7 +25845,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR197 - Premature end of expression[2]", + "ERR201 - Premature end of expression[2]", exprtk_error_location)); return error_node(); @@ -25611,7 +26019,7 @@ namespace exprtk return true; } - inline details::operator_type get_operator(const binary_functor_t& bop) + inline details::operator_type get_operator(const binary_functor_t& bop) const { return (*inv_binary_op_map_).find(bop)->second; } @@ -25670,9 +26078,9 @@ namespace exprtk (details::e_frac == operation) || (details::e_trunc == operation) ; } - inline bool sf3_optimisable(const std::string& sf3id, trinary_functor_t& tfunc) + inline bool sf3_optimisable(const std::string& sf3id, trinary_functor_t& tfunc) const { - typename sf3_map_t::iterator itr = sf3_map_->find(sf3id); + typename sf3_map_t::const_iterator itr = sf3_map_->find(sf3id); if (sf3_map_->end() == itr) return false; @@ -25682,9 +26090,9 @@ namespace exprtk return true; } - inline bool sf4_optimisable(const std::string& sf4id, quaternary_functor_t& qfunc) + inline bool sf4_optimisable(const std::string& sf4id, quaternary_functor_t& qfunc) const { - typename sf4_map_t::iterator itr = sf4_map_->find(sf4id); + typename sf4_map_t::const_iterator itr = sf4_map_->find(sf4id); if (sf4_map_->end() == itr) return false; @@ -25694,9 +26102,9 @@ namespace exprtk return true; } - inline bool sf3_optimisable(const std::string& sf3id, details::operator_type& operation) + inline bool sf3_optimisable(const std::string& sf3id, details::operator_type& operation) const { - typename sf3_map_t::iterator itr = sf3_map_->find(sf3id); + typename sf3_map_t::const_iterator itr = sf3_map_->find(sf3id); if (sf3_map_->end() == itr) return false; @@ -25706,9 +26114,9 @@ namespace exprtk return true; } - inline bool sf4_optimisable(const std::string& sf4id, details::operator_type& operation) + inline bool sf4_optimisable(const std::string& sf4id, details::operator_type& operation) const { - typename sf4_map_t::iterator itr = sf4_map_->find(sf4id); + typename sf4_map_t::const_iterator itr = sf4_map_->find(sf4id); if (sf4_map_->end() == itr) return false; @@ -25836,7 +26244,7 @@ namespace exprtk (details::e_xnor == operation) ; } - inline std::string branch_to_id(expression_node_ptr branch) + inline std::string branch_to_id(expression_node_ptr branch) const { static const std::string null_str ("(null)" ); static const std::string const_str ("(c)" ); @@ -25877,7 +26285,7 @@ namespace exprtk return "ERROR"; } - inline std::string branch_to_id(expression_node_ptr (&branch)[2]) + inline std::string branch_to_id(expression_node_ptr (&branch)[2]) const { return branch_to_id(branch[0]) + std::string("o") + branch_to_id(branch[1]); } @@ -25995,7 +26403,7 @@ namespace exprtk !details::is_constant_node(branch[1]) ; } - inline bool is_invalid_assignment_op(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + inline bool is_invalid_assignment_op(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const { if (is_assignment_operation(operation)) { @@ -26017,14 +26425,14 @@ namespace exprtk return false; } - inline bool is_constpow_operation(const details::operator_type& operation, expression_node_ptr(&branch)[2]) + inline bool is_constpow_operation(const details::operator_type& operation, expression_node_ptr(&branch)[2]) const { if ( - !is_constant_node(branch[1]) || - is_constant_node(branch[0]) || - is_variable_node(branch[0]) || - is_vector_node (branch[0]) || - is_generally_string_node(branch[0]) + !details::is_constant_node(branch[1]) || + details::is_constant_node(branch[0]) || + details::is_variable_node(branch[0]) || + details::is_vector_node (branch[0]) || + details::is_generally_string_node(branch[0]) ) return false; @@ -26033,7 +26441,7 @@ namespace exprtk return cardinal_pow_optimisable(operation, c); } - inline bool is_invalid_break_continue_op(expression_node_ptr (&branch)[2]) + inline bool is_invalid_break_continue_op(expression_node_ptr (&branch)[2]) const { return ( details::is_break_node (branch[0]) || @@ -26043,7 +26451,7 @@ namespace exprtk ); } - inline bool is_invalid_string_op(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + inline bool is_invalid_string_op(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const { const bool b0_string = is_generally_string_node(branch[0]); const bool b1_string = is_generally_string_node(branch[1]); @@ -26063,7 +26471,7 @@ namespace exprtk return result; } - inline bool is_invalid_string_op(const details::operator_type& operation, expression_node_ptr (&branch)[3]) + inline bool is_invalid_string_op(const details::operator_type& operation, expression_node_ptr (&branch)[3]) const { const bool b0_string = is_generally_string_node(branch[0]); const bool b1_string = is_generally_string_node(branch[1]); @@ -26084,7 +26492,7 @@ namespace exprtk return result; } - inline bool is_string_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + inline bool is_string_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const { const bool b0_string = is_generally_string_node(branch[0]); const bool b1_string = is_generally_string_node(branch[1]); @@ -26092,7 +26500,7 @@ namespace exprtk return (b0_string && b1_string && valid_string_operation(operation)); } - inline bool is_string_operation(const details::operator_type& operation, expression_node_ptr (&branch)[3]) + inline bool is_string_operation(const details::operator_type& operation, expression_node_ptr (&branch)[3]) const { const bool b0_string = is_generally_string_node(branch[0]); const bool b1_string = is_generally_string_node(branch[1]); @@ -26102,7 +26510,7 @@ namespace exprtk } #ifndef exprtk_disable_sc_andor - inline bool is_shortcircuit_expression(const details::operator_type& operation) + inline bool is_shortcircuit_expression(const details::operator_type& operation) const { return ( (details::e_scand == operation) || @@ -26110,13 +26518,13 @@ namespace exprtk ); } #else - inline bool is_shortcircuit_expression(const details::operator_type&) + inline bool is_shortcircuit_expression(const details::operator_type&) const { return false; } #endif - inline bool is_null_present(expression_node_ptr (&branch)[2]) + inline bool is_null_present(expression_node_ptr (&branch)[2]) const { return ( details::is_null_node(branch[0]) || @@ -26124,7 +26532,7 @@ namespace exprtk ); } - inline bool is_vector_eqineq_logic_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + inline bool is_vector_eqineq_logic_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const { if (!is_ivector_node(branch[0]) && !is_ivector_node(branch[1])) return false; @@ -26146,7 +26554,7 @@ namespace exprtk ); } - inline bool is_vector_arithmetic_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + inline bool is_vector_arithmetic_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const { if (!is_ivector_node(branch[0]) && !is_ivector_node(branch[1])) return false; @@ -26319,15 +26727,19 @@ namespace exprtk return (*this)(operation,branch); } - inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr b0, expression_node_ptr b1) + inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr& b0, expression_node_ptr& b1) { - if ((0 == b0) || (0 == b1)) - return error_node(); - else + expression_node_ptr result = error_node(); + + if ((0 != b0) && (0 != b1)) { expression_node_ptr branch[2] = { b0, b1 }; - return expression_generator<Type>::operator()(operation,branch); + result = expression_generator<Type>::operator()(operation, branch); + b0 = branch[0]; + b1 = branch[1]; } + + return result; } inline expression_node_ptr conditional(expression_node_ptr condition, @@ -26521,7 +26933,7 @@ namespace exprtk return result; } - else if (details::is_null_node(condition)) + else if (details::is_null_node(condition) || (0 == condition)) { free_node(*node_allocator_, initialiser); free_node(*node_allocator_, condition); @@ -26553,7 +26965,7 @@ namespace exprtk } template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node_ptr const_optimise_switch(Sequence<expression_node_ptr,Allocator>& arg_list) { expression_node_ptr result = error_node(); @@ -26589,7 +27001,7 @@ namespace exprtk } template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node_ptr const_optimise_mswitch(Sequence<expression_node_ptr,Allocator>& arg_list) { expression_node_ptr result = error_node(); @@ -26714,7 +27126,7 @@ namespace exprtk }; template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node_ptr switch_statement(Sequence<expression_node_ptr,Allocator>& arg_list) { if (arg_list.empty()) @@ -26754,7 +27166,7 @@ namespace exprtk } template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node_ptr multi_switch_statement(Sequence<expression_node_ptr,Allocator>& arg_list) { if (!all_nodes_valid(arg_list)) @@ -27066,7 +27478,7 @@ namespace exprtk } template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node_ptr const_optimise_varargfunc(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list) { expression_node_ptr temp_node = error_node(); @@ -27098,7 +27510,7 @@ namespace exprtk return node_allocator_->allocate<literal_node_t>(v); } - inline bool special_one_parameter_vararg(const details::operator_type& operation) + inline bool special_one_parameter_vararg(const details::operator_type& operation) const { return ( (details::e_sum == operation) || @@ -27110,7 +27522,7 @@ namespace exprtk } template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node_ptr varnode_optimise_varargfunc(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list) { switch (operation) @@ -27133,7 +27545,7 @@ namespace exprtk } template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node_ptr vectorize_func(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list) { if (1 == arg_list.size()) @@ -27158,7 +27570,7 @@ namespace exprtk } template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline expression_node_ptr vararg_function(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list) { if (!all_nodes_valid(arg_list)) @@ -27520,7 +27932,7 @@ namespace exprtk template <typename NodePtr, typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline bool is_constant_foldable(const Sequence<NodePtr,Allocator>& b) const { for (std::size_t i = 0; i < b.size(); ++i) @@ -27666,6 +28078,8 @@ namespace exprtk } else if (details::is_vector_elem_node(branch[0])) { + lodge_assignment(e_st_vecelem,branch[0]); + switch (operation) { #define case_stmt(op0,op1) \ @@ -27684,6 +28098,8 @@ namespace exprtk } else if (details::is_rebasevector_elem_node(branch[0])) { + lodge_assignment(e_st_vecelem,branch[0]); + switch (operation) { #define case_stmt(op0,op1) \ @@ -27702,6 +28118,8 @@ namespace exprtk } else if (details::is_rebasevector_celem_node(branch[0])) { + lodge_assignment(e_st_vecelem,branch[0]); + switch (operation) { #define case_stmt(op0,op1) \ @@ -28052,7 +28470,7 @@ namespace exprtk case_stmt(details::e_xnor, details::xnor_op) \ #ifndef exprtk_disable_cardinal_pow_optimisation - template <typename TType, template <typename,typename> class IPowNode> + template <typename TType, template <typename, typename> class IPowNode> inline expression_node_ptr cardinal_pow_optimisation_impl(const TType& v, const unsigned int& p) { switch (p) @@ -28102,7 +28520,7 @@ namespace exprtk } } - inline bool cardinal_pow_optimisable(const details::operator_type& operation, const T& c) + inline bool cardinal_pow_optimisable(const details::operator_type& operation, const T& c) const { return (details::e_pow == operation) && (details::numeric::abs(c) <= T(60)) && details::numeric::is_integer(c); } @@ -29228,7 +29646,8 @@ namespace exprtk if (!expr_gen.sf3_optimisable(id,sf3opr)) return false; else - result = synthesize_sf3ext_expression::template process<T0,T1,T2>(expr_gen,sf3opr,t0,t1,t2); + result = synthesize_sf3ext_expression::template process<T0, T1, T2> + (expr_gen, sf3opr, t0, t1, t2); return true; } @@ -29295,7 +29714,7 @@ namespace exprtk if (!expr_gen.sf4_optimisable(id,sf4opr)) return false; else - result = synthesize_sf4ext_expression::template process<T0,T1,T2,T3> + result = synthesize_sf4ext_expression::template process<T0, T1, T2, T3> (expr_gen, sf4opr, t0, t1, t2, t3); return true; @@ -29315,28 +29734,28 @@ namespace exprtk typedef details::T0oT1oT2_base_node<Type>* sf3ext_base_ptr; sf3ext_base_ptr n = static_cast<sf3ext_base_ptr>(sf3node); - std::string id = "t" + expr_gen.to_str(operation) + "(" + n->type_id() + ")"; + const std::string id = "t" + expr_gen.to_str(operation) + "(" + n->type_id() + ")"; switch (n->type()) { case details::expression_node<Type>::e_covoc : return compile_right_impl - <typename covoc_t::sf3_type_node,ExternalType,ctype,vtype,ctype> + <typename covoc_t::sf3_type_node,ExternalType, ctype, vtype, ctype> (expr_gen, id, t, sf3node, result); case details::expression_node<Type>::e_covov : return compile_right_impl - <typename covov_t::sf3_type_node,ExternalType,ctype,vtype,vtype> + <typename covov_t::sf3_type_node,ExternalType, ctype, vtype, vtype> (expr_gen, id, t, sf3node, result); case details::expression_node<Type>::e_vocov : return compile_right_impl - <typename vocov_t::sf3_type_node,ExternalType,vtype,ctype,vtype> + <typename vocov_t::sf3_type_node,ExternalType, vtype, ctype, vtype> (expr_gen, id, t, sf3node, result); case details::expression_node<Type>::e_vovoc : return compile_right_impl - <typename vovoc_t::sf3_type_node,ExternalType,vtype,vtype,ctype> + <typename vovoc_t::sf3_type_node,ExternalType, vtype, vtype, ctype> (expr_gen, id, t, sf3node, result); case details::expression_node<Type>::e_vovov : return compile_right_impl - <typename vovov_t::sf3_type_node,ExternalType,vtype,vtype,vtype> + <typename vovov_t::sf3_type_node,ExternalType, vtype, vtype, vtype> (expr_gen, id, t, sf3node, result); default : return false; @@ -29358,28 +29777,28 @@ namespace exprtk sf3ext_base_ptr n = static_cast<sf3ext_base_ptr>(sf3node); - std::string id = "(" + n->type_id() + ")" + expr_gen.to_str(operation) + "t"; + const std::string id = "(" + n->type_id() + ")" + expr_gen.to_str(operation) + "t"; switch (n->type()) { case details::expression_node<Type>::e_covoc : return compile_left_impl - <typename covoc_t::sf3_type_node,ExternalType,ctype,vtype,ctype> + <typename covoc_t::sf3_type_node,ExternalType, ctype, vtype, ctype> (expr_gen, id, t, sf3node, result); case details::expression_node<Type>::e_covov : return compile_left_impl - <typename covov_t::sf3_type_node,ExternalType,ctype,vtype,vtype> + <typename covov_t::sf3_type_node,ExternalType, ctype, vtype, vtype> (expr_gen, id, t, sf3node, result); case details::expression_node<Type>::e_vocov : return compile_left_impl - <typename vocov_t::sf3_type_node,ExternalType,vtype,ctype,vtype> + <typename vocov_t::sf3_type_node,ExternalType, vtype, ctype, vtype> (expr_gen, id, t, sf3node, result); case details::expression_node<Type>::e_vovoc : return compile_left_impl - <typename vovoc_t::sf3_type_node,ExternalType,vtype,vtype,ctype> + <typename vovoc_t::sf3_type_node,ExternalType, vtype, vtype, ctype> (expr_gen, id, t, sf3node, result); case details::expression_node<Type>::e_vovov : return compile_left_impl - <typename vovov_t::sf3_type_node,ExternalType,vtype,vtype,vtype> + <typename vovov_t::sf3_type_node,ExternalType, vtype, vtype, vtype> (expr_gen, id, t, sf3node, result); default : return false; @@ -29401,7 +29820,7 @@ namespace exprtk T1 t1 = n->t1(); T2 t2 = n->t2(); - return synthesize_sf4ext_expression::template compile<ExternalType,T0,T1,T2> + return synthesize_sf4ext_expression::template compile<ExternalType, T0, T1, T2> (expr_gen, id, t, t0, t1, t2, result); } else @@ -29423,7 +29842,7 @@ namespace exprtk T1 t1 = n->t1(); T2 t2 = n->t2(); - return synthesize_sf4ext_expression::template compile<T0,T1,T2,ExternalType> + return synthesize_sf4ext_expression::template compile<T0, T1, T2, ExternalType> (expr_gen, id, t0, t1, t2, t, result); } else @@ -29487,7 +29906,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; } }; @@ -29547,7 +29969,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; } }; @@ -29608,7 +30033,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; } }; @@ -29668,7 +30096,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; } }; @@ -29728,7 +30159,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; } }; @@ -29788,7 +30222,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; } }; @@ -29848,7 +30285,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; } }; @@ -29908,7 +30348,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; } }; @@ -30022,7 +30465,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; } }; @@ -30136,7 +30582,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; } }; @@ -30259,7 +30708,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; } }; @@ -30381,7 +30833,10 @@ namespace exprtk static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; } }; @@ -30510,7 +30965,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; } }; @@ -30594,7 +31053,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; } }; @@ -30678,7 +31141,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; } }; @@ -30762,7 +31229,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; } }; @@ -30846,7 +31317,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; } }; @@ -31035,7 +31510,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; } }; @@ -31274,7 +31753,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; } }; @@ -31463,7 +31946,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; } }; @@ -31651,7 +32138,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; } }; @@ -31703,7 +32194,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t))"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; } }; @@ -31759,7 +32254,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t))"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; } }; @@ -31815,7 +32314,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t))"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; } }; @@ -31871,7 +32374,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t))"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; } }; @@ -31928,7 +32435,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t))"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; } }; @@ -31985,7 +32496,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t))"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; } }; @@ -32041,7 +32556,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t))"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; } }; @@ -32097,7 +32616,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t))"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; } }; @@ -32153,7 +32676,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "(t" << expr_gen.to_str(o2) << "t))"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; } }; @@ -32209,7 +32736,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "((t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; } }; @@ -32265,7 +32796,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "((t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; } }; @@ -32321,7 +32856,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "((t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; } }; @@ -32377,7 +32916,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "((t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; } }; @@ -32434,7 +32977,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "((t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; } }; @@ -32491,7 +33038,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "((t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; } }; @@ -32547,7 +33098,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "((t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; } }; @@ -32604,7 +33159,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "t" << expr_gen.to_str(o0) << "((t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t)"); + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; } }; @@ -32677,7 +33236,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "((t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -32734,7 +33297,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "((t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -32790,7 +33357,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "((t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -32846,7 +33417,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "((t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -32902,7 +33477,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "((t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -32958,7 +33537,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "((t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33015,7 +33598,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "((t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33072,7 +33659,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "((t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33128,7 +33719,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "((t" << expr_gen.to_str(o0) << "t)" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33184,7 +33779,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33241,7 +33840,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33297,7 +33900,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33352,7 +33959,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33408,7 +34019,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33464,7 +34079,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33521,7 +34140,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -33578,7 +34201,11 @@ namespace exprtk const details::operator_type o1, const details::operator_type o2) { - return (details::build_string() << "(t" << expr_gen.to_str(o0) << "(t" << expr_gen.to_str(o1) << "t)" << expr_gen.to_str(o2) << "t"); + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; } }; @@ -34125,9 +34752,9 @@ namespace exprtk std::string& s1 = static_cast<details::stringvar_node<Type>*>(branch[1])->ref(); std::string& s2 = static_cast<details::stringvar_node<Type>*>(branch[2])->ref(); - typedef typename details::sosos_node<Type,std::string&,std::string&,std::string&,details::inrange_op<Type> > inrange_t; + typedef typename details::sosos_node<Type, std::string&, std::string&, std::string&, details::inrange_op<Type> > inrange_t; - return node_allocator_->allocate_type<inrange_t,std::string&,std::string&,std::string&>(s0,s1,s2); + return node_allocator_->allocate_type<inrange_t, std::string&, std::string&, std::string&>(s0, s1, s2); } else if ( details::is_const_string_node(branch[0]) && @@ -34139,12 +34766,12 @@ namespace exprtk std::string& s1 = static_cast< details::stringvar_node<Type>*>(branch[1])->ref(); std::string s2 = static_cast<details::string_literal_node<Type>*>(branch[2])->str(); - typedef typename details::sosos_node<Type,std::string,std::string&,std::string,details::inrange_op<Type> > inrange_t; + typedef typename details::sosos_node<Type, std::string, std::string&, std::string, details::inrange_op<Type> > inrange_t; details::free_node(*node_allocator_,branch[0]); details::free_node(*node_allocator_,branch[2]); - return node_allocator_->allocate_type<inrange_t,std::string,std::string&,std::string>(s0,s1,s2); + return node_allocator_->allocate_type<inrange_t, std::string, std::string&, std::string>(s0, s1, s2); } else if ( details::is_string_node(branch[0]) && @@ -34156,11 +34783,11 @@ namespace exprtk std::string s1 = static_cast<details::string_literal_node<Type>*>(branch[1])->str(); std::string& s2 = static_cast< details::stringvar_node<Type>*>(branch[2])->ref(); - typedef typename details::sosos_node<Type,std::string&,std::string,std::string&,details::inrange_op<Type> > inrange_t; + typedef typename details::sosos_node<Type, std::string&, std::string, std::string&, details::inrange_op<Type> > inrange_t; details::free_node(*node_allocator_,branch[1]); - return node_allocator_->allocate_type<inrange_t,std::string&,std::string,std::string&>(s0,s1,s2); + return node_allocator_->allocate_type<inrange_t, std::string&, std::string, std::string&>(s0, s1, s2); } else if ( details::is_string_node(branch[0]) && @@ -34172,11 +34799,11 @@ namespace exprtk std::string& s1 = static_cast< details::stringvar_node<Type>*>(branch[1])->ref(); std::string s2 = static_cast<details::string_literal_node<Type>*>(branch[2])->str(); - typedef typename details::sosos_node<Type,std::string&,std::string&,std::string,details::inrange_op<Type> > inrange_t; + typedef typename details::sosos_node<Type, std::string&, std::string&, std::string, details::inrange_op<Type> > inrange_t; details::free_node(*node_allocator_,branch[2]); - return node_allocator_->allocate_type<inrange_t,std::string&,std::string&,std::string>(s0,s1,s2); + return node_allocator_->allocate_type<inrange_t, std::string&, std::string&, std::string>(s0, s1, s2); } else if ( details::is_const_string_node(branch[0]) && @@ -34188,11 +34815,11 @@ namespace exprtk std::string& s1 = static_cast< details::stringvar_node<Type>*>(branch[1])->ref(); std::string& s2 = static_cast< details::stringvar_node<Type>*>(branch[2])->ref(); - typedef typename details::sosos_node<Type,std::string,std::string&,std::string&,details::inrange_op<Type> > inrange_t; + typedef typename details::sosos_node<Type, std::string, std::string&, std::string&, details::inrange_op<Type> > inrange_t; details::free_node(*node_allocator_,branch[0]); - return node_allocator_->allocate_type<inrange_t,std::string,std::string&,std::string&>(s0,s1,s2); + return node_allocator_->allocate_type<inrange_t, std::string, std::string&, std::string&>(s0, s1, s2); } else return error_node(); @@ -34692,13 +35319,14 @@ namespace exprtk lexer::helper::helper_assembly helper_assembly_; - lexer::helper::commutative_inserter commutative_inserter_; - lexer::helper::operator_joiner operator_joiner_2_; - lexer::helper::operator_joiner operator_joiner_3_; - lexer::helper::symbol_replacer symbol_replacer_; - lexer::helper::bracket_checker bracket_checker_; - lexer::helper::numeric_checker numeric_checker_; - lexer::helper::sequence_validator sequence_validator_; + lexer::helper::commutative_inserter commutative_inserter_; + lexer::helper::operator_joiner operator_joiner_2_; + lexer::helper::operator_joiner operator_joiner_3_; + lexer::helper::symbol_replacer symbol_replacer_; + lexer::helper::bracket_checker bracket_checker_; + lexer::helper::numeric_checker numeric_checker_; + lexer::helper::sequence_validator sequence_validator_; + lexer::helper::sequence_validator_3tokens sequence_validator_3tkns_; template <typename ParserType> friend void details::disable_type_checking(ParserType& p); @@ -35875,7 +36503,7 @@ namespace exprtk def_fp_retval(6) template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> inline bool add(const std::string& name, const std::string& expression, const Sequence<std::string,Allocator>& var_list, @@ -35971,7 +36599,7 @@ namespace exprtk private: template <typename Allocator, - template <typename,typename> class Sequence> + template <typename, typename> class Sequence> bool compile_expression(const std::string& name, const std::string& expression, const Sequence<std::string,Allocator>& input_var_list, @@ -36290,8 +36918,8 @@ namespace exprtk { static const T lower_bound = T(-20); static const T upper_bound = T(+20); + static const T delta = T(0.1); - T delta = T(0.1); T total = T(0); for (x = lower_bound; x <= upper_bound; x += delta) @@ -36315,7 +36943,7 @@ namespace exprtk { for (std::size_t i = 0; i < 10000; ++i) { - T v = T(123.456 + i); + const T v = T(123.456 + i); if (details::is_true(details::numeric::nequal(details::numeric::fast_exp<T, 1>::result(v),details::numeric::pow(v,T( 1))))) return false; @@ -36758,7 +37386,7 @@ namespace exprtk } } - bool eof() + bool eof() const { switch (mode) { @@ -36769,7 +37397,7 @@ namespace exprtk } } - file_mode get_file_mode(const std::string& access) + file_mode get_file_mode(const std::string& access) const { if (access.empty() || access.size() > 2) return e_error; @@ -36847,11 +37475,9 @@ namespace exprtk inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) { - std::string file_name; + std::string file_name = to_str(string_t(parameters[0])); std::string access; - file_name = to_str(string_t(parameters[0])); - if (file_name.empty()) return T(0); @@ -36932,27 +37558,27 @@ namespace exprtk case 0 : { const string_t buffer(parameters[1]); amount = buffer.size(); - return T(fd->write(buffer,amount) ? 1 : 0); + return T(fd->write(buffer, amount) ? 1 : 0); } case 1 : { const string_t buffer(parameters[1]); amount = std::min(buffer.size(), static_cast<std::size_t>(scalar_t(parameters[2])())); - return T(fd->write(buffer,amount) ? 1 : 0); + return T(fd->write(buffer, amount) ? 1 : 0); } case 2 : { const vector_t vec(parameters[1]); amount = vec.size(); - return T(fd->write(vec,amount) ? 1 : 0); + return T(fd->write(vec, amount) ? 1 : 0); } case 3 : { const vector_t vec(parameters[1]); amount = std::min(vec.size(), static_cast<std::size_t>(scalar_t(parameters[2])())); - return T(fd->write(vec,amount) ? 1 : 0); + return T(fd->write(vec, amount) ? 1 : 0); } } @@ -37153,10 +37779,10 @@ namespace exprtk namespace details { template <typename T> - inline void kahan_sum(T& sum, T& error, T v) + inline void kahan_sum(T& sum, T& error, const T v) { - T x = v - error; - T y = sum + x; + const T x = v - error; + const T y = sum + x; error = (y - sum) - x; sum = y; } @@ -37877,10 +38503,10 @@ namespace exprtk if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 3, 4, 1)) return std::numeric_limits<T>::quiet_NaN(); - else if (helper::invalid_range(y,r0,r1)) + else if (helper::invalid_range(y, r0, r1)) return std::numeric_limits<T>::quiet_NaN(); - T a = scalar_t(parameters[0])(); + const T a = scalar_t(parameters[0])(); for (std::size_t i = r0; i <= r1; ++i) { @@ -37924,7 +38550,7 @@ namespace exprtk if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 4, 5, 1)) return std::numeric_limits<T>::quiet_NaN(); - else if (helper::invalid_range(y,r0,r1)) + else if (helper::invalid_range(y, r0, r1)) return std::numeric_limits<T>::quiet_NaN(); const T a = scalar_t(parameters[0])(); @@ -37973,12 +38599,12 @@ namespace exprtk if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 3, 4, 1)) return std::numeric_limits<T>::quiet_NaN(); - else if (helper::invalid_range(y,r0,r1)) + else if (helper::invalid_range(y, r0, r1)) return std::numeric_limits<T>::quiet_NaN(); - else if (helper::invalid_range(z,r0,r1)) + else if (helper::invalid_range(z, r0, r1)) return std::numeric_limits<T>::quiet_NaN(); - T a = scalar_t(parameters[0])(); + const T a = scalar_t(parameters[0])(); for (std::size_t i = r0; i <= r1; ++i) { @@ -38023,9 +38649,9 @@ namespace exprtk if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 4, 5, 1)) return std::numeric_limits<T>::quiet_NaN(); - else if (helper::invalid_range(y,r0,r1)) + else if (helper::invalid_range(y, r0, r1)) return std::numeric_limits<T>::quiet_NaN(); - else if (helper::invalid_range(z,r0,r1)) + else if (helper::invalid_range(z, r0, r1)) return std::numeric_limits<T>::quiet_NaN(); const T a = scalar_t(parameters[0])(); @@ -38073,7 +38699,7 @@ namespace exprtk if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 4, 5, 1)) return std::numeric_limits<T>::quiet_NaN(); - else if (helper::invalid_range(z,r0,r1)) + else if (helper::invalid_range(z, r0, r1)) return std::numeric_limits<T>::quiet_NaN(); const T a = scalar_t(parameters[0])(); @@ -38120,7 +38746,7 @@ namespace exprtk if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0)) return std::numeric_limits<T>::quiet_NaN(); - else if (helper::invalid_range(y,r0,r1)) + else if (helper::invalid_range(y, r0, r1)) return std::numeric_limits<T>::quiet_NaN(); T result = T(0); @@ -38166,7 +38792,7 @@ namespace exprtk if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0)) return std::numeric_limits<T>::quiet_NaN(); - else if (helper::invalid_range(y,r0,r1)) + else if (helper::invalid_range(y, r0, r1)) return std::numeric_limits<T>::quiet_NaN(); T result = T(0); @@ -38222,7 +38848,7 @@ namespace exprtk exprtk_register_function("any_true" ,nt) exprtk_register_function("any_false" ,nf) exprtk_register_function("count" , c) - exprtk_register_function("copy" , cp) + exprtk_register_function("copy" ,cp) exprtk_register_function("rotate_left" ,rl) exprtk_register_function("rol" ,rl) exprtk_register_function("rotate_right" ,rr) @@ -38256,9 +38882,9 @@ namespace exprtk namespace information { static const char* library = "Mathematical Expression Toolkit"; - static const char* version = "2.71828182845904523536028747135266249775724709369" - "9959574966967627724076630353547594571382178525166"; - static const char* date = "20180101"; + static const char* version = "2.71828182845904523536028747135266249775724709369995957" + "4966967627724076630353547594571382178525166427427466391"; + static const char* date = "20190818"; static inline std::string data() { diff --git a/src/executiontree/ExecutionTree.cpp b/src/executiontree/ExecutionTree.cpp index b273897077c8acaa14be191596c6b1b7ca272b21..346b089ecd22d2072bd7ca47fa2054ca684767f2 100644 --- a/src/executiontree/ExecutionTree.cpp +++ b/src/executiontree/ExecutionTree.cpp @@ -125,7 +125,7 @@ void EveryNth::operator()() ++calls_; } -const std::string EveryNth::getName() const +std::string EveryNth::getName() const { std::stringstream ss; ss << "every " << interval_ << "th step:"; @@ -208,7 +208,7 @@ void Loop::synchronizedStop( bool stopVar ) mpi::allReduceInplace( stop_, mpi::LOGICAL_OR ); } -const std::string Loop::getName() const +std::string Loop::getName() const { std::stringstream ss; ss << "Loop [" << iterations_ << "]"; diff --git a/src/executiontree/ExecutionTree.h b/src/executiontree/ExecutionTree.h index 99b3515a5c834a5c70e5327f344edc1766f56a73..f9c838ec63cc7ff8b5f01e02d962d3b9c9d6e36f 100644 --- a/src/executiontree/ExecutionTree.h +++ b/src/executiontree/ExecutionTree.h @@ -109,8 +109,8 @@ class IFunctionNode public: virtual ~IFunctionNode() {} virtual void operator()() = 0; - virtual const std::string getName() const = 0; - virtual const std::deque< shared_ptr< IFunctionNode > > getChildren() const { return {}; } + virtual std::string getName() const = 0; + virtual std::deque< shared_ptr< IFunctionNode > > getChildren() const { return {}; } }; @@ -122,7 +122,7 @@ public: const std::string &name, const TimingTreePtr & timingTree ); - const std::string getName() const override { return name_ != "" ? name_ : "Functor"; }; + std::string getName() const override { return name_ != "" ? name_ : "Functor"; }; void operator() () override; private: @@ -138,8 +138,8 @@ public: EveryNth( const IFunctionNodePtr &node, uint_t interval, bool onFirst = false, uint_t startValue = 0 ); void operator()() override; - const std::string getName() const override; - const std::deque< shared_ptr< IFunctionNode > > getChildren() const override { return { wrapped_ }; } + std::string getName() const override; + std::deque< shared_ptr< IFunctionNode > > getChildren() const override { return { wrapped_ }; } private: IFunctionNodePtr wrapped_; @@ -158,8 +158,8 @@ public: void push_back( const IFunctionNodePtr &fct ) { children_.push_back( fct ); } void push_front( const IFunctionNodePtr &fct ) { children_.push_front( fct ); } - const std::string getName() const override { return name_ != "" ? name_ : "Sequence"; }; - const std::deque< IFunctionNodePtr > getChildren() const override { return children_; }; + std::string getName() const override { return name_ != "" ? name_ : "Sequence"; }; + std::deque< IFunctionNodePtr > getChildren() const override { return children_; }; private: std::string name_; @@ -185,8 +185,8 @@ public: uint_t getCurrentTimeStep() const override { return currentIteration_; } uint_t getNrOfTimeSteps() const override { return iterations_; } - const std::deque< shared_ptr< IFunctionNode > > getChildren() const override { return { body_ }; } - const std::string getName() const override; + std::deque< shared_ptr< IFunctionNode > > getChildren() const override { return { body_ }; } + std::string getName() const override; private: IFunctionNodePtr body_; @@ -203,4 +203,4 @@ private: } // namespace walberla -#include "ExecutionTree.impl.h" \ No newline at end of file +#include "ExecutionTree.impl.h" diff --git a/src/gather/MPIGatherScheme.cpp b/src/gather/MPIGatherScheme.cpp index c40a63c691255072678aa70c9621fc38583e08f9..1fc309303fbced68844561da221c1fd833f6a008 100644 --- a/src/gather/MPIGatherScheme.cpp +++ b/src/gather/MPIGatherScheme.cpp @@ -169,7 +169,7 @@ void MPIGatherScheme::communicate() auto mpiManager = MPIManager::instance(); - if ( packInfos_.size() == 0 ) { + if ( packInfos_.empty() ) { WALBERLA_LOG_WARNING( "Communicating MPIGatherScheme without registered PackInfos"); return; } diff --git a/src/geometry/bodies/Sphere.cpp b/src/geometry/bodies/Sphere.cpp index f7fc0dce0f1bc57ca1da2e0d686c832552ebb8dd..ffd62b51aa7799e18743b286b86eaf3e6acafe5d 100644 --- a/src/geometry/bodies/Sphere.cpp +++ b/src/geometry/bodies/Sphere.cpp @@ -57,9 +57,9 @@ namespace geometry { template<> FastOverlapResult fastOverlapCheck ( const Sphere & sphere, const AABB & box ) { - if ( ! sphere.boundingBox().intersects( box ) ) return COMPLETELY_OUTSIDE; - else if ( sphere.innerBox() .contains( box ) ) return CONTAINED_INSIDE_BODY; - else return DONT_KNOW; + if ( ! sphere.boundingBox().intersects( box ) ) return COMPLETELY_OUTSIDE; + if ( sphere.innerBox() .contains( box ) ) return CONTAINED_INSIDE_BODY; + return DONT_KNOW; } template<> diff --git a/src/geometry/mesh/TriangleMeshIO.cpp b/src/geometry/mesh/TriangleMeshIO.cpp index 3581637e30e278fa638b72a7cbbf0d466c2f229b..1161e086540dc8a195f03354f61ac2c22041cd65 100644 --- a/src/geometry/mesh/TriangleMeshIO.cpp +++ b/src/geometry/mesh/TriangleMeshIO.cpp @@ -493,16 +493,15 @@ namespace geometry { bool hasVertexNormals = mesh.hasVertexNormals(); size_t j; - TriangleMesh::index_t i; - for( i = 0u; i < mesh.getNumVertices(); ++i ){ + for( TriangleMesh::index_t i = 0; i < mesh.getNumVertices(); ++i ){ j = i / itemsPerMesh; meshVec[j].addVertex( mesh.getVertex(i) ); if( hasVertexNormals ) meshVec[j].addVertexNormal( mesh.getVertexNormal(i) ); } - for( i = 0u; i < mesh.getNumTriangles(); ++i ){ + for( size_t i = 0; i < mesh.getNumTriangles(); ++i ){ TriangleMesh::index_t ix = mesh.getVertexIndex( i, 0 ); TriangleMesh::index_t iy = mesh.getVertexIndex( i, 1 ); TriangleMesh::index_t iz = mesh.getVertexIndex( i, 2 ); diff --git a/src/mesa_pd/collision_detection/AnalyticContactDetection.h b/src/mesa_pd/collision_detection/AnalyticContactDetection.h index bb3f8f1f21ba9356d0a4385c8de871efe0ec5dc6..284cd1eb9470b155d8ae7bedd034070f09319182 100644 --- a/src/mesa_pd/collision_detection/AnalyticContactDetection.h +++ b/src/mesa_pd/collision_detection/AnalyticContactDetection.h @@ -147,11 +147,11 @@ public: Accessor& ac); protected: - size_t idx1_; - size_t idx2_; - Vec3 contactPoint_; - Vec3 contactNormal_; - real_t penetrationDepth_; + size_t idx1_ = std::numeric_limits<size_t>::max(); + size_t idx2_ = std::numeric_limits<size_t>::max(); + Vec3 contactPoint_ = Vec3(); + Vec3 contactNormal_ = Vec3(); + real_t penetrationDepth_ = real_t(0); real_t contactThreshold_ = real_t(0.0); }; diff --git a/src/mesa_pd/collision_detection/EPA.cpp b/src/mesa_pd/collision_detection/EPA.cpp index 304b4e6f16a3b2229ea0ff72648b0c9d1fcef838..3fb4ced431d587865cd0e5a454979823c2d31988 100644 --- a/src/mesa_pd/collision_detection/EPA.cpp +++ b/src/mesa_pd/collision_detection/EPA.cpp @@ -275,7 +275,7 @@ bool EPA::doEPA( Support &geom1, } } - if(entryHeap.size() == 0) { + if(entryHeap.empty()) { //unrecoverable error. return false; } @@ -473,7 +473,7 @@ bool EPA::doEPA( Support &geom1, firstTriangle->link(2, lastTriangle, 1); } - } while (entryHeap.size() > 0 && entryHeap[0]->getSqrDist() <= upperBoundSqr); + } while (!entryHeap.empty() && entryHeap[0]->getSqrDist() <= upperBoundSqr); //Normal must be inverted retNormal = -current->getClosest().getNormalizedOrZero(); diff --git a/src/mesa_pd/collision_detection/GJK.cpp b/src/mesa_pd/collision_detection/GJK.cpp index edd2a963ff1cf5678e2363af7aa28e3e202c23e7..d111caccdd28eeb7070607df748059c824a78b6f 100644 --- a/src/mesa_pd/collision_detection/GJK.cpp +++ b/src/mesa_pd/collision_detection/GJK.cpp @@ -47,7 +47,7 @@ GJK::GJK() * \param dir The support point direction. * \param threshold Extension of the particle. */ -const Vec3 GJK::putSupport(const Support &geom1, +Vec3 GJK::putSupport(const Support &geom1, const Support &geom2, const Vec3& dir, const real_t margin, @@ -313,7 +313,7 @@ inline real_t GJK::calcDistance( Vec3& normal, Vec3& contactPoint ) //its distance to the origin is the distance of the two objects real_t dist= 0.0; - real_t barCoords[3] = { 0.0, 0.0, 0.0}; + std::array<real_t, 3> barCoords = {{ 0.0, 0.0, 0.0 }}; real_t& u = barCoords[0]; real_t& v = barCoords[1]; real_t& w = barCoords[2]; diff --git a/src/mesa_pd/collision_detection/GJK.h b/src/mesa_pd/collision_detection/GJK.h index 96d6d5be8220dd16ef93e73a4951a5a65af67277..47b4247319d95229fbce258ab570092034524272 100644 --- a/src/mesa_pd/collision_detection/GJK.h +++ b/src/mesa_pd/collision_detection/GJK.h @@ -81,7 +81,7 @@ private: inline bool zeroLengthVector( const Vec3& vec ) const { return vec.sqrLength() < math::Limits<real_t>::fpuAccuracy(); } real_t calcDistance ( Vec3& normal, Vec3& contactPoint ); - inline const Vec3 putSupport(const Support &geom1, + inline Vec3 putSupport(const Support &geom1, const Support &geom2, const Vec3& dir, const real_t margin, diff --git a/src/mesa_pd/data/ContactAccessor.h b/src/mesa_pd/data/ContactAccessor.h index b0f76962491472c0f0efb436d67a2a919ec43cab..470eb0b7c72670d141e58325b2a0106d8c097a64 100644 --- a/src/mesa_pd/data/ContactAccessor.h +++ b/src/mesa_pd/data/ContactAccessor.h @@ -26,7 +26,7 @@ #pragma once -#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/IContactAccessor.h> #include <mesa_pd/data/ContactStorage.h> #include <core/UniqueID.h> @@ -43,7 +43,7 @@ namespace data { * Provides get, set and getRef for all members of the ContactStorage. * Can be used as a basis class for a more advanced ContactAccessor. */ -class ContactAccessor : public IAccessor +class ContactAccessor : public IContactAccessor { public: ContactAccessor(const std::shared_ptr<data::ContactStorage>& ps) : ps_(ps) {} diff --git a/src/mesa_pd/data/IContactAccessor.h b/src/mesa_pd/data/IContactAccessor.h new file mode 100644 index 0000000000000000000000000000000000000000..68382fa9b4d38492055050b148b016f0d7821e77 --- /dev/null +++ b/src/mesa_pd/data/IContactAccessor.h @@ -0,0 +1,48 @@ +//====================================================================================================================== +// +// 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 IContactAccessor.h +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + +#pragma once + +#include <core/DataTypes.h> + +#include <typeinfo> +#include <type_traits> + +namespace walberla { +namespace mesa_pd { +namespace data { + +/** + * @brief Add this as a base class to identify your class as an accessor for contact objects. + * + * Contact Accessors passed via templated arguments might be checked like + * \code + * static_assert(std::is_base_of<IContactAccessor, X>::value, typeid(X).name() + " is not a accessor"); + * \endcode + */ +class IContactAccessor +{ +public: + virtual ~IContactAccessor() = default; +}; + +} //namespace data +} //namespace mesa_pd +} //namespace walberla diff --git a/src/mesa_pd/kernel/HCSITSRelaxationStep.h b/src/mesa_pd/kernel/HCSITSRelaxationStep.h new file mode 100644 index 0000000000000000000000000000000000000000..10d87699963103261cbe32cd411f2af46429739a --- /dev/null +++ b/src/mesa_pd/kernel/HCSITSRelaxationStep.h @@ -0,0 +1,1060 @@ +//====================================================================================================================== +// +// 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 HCSITSRelaxationStep.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/IContactAccessor.h> +#include <mesa_pd/data/Flags.h> +#include <core/logging/Logging.h> +#include <core/math/Constants.h> +#include <core/math/Limits.h> + +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { +/** + * Apply a HCSITS (Hard-contact semi-implicit timestepping solver) relaxation to all contacts. + * Call this kernel on all bodies to perform on relaxation step of the solver. + * There exist different friction and relaxation models. See comments on their respective methods + * for an extended documentation. Use setRelaxationModel() to set it. + * getDeltaMax() can be used to query the maximum change in the impulses applied wrt to the last iteration. + * + * Use setMaxSubIterations() to set the maximum number of iterations performed to solve optimization problems arising + * during the relaxation (in InelasticGeneralizedMaximumDissipationContact and InelasticCoulombContactByDecoupling). + * + * Use setCor() to set the coefficient of restitution between 0 and 1 for the InelasticProjectedGaussSeidel model. + * All other models describe inelastic contacts. + * + * Example of Using the HCSITS kernels in a simulation: + * \code + * // ps: Particle storage + * // cs: Contact storage + * // pa: Particle accessor + * // ca: Contact accessor + * + * // Detect contacts first and fill contact storage + * + * mesa_pd::mpi::ReduceProperty reductionKernel; + * mesa_pd::mpi::BroadcastProperty broadcastKernel; + * + * kernel::IntegrateBodiesHCSITS integration; + * + * kernel::InitContactsForHCSITS initContacts(1); + * initContacts.setFriction(0,0,real_t(0.2)); + * kernel::InitBodiesForHCSITS initBodies; + * + * kernel::HCSITSRelaxationStep relaxationStep; + * relaxationStep.setCor(real_t(0.1)); // Only effective for PGSM + * + * // Init Contacts and Bodies (order is arbitrary) + * cs.forEachContact(false, kernel::SelectAll(), ca, initContacts, ca, pa); + * ps.forEachParticle(false, kernel::SelectAll(), pa, initBodies, pa, dt); + * + * // Reduce and Broadcast velocities with relaxation parameter 1 before the iteration + * VelocityUpdateNotification::Parameters::relaxationParam = real_t(1.0); + * reductionKernel.operator()<VelocityCorrectionNotification>(*ps); + * broadcastKernel.operator()<VelocityUpdateNotification>(*ps); + * + * VelocityUpdateNotification::Parameters::relaxationParam = real_t(0.8); + * for(int j = 0; j < 10; j++){ + * cs.forEachContact(false, kernel::SelectAll(), ca, relaxationStep, ca, pa, dt); + * reductionKernel.operator()<VelocityCorrectionNotification>(*ps); + * broadcastKernel.operator()<VelocityUpdateNotification>(*ps); + * } + * + * ps.forEachParticle(false, kernel::SelectAll(), pa, integration, pa, dt); + * \endcode + * \ingroup mesa_pd_kernel + * + * + */ +class HCSITSRelaxationStep +{ +public: + enum RelaxationModel { + InelasticFrictionlessContact, + ApproximateInelasticCoulombContactByDecoupling, + ApproximateInelasticCoulombContactByOrthogonalProjections, + InelasticCoulombContactByDecoupling, + InelasticCoulombContactByOrthogonalProjections, + InelasticGeneralizedMaximumDissipationContact, + InelasticProjectedGaussSeidel + }; + + // Default constructor sets the default values + HCSITSRelaxationStep() : + maxSubIterations_( 20 ), + relaxationModel_( InelasticFrictionlessContact ), + deltaMax_( 0 ), + cor_( real_t(0.2) ) + {} + + // Getter and Setter Functions + inline const size_t& getMaxSubIterations() const {return maxSubIterations_;} + inline void setMaxSubIterations(size_t v) { maxSubIterations_ = v;} + + inline const RelaxationModel& getRelaxationModel() const {return relaxationModel_;} + inline void setRelaxationModel(RelaxationModel v) { relaxationModel_ = v;} + + inline const real_t& getDeltaMax() const {return deltaMax_;} + inline void setDeltaMax(real_t v) { deltaMax_ = v;} + + inline const real_t& getCor() const {return cor_;} + inline void setCor(real_t v) { cor_ = v;} + + + /** + * Perform relaxation for a single contact. + * \param cid The index of the contact with the accessor ca. + * \param ca The contact accessor. + * \param pa The particle accessor. + * \param dt The timestep size used. + */ + template <typename CAccessor, typename PAccessor> + void operator()(size_t cid, CAccessor &ca, PAccessor& pa, real_t dt); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticFrictionlessContacts(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa); + + template <typename CAccessor, typename PAccessor> + real_t relaxApproximateInelasticCoulombContactsByDecoupling(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticCoulombContactsByDecoupling(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticCoulombContactsByOrthogonalProjections(size_t cid, real_t dtinv, bool approximate, CAccessor &ca, PAccessor& pa ); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticGeneralizedMaximumDissipationContacts(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ); + + template <typename CAccessor, typename PAccessor> + real_t relaxInelasticContactsByProjectedGaussSeidel(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ); + +private: + // List properties + + size_t maxSubIterations_; + RelaxationModel relaxationModel_; + real_t deltaMax_; + real_t cor_; +}; + + +template <typename CAccessor, typename PAccessor> +inline void HCSITSRelaxationStep::operator()(size_t cid, CAccessor &ca, PAccessor& pa, real_t dt) +{ + static_assert(std::is_base_of<data::IContactAccessor, CAccessor>::value, "please provide a valid contact accessor"); + static_assert(std::is_base_of<data::IAccessor, PAccessor>::value, "please provide a valid accessor"); + + real_t dtinv(real_t(1.0)/dt); + switch( getRelaxationModel() ) { + case InelasticFrictionlessContact: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticFrictionlessContacts(cid, dtinv, ca, pa ))); + break; + + case ApproximateInelasticCoulombContactByDecoupling: + setDeltaMax(std::max( getDeltaMax(), relaxApproximateInelasticCoulombContactsByDecoupling( cid, dtinv, ca, pa ))); + break; + + case ApproximateInelasticCoulombContactByOrthogonalProjections: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticCoulombContactsByOrthogonalProjections( cid, dtinv, true, ca, pa ))); + break; + + case InelasticCoulombContactByDecoupling: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticCoulombContactsByDecoupling( cid, dtinv, ca, pa ))); + break; + + case InelasticCoulombContactByOrthogonalProjections: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticCoulombContactsByOrthogonalProjections( cid, dtinv, false, ca, pa ))); + break; + + case InelasticGeneralizedMaximumDissipationContact: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticGeneralizedMaximumDissipationContacts( cid, dtinv, ca, pa ))); + break; + + case InelasticProjectedGaussSeidel: + setDeltaMax(std::max( getDeltaMax(), relaxInelasticContactsByProjectedGaussSeidel( cid, dtinv, ca, pa ))); + break; + + default: + throw std::runtime_error( "Unsupported relaxation model." ); +} + +WALBERLA_LOG_DETAIL("Delta Max: " << getDeltaMax()); + +} + +//************************************************************************************************* +/*!\brief Relaxes The contact with ID cid once. The contact model is for inelastic unilateral contacts without friction. + * + * \param cid The index of the contact + * \param ca The contact accessor + * \param pa The particle accessor + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). Separating contacts are preferred over + * persisting solutions if valid. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticFrictionlessContacts(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + if( gdot_nto[0] >= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + + // No need to apply zero impulse. + } + else { + // Contact is persisting. + + // Calculate the impulse necessary for a static contact expressed as components in the contact frame. + Vec3 p_wf( ca.getNormal(cid) * ( -ca.getDiag_n_inv(cid) * gdot_nto[0] ) ); + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + + return delta_max; +} +//************************************************************************************************* + + +//************************************************************************************************* +/*!\brief Relaxes all contacts once. The contact model is for inelastic unilateral contacts with approximate Coulomb friction. + * + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). Separating contacts are preferred over + * other solutions if valid. Static solutions are preferred over dynamic solutions. Dynamic + * solutions are computed by decoupling the normal from the frictional components. That is + * for a dynamic contact the normal component is relaxed first followed by the frictional + * components. The determination of the frictional components does not perform any subiterations + * and guarantees that the friction partially opposes slip. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxApproximateInelasticCoulombContactsByDecoupling(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + + if( gdot_nto[0] >= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + + // No need to apply zero impulse. + } + else { + // Contact is persisting (either static or dynamic). + + // Calculate the impulse necessary for a static contact expressed as components in the contact frame. + //Vec3 p_cf( -( contactCache.diag_nto_inv_[i] * gdot_nto ) ); + Vec3 p_cf( -( ca.getDiag_nto_inv(cid) * gdot_nto ) ); + + // Can p_cf[0] be negative even though -gdot_nto[0] > 0? Yes! Try: + // A = [0.5 -0.1 +0.1; -0.1 0.5 -0.1; +0.1 -0.1 1]; + // b = [0.01 -1 -1]'; + // A\b \approx [-0.19 -2.28 -1.21]' + // eig(A) \approx [ 0.40 0.56 1.04]' + + //real_t flimit( contactCache.mu_[i] * p_cf[0] ); + real_t flimit( ca.getMu(cid) * p_cf[0] ); + real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); + if( fsq > flimit * flimit || p_cf[0] < 0 ) { + // Contact cannot be static so it must be dynamic. + // => Complementarity condition on normal reaction now turns into an equation since we know that the normal reaction is definitely not zero. + + // For simplicity we change to a simpler relaxation scheme here: + // 1. Relax normal reaction with the tangential components equal to the previous values + // 2. Relax tangential components with the newly relaxed normal reaction + // Note: The better approach would be to solve the true 3x3 block problem! + // Warning: Simply projecting the frictional components is wrong since then the normal action is no longer 0 and simulations break. + + // Add the action of the frictional reactions from the last iteration to the relative contact velocity in normal direction so we can relax it separately. + // TODO This can be simplified: + //p_cf = trans( contactframe ) * contactCache.p_[i]; + //p_cf[0] = 0; + //p_[i] = contactframe * p_cf; + + + //Vec3 p_tmp = ( contactCache.t_[i] * contactCache.p_[i] ) * contactCache.t_[i] + ( contactCache.o_[i] * contactCache.p_[i] ) * contactCache.o_[i]; + Vec3 p_tmp = ( ca.getT(cid) * ca.getP(cid) ) * ca.getT(cid) + ( ca.getO(cid) * ca.getP(cid) ) * ca.getO(cid); + + + //real_t gdot_n = gdot_nto[0] + contactCache.n_[i] * ( ( contactCache.body1_[i]->getInvInertia() * ( contactCache.r1_[i] % p_tmp ) ) % contactCache.r1_[i] + ( contactCache.body2_[i]->getInvInertia() * ( contactCache.r2_[i] % p_tmp ) ) % contactCache.r2_[i] /* + diag_[i] * p */ ); + real_t gdot_n = gdot_nto[0] + ca.getNormal(cid) * ( ( pa.getInvInertia(bId1) * ( ca.getR1(cid) % p_tmp ) ) % ca.getR1(cid) + ( pa.getInvInertia(bId2) * ( ca.getR2(cid) % p_tmp ) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + //p_cf[0] = -( contactCache.diag_n_inv_[i] * gdot_n ); + p_cf[0] = -( ca.getDiag_n_inv(cid) * gdot_n ); + + // We cannot be sure that gdot_n <= 0 here and thus p_cf[0] >= 0 since we just modified it with the old values of the tangential reactions! => Project + p_cf[0] = std::max( real_c( 0 ), p_cf[0] ); + + // Now add the action of the normal reaction to the relative contact velocity in the tangential directions so we can relax the frictional components separately. + p_tmp = ca.getNormal(cid) * p_cf[0]; + Vec3 gdot2 = gdot + ( pa.getInvInertia(bId1) * ( ca.getR1(cid) % p_tmp ) ) % ca.getR1(cid) + ( pa.getInvInertia(bId2)* ( ca.getR2(cid) % p_tmp ) ) % ca.getR2(cid); + Vec2 gdot_to; + gdot_to[0] = ca.getT(cid) * gdot2; + gdot_to[1] = ca.getO(cid) * gdot2; + + //Vec2 ret = -( contactCache.diag_to_inv_[i] * gdot_to ); + Vec2 ret = -( ca.getDiag_to_inv(cid) * gdot_to ); + p_cf[1] = ret[0]; + p_cf[2] = ret[1]; + + flimit = ca.getMu(cid) * p_cf[0]; + fsq = p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2]; + if( fsq > flimit * flimit ) { + const real_t f( flimit / std::sqrt( fsq ) ); + p_cf[1] *= f; + p_cf[2] *= f; + } + } + else { + // Contact is static. + } + Vec3 p_wf( contactframe * p_cf ); + + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + return delta_max; +} +//************************************************************************************************* + + +//************************************************************************************************* + +/*!\brief Relaxes all contacts once. The contact model is for inelastic unilateral contacts with Coulomb friction. + * + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). Separating contacts are preferred over + * other solutions if valid. Static solutions are preferred over dynamic solutions. Dynamic + * solutions are computed by decoupling the normal from the frictional components. That is + * for a dynamic contact the normal component is relaxed first followed by the frictional + * components. How much the frictional components directly oppose slip as required by the Coulomb + * friction model depends on the number of subiterations performed. If no subiterations are + * performed the friction is guaranteed to be at least partially dissipative. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticCoulombContactsByDecoupling(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + //WALBERLA_LOG_WARNING( "Contact #" << i << " is\nA = \n" << contactCache.diag_nto_[i] << "\nb = \n" << gdot_nto << "\nmu = " << contactCache.mu_[i] ); + + if( gdot_nto[0] >= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + //WALBERLA_LOG_WARNING( "Contact #" << i << " is separating." ); + + // No need to apply zero impulse. + } + else { + // Contact is persisting (either static or dynamic). + + // Calculate the impulse necessary for a static contact expressed as components in the contact frame. + Vec3 p_cf( -( ca.getDiag_nto_inv(cid) * gdot_nto ) ); + + // Can p_cf[0] be negative even though -gdot_nto[0] > 0? Yes! Try: + // A = [0.5 -0.1 +0.1; -0.1 0.5 -0.1; +0.1 -0.1 1]; + // b = [0.01 -1 -1]'; + // A\b \approx [-0.19 -2.28 -1.21]' + // eig(A) \approx [ 0.40 0.56 1.04]' + + real_t flimit( ca.getMu(cid) * p_cf[0] ); + real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); + if( fsq > flimit * flimit || p_cf[0] < 0 ) { + // Contact cannot be static so it must be dynamic. + // => Complementarity condition on normal reaction now turns into an equation since we know that the normal reaction is definitely not zero. + + for (int j = 0; j < 20; ++j) { + // For simplicity we change to a simpler relaxation scheme here: + // 1. Relax normal reaction with the tangential components equal to the previous values + // 2. Relax tangential components with the newly relaxed normal reaction + // Note: The better approach would be to solve the true 3x3 block problem! + // Warning: Simply projecting the frictional components is wrong since then the normal action is no longer 0 and simulations break. + + Vec3 gdotCorrected; + real_t gdotCorrected_n; + Vec2 gdotCorrected_to; + + // Calculate the relative contact velocity in the global world frame (if no normal contact reaction is present at contact i) + p_cf[0] = 0; + // |<- p_cf is orthogonal to the normal and drops out in next line ->| + gdotCorrected = /* ( contactCache.body1_[i]->getInvMass() + contactCache.body2_[i]->getInvMass() ) * p_cf */ gdot + ( pa.getInvInertia(bId1) * ( ca.getR1(cid) % ( ca.getT(cid) * p_cf[1] + ca.getO(cid) * p_cf[2] ) ) ) % ca.getR1(cid) + ( pa.getInvInertia(bId2) * ( ca.getR2(cid) % ( ca.getT(cid) * p_cf[1] + ca.getO(cid) * p_cf[2] ) ) ) % ca.getR2(cid); + gdotCorrected_n = ca.getNormal(cid) * gdotCorrected + ca.getDistance(cid) * dtinv; + + // Relax normal component. + p_cf[0] = std::max( real_c( 0 ), -( ca.getDiag_n_inv(cid) * gdotCorrected_n ) ); + + // Calculate the relative contact velocity in the global world frame (if no frictional contact reaction is present at contact i) + p_cf[1] = p_cf[2] = real_c( 0 ); + // |<- p_cf is orthogonal to the tangential plane and drops out ->| + gdotCorrected = /* ( contactCache.body1_[i]->getInvMass() + contactCache.body2_[i]->getInvMass() ) * p_cf */ gdot + ( pa.getInvInertia(bId1) * ( ca.getR1(cid) % ( ca.getNormal(cid) * p_cf[0] ) ) ) % ca.getR1(cid) + ( pa.getInvInertia(bId2) * ( ca.getR2(cid) % ( ca.getNormal(cid) * p_cf[0] ) ) ) % ca.getR2(cid); + gdotCorrected_to[0] = ca.getT(cid) * gdotCorrected; + gdotCorrected_to[1] = ca.getO(cid) * gdotCorrected; + + // Relax frictional components. + Vec2 ret = -( ca.getDiag_to_inv(cid) * gdotCorrected_to ); + p_cf[1] = ret[0]; + p_cf[2] = ret[1]; + + flimit = ca.getMu(cid) * p_cf[0]; + fsq = p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2]; + if( fsq > flimit * flimit ) { + // 3.2.1 Decoupling + // \tilde{x}^0 = p_cf[1..2] + + // Determine \tilde{A} + Mat2 diag_to( ca.getDiag_nto(cid)(1, 1), ca.getDiag_nto(cid)(1, 2), ca.getDiag_nto(cid)(2, 1), ca.getDiag_nto(cid)(2, 2) ); + + const real_t f( flimit / std::sqrt( fsq ) ); + //p_cf[1] *= f; + //p_cf[2] *= f; + + // Determine search interval for Golden Section Search + const real_t phi( real_c(0.5) * ( real_c(1) + std::sqrt( real_c( 5 ) ) ) ); + real_t shift( std::atan2( -p_cf[2], p_cf[1] ) ); + real_t acos_f( std::acos( f ) ); + + //WALBERLA_LOG_WARNING( acos_f << " " << shift ); + + real_t alpha_left( -acos_f - shift ); + //Vec2 x_left( flimit * std::cos( alpha_left ), flimit * std::sin( alpha_left ) ); + //real_t f_left( 0.5 * trans( x_left ) * ( diag_to * x_left ) - trans( x_left ) * ( -gdot_to ) ); + + real_t alpha_right( acos_f - shift ); + //Vec2 x_right( flimit * std::cos( alpha_right ), flimit * std::sin( alpha_right ) ); + //real_t f_right( 0.5 * trans( x_right ) * ( diag_to * x_right ) - trans( x_right ) * ( -gdot_to ) ); + + real_t alpha_mid( ( alpha_right + alpha_left * phi ) / ( 1 + phi ) ); + Vec2 x_mid( flimit * std::cos( alpha_mid ), flimit * std::sin( alpha_mid ) ); + real_t f_mid( real_c(0.5) * x_mid * ( diag_to * x_mid ) - x_mid * ( -gdotCorrected_to ) ); + + bool leftlarger = false; + for( size_t k = 0; k < getMaxSubIterations(); ++k ) { + real_t alpha_next( alpha_left + ( alpha_right - alpha_mid ) ); + Vec2 x_next( flimit * std::cos( alpha_next ), flimit * std::sin( alpha_next ) ); + real_t f_next( real_c(0.5) * x_next * ( diag_to * x_next ) - x_next * ( -gdotCorrected_to ) ); + //WALBERLA_LOG_WARNING( "[(" << alpha_left << ", ?); (" << alpha_mid << ", " << f_mid << "); (" << alpha_right << ", ?)] <- (" << alpha_next << ", " << f_next << ")" ); + //WALBERLA_LOG_WARNING( "left: " << alpha_mid - alpha_left << " right: " << alpha_right - alpha_mid << " ll: " << leftlarger ); + //WALBERLA_ASSERT(leftlarger ? (alpha_mid - alpha_left > alpha_right - alpha_mid) : (alpha_mid - alpha_left < alpha_right - alpha_mid), "ll inconsistent!" ); + + if (leftlarger) { + // left interval larger + if( f_next < f_mid ) { + alpha_right = alpha_mid; + alpha_mid = alpha_next; + x_mid = x_next; + f_mid = f_next; + leftlarger = true; + } + else { + alpha_left = alpha_next; + leftlarger = false; + } + } + else { + // right interval larger + if( f_next < f_mid ) { + alpha_left = alpha_mid; + alpha_mid = alpha_next; + x_mid = x_next; + f_mid = f_next; + leftlarger = false; + } + else { + alpha_right = alpha_next; + leftlarger = true; + } + } + } + //WALBERLA_LOG_WARNING( "dalpha = " << alpha_right - alpha_left ); + + p_cf[1] = x_mid[0]; + p_cf[2] = x_mid[1]; + } + } + //WALBERLA_LOG_WARNING( "Contact #" << i << " is dynamic." ); + } + else { + // Contact is static. + //WALBERLA_LOG_WARNING( "Contact #" << i << " is static." ); + } + + //WALBERLA_LOG_WARNING( "Contact reaction in contact frame: " << p_cf << "\n" << ca.getDiag_nto(cid)*p_cf + gdot_nto ); + Vec3 p_wf( contactframe * p_cf ); + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + + return delta_max; +} +//************************************************************************************************* + + +//************************************************************************************************* +/*!\brief Relaxes all contacts once. The contact model is for inelastic unilateral contacts with Coulomb friction. + * + * \param dtinv The inverse of the current time step. + * \param approximate Use the approximate model showing bouncing. + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). The iterative method to solve the contact + * problem is e.g. described in the article "A matrix-free cone complementarity approach for + * solving large-scale, nonsmooth, rigid body dynamics" by A. Tasora and M. Anitescu in Computer + * Methods in Applied Mechanics and Engineering (Volume 200, Issues 5–8, 15 January 2011, + * Pages 439-453). The contact model is purely quadratic and convergence should be good but depends + * on a parameter. The one-contact problem has a unique solution. The frictional reactions + * for a dynamic contact converge to those that directly oppose slip. However, the contact is + * not perfectly inelastic for dynamic contacts but bounces. These vertical motions tend to + * go to zero for smaller time steps and can be interpreted as exaggerated vertical motions + * coming from micro asperities (see "Optimization-based simulation of nonsmooth rigid multibody + * dynamics" by M. Anitescu in Mathematical Programming (Volume 105, Issue 1, January 2006, Pages + * 113-143). These motions can be prevented by a small change in the iteration proposed in "The + * bipotential method: a constructive approach to design the complete contact law with friction and + * improved numerical algorithms" by G. De Saxce and Z-Q. Feng in Mathematical and Computer + * Modelling (Volume 28, Issue 4, 1998, Pages 225-245). Which iteration is used is controlled with + * the approximate parameter. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticCoulombContactsByOrthogonalProjections(size_t cid, real_t dtinv, bool approximate, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + + const real_t w( 1 ); // w > 0 + Vec3 p_cf( contactframe.getTranspose() * ca.getP(cid) ); + if( approximate ) { + // Calculate next iterate (Anitescu/Tasora). + p_cf = p_cf - w * ( ca.getDiag_nto(cid) * p_cf + gdot_nto ); + } + else { + // Calculate next iterate (De Saxce/Feng). + Vec3 tmp( ca.getDiag_nto(cid) * p_cf + gdot_nto ); + tmp[0] += std::sqrt( math::sq( tmp[1] ) + math::sq( tmp[2] ) ) * ca.getMu(cid); + p_cf = p_cf - w * tmp; + } + + // Project. + real_t flimit( ca.getMu(cid) * p_cf[0] ); + real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); + if( p_cf[0] > 0 && fsq < flimit * flimit ) { + // Unconstrained minimum is in cone leading to a static contact and no projection + // is necessary. + } + else if( p_cf[0] < 0 && fsq < math::sq( p_cf[0] )/ math::sq( ca.getMu(cid) ) ) { + // Unconstrained minimum is in dual cone leading to a separating contact where no contact + // reaction is present (the unconstrained minimum is projected to the tip of the cone). + p_cf = Vec3(); + } + else { + // The contact is dynamic. + real_t f( std::sqrt( fsq ) ); + p_cf[0] = ( f * ca.getMu(cid) + p_cf[0] ) / ( math::sq( ca.getMu(cid) ) + 1 ); + real_t factor( ca.getMu(cid) * p_cf[0] / f ); + p_cf[1] *= factor; + p_cf[2] *= factor; + } + + Vec3 p_wf( contactframe * p_cf ); + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + return delta_max; +} +//************************************************************************************************* + + +//************************************************************************************************* + +/*!\brief Relaxes all contacts once. The contact model is for inelastic unilateral contacts with the generalized maximum dissipation principle for friction. + * + * \return The largest variation of contact impulses in the L-infinity norm. + * + * This function is to be called from resolveContacts(). Dynamic solutions are computed by + * minimizing the kinetic energy along the intersection of the plane of maximum compression and + * the friction cone. + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticGeneralizedMaximumDissipationContacts(size_t cid, real_t dtinv, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + + // Remove velocity corrections of this contact's reaction. + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + Vec3 gdot ( ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) - + ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) + + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) - + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) /* + diag_[i] * p */ ); + + // Change from the global world frame to the contact frame + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 gdot_nto( contactframe.getTranspose() * gdot ); + + // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + ca.getDistance(cid) ) * dtinv; + + + if( gdot_nto[0] >= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + + // No need to apply zero impulse. + } + else { + // Contact is persisting (either static or dynamic). + + // Calculate the impulse necessary for a static contact expressed as components in the contact frame. + Vec3 p_cf( -( ca.getDiag_nto_inv(cid) * gdot_nto ) ); + + // Can p_cf[0] be negative even though -gdot_nto[0] > 0? Yes! Try: + // A = [0.5 -0.1 +0.1; -0.1 0.5 -0.1; +0.1 -0.1 1]; + // b = [0.01 -1 -1]'; + // A\b \approx [-0.19 -2.28 -1.21]' + // eig(A) \approx [ 0.40 0.56 1.04]' + + real_t flimit( ca.getMu(cid) * p_cf[0] ); + real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); + if( fsq > flimit * flimit || p_cf[0] < 0 ) { + // Contact cannot be static so it must be dynamic. + // => Complementarity condition on normal reaction now turns into an equation since we know that the normal reaction is definitely not zero. + + // \breve{x}^0 = p_cf[1..2] + + // Eliminate normal component from 3x3 system: ca.getDiag_nto(cid)*p_cf + gdot_nto => \breve{A} \breve{x} - \breve{b} + const real_t invA_nn( math::inv( ca.getDiag_nto(cid)(0, 0) ) ); + const real_t offdiag( ca.getDiag_nto(cid)(1, 2) - invA_nn * ca.getDiag_nto(cid)(0, 1) * ca.getDiag_nto(cid)(0, 2) ); + Mat2 A_breve( ca.getDiag_nto(cid)(1, 1) - invA_nn *math::sq( ca.getDiag_nto(cid)(0, 1) ), offdiag, offdiag, ca.getDiag_nto(cid)(2, 2) - invA_nn *math::sq( ca.getDiag_nto(cid)(0, 2) ) ); + Vec2 b_breve( -gdot_nto[1] + invA_nn * ca.getDiag_nto(cid)(0, 1) * gdot_nto[0], -gdot_nto[2] + invA_nn * ca.getDiag_nto(cid)(0, 2) * gdot_nto[0] ); + + const real_t shiftI( std::atan2( -ca.getDiag_nto(cid)(0, 2), ca.getDiag_nto(cid)(0, 1) ) ); + const real_t shiftJ( std::atan2( -p_cf[2], p_cf[1] ) ); + const real_t a3( std::sqrt(math::sq( ca.getDiag_nto(cid)(0, 1) ) +math::sq( ca.getDiag_nto(cid)(0, 2) ) ) ); + const real_t fractionI( -ca.getDiag_nto(cid)(0, 0) / ( ca.getMu(cid) * a3 ) ); + const real_t fractionJ( std::min( invA_nn * ca.getMu(cid) * ( ( -gdot_nto[0] ) / std::sqrt( fsq ) - a3 * std::cos( shiftI - shiftJ ) ), real_c( 1 ) ) ); + + // Search interval determination. + real_t alpha_left, alpha_right; + if( fractionJ < -1 ) { + // J is complete + const real_t angleI( std::acos( fractionI ) ); + alpha_left = -angleI - shiftI; + alpha_right = +angleI - shiftI; + if( alpha_left < 0 ) { + alpha_left += 2 * math::pi; + alpha_right += 2 * math::pi; + } + } + else if( ca.getDiag_nto(cid)(0, 0) > ca.getMu(cid) * a3 ) { + // I is complete + const real_t angleJ( std::acos( fractionJ ) ); + alpha_left = -angleJ - shiftJ; + alpha_right = +angleJ - shiftJ; + if( alpha_left < 0 ) { + alpha_left += 2 * math::pi; + alpha_right += 2 * math::pi; + } + } + else { + // neither I nor J is complete + const real_t angleJ( std::acos( fractionJ ) ); + real_t alpha1_left( -angleJ - shiftJ ); + real_t alpha1_right( +angleJ - shiftJ ); + if( alpha1_left < 0 ) { + alpha1_left += 2 * math::pi; + alpha1_right += 2 * math::pi; + } + const real_t angleI( std::acos( fractionI ) ); + real_t alpha2_left( -angleI - shiftI ); + real_t alpha2_right( +angleI - shiftI ); + if( alpha2_left < 0 ) { + alpha2_left += 2 * math::pi; + alpha2_right += 2 * math::pi; + } + + // Swap intervals if second interval does not start right of the first interval. + if( alpha1_left > alpha2_left ) { + std::swap( alpha1_left, alpha2_left ); + std::swap( alpha1_right, alpha2_right ); + } + + if( alpha2_left > alpha1_right ) { + alpha2_right -= 2*math::pi; + if( alpha2_right > alpha1_right ) { + // [alpha1_left; alpha1_right] \subset [alpha2_left; alpha2_right] + } + else { + // [alpha2_left; alpha2_right] intersects the left end of [alpha1_left; alpha1_right] + alpha1_right = alpha2_right; + } + } + else { + alpha1_left = alpha2_left; + if( alpha2_right > alpha1_right ) { + // [alpha2_left; alpha2_right] intersects the right end of [alpha1_left; alpha1_right] + } + else { + // [alpha2_left; alpha2_right] \subset [alpha1_left; alpha1_right] + alpha1_right = alpha2_right; + } + } + + alpha_left = alpha1_left; + alpha_right = alpha1_right; + } + + const real_t phi( real_c(0.5) * ( real_c(1) + std::sqrt( real_c( 5 ) ) ) ); + real_t alpha_mid( ( alpha_right + alpha_left * phi ) / ( 1 + phi ) ); + Vec2 x_mid; + real_t f_mid; + + { + real_t r_ub = ca.getMu(cid) * ( -gdot_nto[0] ) / ( ca.getDiag_nto(cid)(0, 0) + ca.getMu(cid) * a3 * std::cos( alpha_mid + shiftI ) ); + if( r_ub < 0 ) + r_ub = math::Limits<real_t>::inf(); + x_mid = Vec2( r_ub * std::cos( alpha_mid ), r_ub * std::sin( alpha_mid ) ); + f_mid = real_c(0.5) * x_mid * ( A_breve * x_mid ) - x_mid * b_breve; + } + + bool leftlarger = false; + for( size_t k = 0; k < maxSubIterations_; ++k ) { + real_t alpha_next( alpha_left + ( alpha_right - alpha_mid ) ); + real_t r_ub = ca.getMu(cid) * ( -gdot_nto[0] ) / ( ca.getDiag_nto(cid)(0, 0) + ca.getMu(cid) * a3 * std::cos( alpha_next + shiftI ) ); + if( r_ub < 0 ) + r_ub = math::Limits<real_t>::inf(); + Vec2 x_next( r_ub * std::cos( alpha_next ), r_ub * std::sin( alpha_next ) ); + real_t f_next( real_c(0.5) * x_next * ( A_breve * x_next ) - x_next * b_breve ); + + //WALBERLA_LOG_WARNING( "[(" << alpha_left << ", ?); (" << alpha_mid << ", " << f_mid << "); (" << alpha_right << ", ?)] <- (" << alpha_next << ", " << f_next << ")" ); + //WALBERLA_LOG_WARNING( "left: " << alpha_mid - alpha_left << " right: " << alpha_right - alpha_mid << " ll: " << leftlarger ); + //WALBERLA_ASSERT(leftlarger ? (alpha_mid - alpha_left > alpha_right - alpha_mid) : (alpha_mid - alpha_left < alpha_right - alpha_mid), "ll inconsistent!" ); + + if (leftlarger) { + // left interval larger + if( f_next < f_mid ) { + alpha_right = alpha_mid; + alpha_mid = alpha_next; + x_mid = x_next; + f_mid = f_next; + leftlarger = true; + } + else { + alpha_left = alpha_next; + leftlarger = false; + } + } + else { + // right interval larger + if( f_next < f_mid ) { + alpha_left = alpha_mid; + alpha_mid = alpha_next; + x_mid = x_next; + f_mid = f_next; + leftlarger = false; + } + else { + alpha_right = alpha_next; + leftlarger = true; + } + } + } + //WALBERLA_LOG_DETAIL( "dalpha = " << alpha_right - alpha_left << "\n"); + { + real_t alpha_init( std::atan2( p_cf[2], p_cf[1] ) ); + real_t r_ub = ca.getMu(cid) * ( -gdot_nto[0] ) / ( ca.getDiag_nto(cid)(0, 0) + ca.getMu(cid) * a3 * std::cos( alpha_init + shiftI ) ); + if( r_ub < 0 ) + r_ub = math::Limits<real_t>::inf(); + Vec2 x_init( r_ub * std::cos( alpha_init ), r_ub * std::sin( alpha_init ) ); + real_t f_init( real_c(0.5) * x_init * ( A_breve * x_init ) - x_init * b_breve ); + + if( f_init < f_mid ) + { + x_mid = x_init; + WALBERLA_LOG_DETAIL( "Replacing solution by primitive dissipative solution (" << f_init << " < " << f_mid << " at " << alpha_init << " vs. " << alpha_mid << ").\n"); + } + } + + p_cf[0] = invA_nn * ( -gdot_nto[0] - ca.getDiag_nto(cid)(0, 1) * x_mid[0] - ca.getDiag_nto(cid)(0, 2) * x_mid[1] ); + p_cf[1] = x_mid[0]; + p_cf[2] = x_mid[1]; + //WALBERLA_LOG_DETAIL( "Contact #" << i << " is dynamic." ); + } + else { + // Contact is static. + //WALBERLA_LOG_DETAIL( "Contact #" << i << " is static." ); + } + Vec3 p_wf( contactframe * p_cf ); + Vec3 dp( ca.getP(cid) - p_wf ); + delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); + //WALBERLA_LOG_DETAIL( "Contact reaction in contact frame: " << p_cf << "\nContact action in contact frame: " << ca.getDiag_nto(cid)*p_cf + gdot_nto ); + + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) += pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) += pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) -= pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) -= pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + return delta_max; +} +//************************************************************************************************* + +//************************************************************************************************* +//************************************************************************************************* +/*!\brief Relaxes all contacts once. The contact model is from the paper by Tschisgale et al. + * "A constraint-based collision model for Cosserat rods" and works with a coefficient of + * restitution cor with 0 < cor <= 1. + * + * \return The largest variation of contact impulses in the L-infinity norm. + * + */ +template <typename CAccessor, typename PAccessor> +inline real_t HCSITSRelaxationStep::relaxInelasticContactsByProjectedGaussSeidel(size_t cid, real_t /*dtinv*/, CAccessor &ca, PAccessor& pa ) +{ + real_t delta_max( 0 ); + size_t bId1 = ca.getId1(cid); + size_t bId2 = ca.getId2(cid); + // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) + // This is the negative contact velocity as for the other methods + Vec3 gdot ( ( pa.getLinearVelocity(bId2) + pa.getDv(bId2) ) - + ( pa.getLinearVelocity(bId1) + pa.getDv(bId1) ) + + ( pa.getAngularVelocity(bId2) + pa.getDw(bId2) ) % ca.getR2(cid) - + ( pa.getAngularVelocity(bId1) + pa.getDw(bId1) ) % ca.getR1(cid) /* + diag_[i] * p */ ); + + // Use restitution hypothesis + gdot = gdot + getCor()*(gdot*ca.getNormal(cid))*ca.getNormal(cid); + + Mat3 contactframe( ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + + // + // Turn system matrix back to the world frame. + Mat3 K = contactframe * ca.getDiag_nto(cid) * contactframe.getTranspose(); + Mat3 Kinv = contactframe * ca.getDiag_nto_inv(cid) * contactframe.getTranspose(); + + // Compute impulse necessary in the world frame + Vec3 p_wf( - Kinv * gdot ); + + + + if( gdot * ca.getNormal(cid) <= 0 ) { + // Contact is separating if no contact reaction is present at contact i. + delta_max = std::max( delta_max, std::max( std::abs( ca.getP(cid)[0] ), std::max( std::abs( ca.getP(cid)[1] ), std::abs( ca.getP(cid)[2] ) ) ) ); + ca.getPRef(cid) = Vec3(); + // No need to apply zero impulse. + } + else { + // Dissect the impuls in a tangetial and normal directions + // Use the inverted contact frame with -n as in the publication + Mat3 reversedContactFrame( -ca.getNormal(cid), ca.getT(cid), ca.getO(cid) ); + Vec3 p_rcf( reversedContactFrame.getTranspose() * p_wf ); + + // Check the frictional limit + real_t flimit( ca.getMu(cid) * p_rcf[0] ); + + // Square of tangential components of the impulse + real_t fsq( p_rcf[1] * p_rcf[1] + p_rcf[2] * p_rcf[2] ); + + if( fsq > flimit * flimit || p_rcf[0] < 0 ) { + // Contact cannot be static so it must be dynamic. + // Calculate corrected contact reaction by the method of Tschigale et al. + // Normal component is close to 0 or negative, and we cannot asses a tangential direction + // Treat this case with the 0 impulse + if (fsq < real_t(1e-8)) { + delta_max = std::max(delta_max, std::max(std::abs(ca.getP(cid)[0]), + std::max(std::abs(ca.getP(cid)[1]), std::abs(ca.getP(cid)[2])))); + ca.getPRef(cid) = Vec3(); + } else { + // tangential direction of sliding + Vec3 tan_dir = ((p_rcf[1] * ca.getT(cid)) + (p_rcf[2] * ca.getO(cid))).getNormalized(); + // scalar magnitude of pv. + real_t abspv = ((K * p_wf) * ca.getNormal(cid)) / + ((K * (-ca.getNormal(cid) + ca.getMu(cid) * tan_dir)) * ca.getNormal(cid)); + p_wf = abspv * (-ca.getNormal(cid) + ca.getMu(cid) * tan_dir); + + } + } + // Calculate variation + Vec3 dp(ca.getP(cid) - p_wf); + delta_max = std::max(delta_max, std::max(std::abs(dp[0]), std::max(std::abs(dp[1]), std::abs(dp[2])))); + ca.getPRef(cid) = p_wf; + + // Apply impulse right away. + pa.getDvRef(bId1) -= pa.getInvMass(bId1) * ca.getP(cid); + pa.getDwRef(bId1) -= pa.getInvInertia(bId1) * (ca.getR1(cid) % ca.getP(cid)); + pa.getDvRef(bId2) += pa.getInvMass(bId2) * ca.getP(cid); + pa.getDwRef(bId2) += pa.getInvInertia(bId2) * (ca.getR2(cid) % ca.getP(cid)); + } + return delta_max; +} +//************************************************************************************************* + + +} //namespace kernel +} //namespace mesa_pd +} //namespace walberla \ No newline at end of file diff --git a/src/mesa_pd/kernel/InitContactsForHCSITS.h b/src/mesa_pd/kernel/InitContactsForHCSITS.h new file mode 100644 index 0000000000000000000000000000000000000000..e2d14795204d7372f0b61032f3437653acbbd154 --- /dev/null +++ b/src/mesa_pd/kernel/InitContactsForHCSITS.h @@ -0,0 +1,175 @@ +//====================================================================================================================== +// +// 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 InitContactsForHCSITS.h +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/IContactAccessor.h> +#include <mesa_pd/data/Flags.h> +#include <core/logging/Logging.h> + +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { + +/** +* Init the datastructures for a contact for later use of the HCSITS-Solver. +* Call this kernel on all contacts that you want to treat with HCSITS before performing any relaxation timesteps. +* Use setErp() to set the error reduction parameter, which defines the share of the overlap that is resolved in one step +* (0 meaning after the relaxation the overlap is the same, 1 meaning the particles will have no overlap after relaxation). +* Use setFriction(a,b, cof) to define the coefficient of friction cof between materials a and b. It is assumed to be + * symmetric w.r.t. the materials. +* \ingroup mesa_pd_kernel +*/ +class InitContactsForHCSITS{ +public: + + // Default constructor sets the default values + explicit InitContactsForHCSITS(const uint_t numParticleTypes) : + numParticleTypes_ (numParticleTypes), + erp_( real_t(0.8) ), + maximumPenetration_( 0 ) + { + + + friction_.resize(numParticleTypes * numParticleTypes, real_t(0)); + } + + // Getter and Setter Functions + inline const real_t& getErp() const {return erp_;} + inline void setErp(real_t v) { erp_ = v;} + + inline const real_t& getMaximumPenetration() const {return maximumPenetration_;} + inline void setMaximumPenetration(real_t v) { maximumPenetration_ = v;} + + + // Getter and Setter Functions for material parameter combinations (they are assumed symmetric). + + inline void setFriction(const size_t type1, const size_t type2, const real_t& val) + { + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + friction_[numParticleTypes_*type1 + type2] = val; + friction_[numParticleTypes_*type2 + type1] = val; + } + + + inline real_t getFriction(const size_t type1, const size_t type2) const + { + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + WALBERLA_ASSERT_FLOAT_EQUAL( friction_[numParticleTypes_*type1 + type2], + friction_[numParticleTypes_*type2 + type1], + "parameter matrix for friction not symmetric!"); + return friction_[numParticleTypes_*type1 + type2]; + } + + // List material parameters + + std::vector<real_t> friction_ {}; + + template<typename CAccessor, typename PAccessor> + void operator()(size_t c, CAccessor &ca, PAccessor &pa); + +private: + // List properties + const uint_t numParticleTypes_; + + + real_t erp_; + real_t maximumPenetration_; + + + +}; + + +template<typename CAccessor, typename PAccessor> +inline void InitContactsForHCSITS::operator()(size_t c, CAccessor &ca, PAccessor &pa) { + + static_assert(std::is_base_of<data::IContactAccessor, CAccessor>::value, "please provide a valid contact accessor"); + static_assert(std::is_base_of<data::IAccessor, PAccessor>::value, "please provide a valid particle accessor"); + + size_t bId1 = ca.getId1(c); + size_t bId2 = ca.getId2(c); + ca.setR1(c, ca.getPosition(c) - pa.getPosition(bId1)); + ca.setR2(c, ca.getPosition(c) - pa.getPosition(bId2)); + + + // construct vector perpendicular to the normal (cross product with cardinal basis vector where the 1 component is where the other vector has its component of smallest magnitude) + if (std::fabs(ca.getNormalRef(c)[0]) < std::fabs(ca.getNormalRef(c)[1])) { + if (std::fabs(ca.getNormalRef(c)[0]) < std::fabs(ca.getNormalRef(c)[2])) + ca.setT(c, Vec3(0, ca.getNormalRef(c)[2], -ca.getNormalRef(c)[1])); // = n x (1, 0, 0)^T + else + ca.setT(c, Vec3(ca.getNormalRef(c)[1], -ca.getNormalRef(c)[0], 0)); // = n x (0, 0, 1)^T + } else { + if (std::fabs(ca.getNormalRef(c)[1]) < std::fabs(ca.getNormalRef(c)[2])) + ca.setT(c, Vec3(-ca.getNormalRef(c)[2], 0, ca.getNormalRef(c)[0])); // = n x (0, 1, 0)^T + else + ca.setT(c, Vec3(ca.getNormalRef(c)[1], -ca.getNormalRef(c)[0], 0)); // = n x (0, 0, 1)^T + } + normalize(ca.getTRef(c)); + ca.setO(c, ca.getNormal(c) % ca.getT(c)); + + Mat3 contactframe(ca.getNormal(c), ca.getT(c), ca.getO(c)); + + // If the distance is negative then penetration is present. This is an error and should be corrected. + // Correcting the whole error is not recommended since due to the linearization the errors cannot + // completely fixed anyway and the error reduction will introduce artificial restitution. + // However, if the distance is positive then it is not about error correction but the distance that + // can still be overcome without penetration and thus no error correction parameter should be applied. + if (ca.getDistance(c) < real_t(0.0)) { + setMaximumPenetration(std::max(getMaximumPenetration(), -ca.getDistance(c))); + ca.getDistanceRef(c) *= erp_; + } + + ca.getMuRef(c) = getFriction(pa.getType(bId1), pa.getType(bId2)); + + Mat3 diag = -( + math::skewSymCrossProduct(ca.getR1(c), + math::skewSymCrossProduct(pa.getInvInertia(bId1), ca.getR1(c))) + + math::skewSymCrossProduct(ca.getR2(c), + math::skewSymCrossProduct(pa.getInvInertia(bId2), ca.getR2(c)))); + diag[0] += pa.getInvMass(bId1) + pa.getInvMass(bId2); + diag[4] += pa.getInvMass(bId1) + pa.getInvMass(bId2); + diag[8] += pa.getInvMass(bId1) + pa.getInvMass(bId2); + + diag = contactframe.getTranspose() * diag * contactframe; + + // Diagonal block is known to be positive-definite and thus an inverse always exists. + ca.getDiag_ntoRef(c) = diag; + ca.getDiag_nto_invRef(c) = diag.getInverse(); + ca.getDiag_n_invRef(c) = math::inv(diag[0]); + ca.getDiag_to_invRef(c) = Mat2(diag[4], diag[5], diag[7], diag[8]).getInverse(); +} + +} +} +} \ No newline at end of file diff --git a/src/mesa_pd/kernel/InitParticlesForHCSITS.h b/src/mesa_pd/kernel/InitParticlesForHCSITS.h new file mode 100644 index 0000000000000000000000000000000000000000..5a72e4fadaee5c914b88c654ae40cf41a6955d0d --- /dev/null +++ b/src/mesa_pd/kernel/InitParticlesForHCSITS.h @@ -0,0 +1,119 @@ +//====================================================================================================================== +// +// 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 InitParticlesForHCSITS.h +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/Flags.h> +#include <core/logging/Logging.h> + +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { +/** + * Init the datastructures for the particles for later use of the HCSITS-Solver. + * Call this kernel on all particles that will be treated with HCSITS before performing any relaxation timesteps. + * Use setGlobalAcceleration() to set an accelleration action uniformly across all particles (e.g. gravity) + * \ingroup mesa_pd_kernel + */ +class InitParticlesForHCSITS +{ +public: + // Default constructor sets the default values + InitParticlesForHCSITS() : + globalAcceleration_( 0 ) + {} + + // Getter and Setter Functions for properties + inline const walberla::mesa_pd::Vec3& getGlobalAcceleration() const {return globalAcceleration_;} + inline void setGlobalAcceleration(walberla::mesa_pd::Vec3 v) { globalAcceleration_ = v;} + + + template <typename Accessor> + void operator()(size_t j, Accessor& ac, real_t dt); + + template <typename Accessor> + void initializeVelocityCorrections(Accessor& ac, size_t body, Vec3& dv, Vec3& dw, real_t dt ) const; + +private: + // List properties + + walberla::mesa_pd::Vec3 globalAcceleration_; + +}; + + +template <typename Accessor> +inline void InitParticlesForHCSITS::operator()(size_t j, Accessor& ac, real_t dt) +{ + using namespace data::particle_flags; + static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor"); + auto particle_flags = ac.getFlagsRef(j); + if (isSet(particle_flags, GLOBAL)){ + WALBERLA_CHECK( isSet(particle_flags, FIXED), "Global bodies need to have infinite mass as they are not communicated!" ); + initializeVelocityCorrections( ac, j, ac.getDvRef(j), ac.getDwRef(j), dt ); // use applied external forces to calculate starting velocity + }else{ + initializeVelocityCorrections( ac, j, ac.getDvRef(j), ac.getDwRef(j), dt ); // use applied external forces to calculate starting velocity + if(!isSet(particle_flags, FIXED)){ // Update velocities with global acceleration and angular velocity with euler eqn + ac.getLinearVelocityRef(j) = ac.getLinearVelocity(j) + getGlobalAcceleration() * dt; + ac.getAngularVelocityRef(j) = ac.getAngularVelocity(j) + dt * ( ac.getInvInertia(j) * ( ( ac.getInertia(j) * ac.getAngularVelocity(j) ) % ac.getAngularVelocity(j) ) ); + } + } +} + + +//************************************************************************************************* +/*!\brief Calculates the initial velocity corrections of a given body. + * \param ac The particle accessor + * \param body The body whose velocities to time integrate + * \param dv On return the initial linear velocity correction. + * \param w On return the initial angular velocity correction. + * \param dt The time step size. + * \return void + * + * Calculates the velocity corrections effected by external forces and torques in an explicit Euler + * time integration of the velocities of the given body. For fixed objects the velocity corrections + * are set to zero. External forces and torques are reset if indicated by the settings. + */ +template <typename Accessor> +inline void InitParticlesForHCSITS::initializeVelocityCorrections(Accessor& ac, size_t body, Vec3& dv, Vec3& dw, real_t dt ) const +{ + dv = ( ac.getInvMass(body) * dt ) * ac.getForce(body); + dw = dt * ( ac.getInvInertia(body) * ac.getTorque(body) ); + + ac.getForceRef(body) = Vec3(); + ac.getTorqueRef(body) = Vec3(); +} +//************************************************************************************************* + + +} //namespace kernel +} //namespace mesa_pd +} //namespace walberla \ No newline at end of file diff --git a/src/mesa_pd/kernel/IntegrateParticlesHCSITS.h b/src/mesa_pd/kernel/IntegrateParticlesHCSITS.h new file mode 100644 index 0000000000000000000000000000000000000000..7cc75be73f78cba51ba5d78b29e037624710ae42 --- /dev/null +++ b/src/mesa_pd/kernel/IntegrateParticlesHCSITS.h @@ -0,0 +1,158 @@ +//====================================================================================================================== +// +// 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 IntegrateParticlesHCSITS.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/ContactStorage.h> +#include <mesa_pd/data/Flags.h> +#include <core/logging/Logging.h> +#include <core/math/Constants.h> + +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { + +/** + * Kernel performing integration of the particles after the HCSITS iteration is done. + * Call this kernel on all particles j to integrate them by a timestep of size dt. + * The Speed Limiter limits the number of body radii, a particle can travel in one timestep. + * The speed limit factor defines a number of radii that are allowed in a timestep, e.g. a factor of 1 means, that + * a particle can only travel one time its radius in each timestep. + * + * \ingroup mesa_pd_kernel + */ +class IntegrateParticlesHCSITS +{ + public: + // Default constructor sets the default values + IntegrateParticlesHCSITS() : + speedLimiterActive_( false ), + speedLimitFactor_( real_t(1.0) ) + {} + + // Getter and Setter Functions + inline const bool& getSpeedLimiterActive() const {return speedLimiterActive_;} + inline void setSpeedLimiterActive(bool v) { speedLimiterActive_ = v;} + + inline const real_t& getSpeedLimitFactor() const {return speedLimitFactor_;} + inline void setSpeedLimitFactor(real_t v) { speedLimitFactor_ = v;} + + + template <typename PAccessor> + void operator()(size_t j, PAccessor& ac, real_t dt); + + template <typename PAccessor> + void integratePositions(PAccessor& ac, size_t body, Vec3 v, Vec3 w, real_t dt ) const; + + + private: + // List properties + + bool speedLimiterActive_; + real_t speedLimitFactor_; +}; + + +template <typename PAccessor> +inline void IntegrateParticlesHCSITS::operator()(size_t j, PAccessor& ac, real_t dt) +{ + using namespace data::particle_flags; + static_assert(std::is_base_of<data::IAccessor, PAccessor>::value, "please provide a valid accessor"); + + auto body_flags = ac.getFlagsRef(j); + if (isSet(body_flags, FIXED)){ + integratePositions(ac, j, ac.getLinearVelocity(j), ac.getAngularVelocity(j), dt ); + }else{ + integratePositions(ac, j, ac.getLinearVelocity(j) + ac.getDv(j), ac.getAngularVelocity(j) + ac.getDw(j), dt ); + } +} + + + + +//************************************************************************************************* +/*!\brief Time integration of the position and orientation of a given body. + * + * \param body The body whose position and orientation to time integrate + * \param v The linear velocity to use for time integration of the position. + * \param w The angular velocity to use for time integration of the orientation. + * \param dt The time step size. + * \return void + * + * Performs an Euler time integration of the positions of the given body. Velocities are damped if + * indicated by the settings and stored back in the body properties. The bounding box is + * recalculated and it is redetermined whether the body is awake or not. Also the data + * structure tracking the contacts attached to the body are cleared and + */ +template <typename PAccessor> +inline void IntegrateParticlesHCSITS::integratePositions(PAccessor& ac, size_t body, Vec3 v, Vec3 w, real_t dt ) const { + using namespace data::particle_flags; + auto body_flags = ac.getFlags(body); + if (isSet(body_flags, FIXED)) + return; + + + if (getSpeedLimiterActive()) { + const auto speed = v.length(); + const auto edge = ac.getInteractionRadius(body); + if (speed * dt > edge * getSpeedLimitFactor()) { + v = v * (edge * getSpeedLimitFactor() / dt / speed); + } + + const real_t maxPhi = real_t(2) * math::pi * getSpeedLimitFactor(); + const real_t phi = w.length() * dt; + if (phi > maxPhi) { + w = w / phi * maxPhi; + } + } + + // Calculating the translational displacement + ac.getPositionRef(body) = (ac.getPosition(body) + v * dt); + + // Calculating the rotation angle + const Vec3 phi(w * dt); + + // Calculating the new orientation + WALBERLA_ASSERT(!math::isnan(phi), " phi: " << phi << " w: " << w << " dt: " << dt ); + ac.getRotationRef(body).rotate(phi, phi.length()); + + // Storing the velocities back in the body properties + ac.getLinearVelocityRef(body) = v; + ac.getAngularVelocityRef(body) = w; +} +//************************************************************************************************* + + + +} //namespace kernel +} //namespace mesa_pd +} //namespace walberla \ No newline at end of file diff --git a/src/mesa_pd/mpi/notifications/VelocityCorrectionNotification.h b/src/mesa_pd/mpi/notifications/VelocityCorrectionNotification.h new file mode 100644 index 0000000000000000000000000000000000000000..dd4ca2a89d89251f35cca0dfef8750e452cf1b34 --- /dev/null +++ b/src/mesa_pd/mpi/notifications/VelocityCorrectionNotification.h @@ -0,0 +1,120 @@ +//====================================================================================================================== +// +// 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 VelocityCorrectionNotification.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/ParticleStorage.h> +#include <mesa_pd/mpi/notifications/NotificationType.h> +#include <mesa_pd/mpi/notifications/reset.h> + +#include <core/mpi/Datatype.h> +#include <core/mpi/RecvBuffer.h> +#include <core/mpi/SendBuffer.h> + +namespace walberla { +namespace mesa_pd { + +/** + * Transmits corrections of the linear and angular velocity (dv / dw) that were generated by the impulses acting on the + * ghost particles during application of the Hard-Contact-Solvers (HCSITS) to the main particle and adds them up. + * Use this Notification with ReduceProperty only. + */ +class VelocityCorrectionNotification +{ +public: + + struct Parameters + { + id_t uid_; + Vec3 dv_; /* Linear velocity correction */ + Vec3 dw_; /* Angular velocity correction */ + }; + + inline explicit VelocityCorrectionNotification( const data::Particle& p ) : p_(p) {} + + const data::Particle& p_; +}; + + +// Reduce method for reduction (add up the velocity corrections) +void reduce(data::Particle&& p, const VelocityCorrectionNotification::Parameters& objparam) +{ + p.getDvRef() += objparam.dv_; + p.getDwRef() += objparam.dw_; +} + +template<> +void reset<VelocityCorrectionNotification>(data::Particle& p ) +{ + p.setDv( Vec3(real_t(0)) ); + p.setDw( Vec3(real_t(0)) ); +} + +} // namespace mesa_pd +} // namespace walberla + +//====================================================================================================================== +// +// Send/Recv Buffer Serialization Specialization +// +//====================================================================================================================== + +namespace walberla { +namespace mpi { + +template< typename T, // Element type of SendBuffer + typename G> // Growth policy of SendBuffer +mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, const mesa_pd::VelocityCorrectionNotification& obj ) +{ + buf.addDebugMarker( "ft" ); + buf << obj.p_.getUid(); + buf << obj.p_.getDv(); + buf << obj.p_.getDw(); + return buf; +} + +template< typename T> // Element type of RecvBuffer +mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd::VelocityCorrectionNotification::Parameters& objparam ) +{ + buf.readDebugMarker( "ft" ); + buf >> objparam.uid_; + buf >> objparam.dv_; + buf >> objparam.dw_; + return buf; +} + +template< > +struct BufferSizeTrait< mesa_pd::VelocityCorrectionNotification > { + static const bool constantSize = true; + static const uint_t size = BufferSizeTrait<id_t>::size + + BufferSizeTrait<mesa_pd::Vec3>::size + + BufferSizeTrait<mesa_pd::Vec3>::size + + mpi::BUFFER_DEBUG_OVERHEAD; +}; + +} // mpi +} // walberla diff --git a/src/mesa_pd/mpi/notifications/VelocityUpdateNotification.h b/src/mesa_pd/mpi/notifications/VelocityUpdateNotification.h new file mode 100644 index 0000000000000000000000000000000000000000..7f8c1e7c733df07629bbbc4fe3540c19c7dc97d4 --- /dev/null +++ b/src/mesa_pd/mpi/notifications/VelocityUpdateNotification.h @@ -0,0 +1,135 @@ +//====================================================================================================================== +// +// 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 VelocityCorrectionNotification.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/ParticleStorage.h> +#include <mesa_pd/mpi/notifications/NotificationType.h> +#include <mesa_pd/mpi/notifications/reset.h> +#include <core/mpi/Datatype.h> +#include <core/mpi/RecvBuffer.h> +#include <core/mpi/SendBuffer.h> + +namespace walberla { +namespace mesa_pd { + +/** + * Adds up LinearVelocity + Parameters::relaxationParam * velocity_correction in a particle and transmits the result to + * all its ghost particles. + * After adding up and sending the result, the velocity corrections of the main particle are reset to 0. + * After receiving new values, the velocity_corrections of the ghost_particles are reset to 0. + * This notification is used during relaxation with the HCSITS solver. + * Notification for use with BroadcastProperty only. + * + */ +class VelocityUpdateNotification +{ +public: + + struct Parameters + { + static real_t relaxationParam; + id_t uid_; + Vec3 v_; /* Linear velocity */ + Vec3 w_; /* Angular velocity */ + }; + + inline explicit VelocityUpdateNotification( data::Particle& p ) : p_(p) {} + + data::Particle& p_; +}; + +real_t VelocityUpdateNotification::Parameters::relaxationParam = real_t(0.8); + +// Update method for broadcast +void update(data::Particle&& p, const VelocityUpdateNotification::Parameters& objparam) { + // Reset the velocity corrections dv/dw of ghost particle + p.getDvRef() = Vec3(); + p.getDwRef() = Vec3(); + p.getLinearVelocityRef() = objparam.v_; + p.getAngularVelocityRef() = objparam.w_; +} + +template<> +void reset<VelocityUpdateNotification>(data::Particle& p ) +{ + p.setDv( Vec3(real_t(0)) ); + p.setDw( Vec3(real_t(0)) ); + p.setLinearVelocity( Vec3(real_t(0)) ); + p.setAngularVelocity( Vec3(real_t(0)) ); +} + +} // namespace mesa_pd +} // namespace walberla + +//====================================================================================================================== +// +// Send/Recv Buffer Serialization Specialization +// +//====================================================================================================================== + +namespace walberla { +namespace mpi { + +template< typename T, // Element type of SendBuffer + typename G> // Growth policy of SendBuffer +mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, const mesa_pd::VelocityUpdateNotification& obj ) +{ + // Perform the addition + obj.p_.getLinearVelocityRef() = obj.p_.getLinearVelocity() + mesa_pd::VelocityUpdateNotification::Parameters::relaxationParam * obj.p_.getDv(); + obj.p_.getAngularVelocityRef() = obj.p_.getAngularVelocity() + mesa_pd::VelocityUpdateNotification::Parameters::relaxationParam * obj.p_.getDw(); + // Reset the velocity corrections dv/dw of main particle + obj.p_.getDvRef() = mesa_pd::Vec3(); + obj.p_.getDwRef() = mesa_pd::Vec3(); + buf.addDebugMarker( "ft" ); + buf << obj.p_.getUid(); + buf << obj.p_.getLinearVelocity(); + buf << obj.p_.getAngularVelocity(); + return buf; +} + +template< typename T> // Element type of RecvBuffer +mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd::VelocityUpdateNotification::Parameters& objparam ) +{ + buf.readDebugMarker( "ft" ); + buf >> objparam.uid_; + buf >> objparam.v_; + buf >> objparam.w_; + return buf; +} + +template< > +struct BufferSizeTrait< mesa_pd::VelocityUpdateNotification > { + static const bool constantSize = true; + static const uint_t size = BufferSizeTrait<id_t>::size + + BufferSizeTrait<mesa_pd::Vec3>::size + + BufferSizeTrait<mesa_pd::Vec3>::size + + mpi::BUFFER_DEBUG_OVERHEAD; +}; + +} // mpi +} // walberla diff --git a/src/mesa_pd/vtk/ParticleVtkOutput.cpp b/src/mesa_pd/vtk/ParticleVtkOutput.cpp index b63295893ed9827127aabcbe3ae26eb44e48e539..018670be9dd72965049fd3c0006fbe41a3cecd0f 100644 --- a/src/mesa_pd/vtk/ParticleVtkOutput.cpp +++ b/src/mesa_pd/vtk/ParticleVtkOutput.cpp @@ -72,7 +72,7 @@ void ParticleVtkOutput::push( walberla::vtk::Base64Writer& b64, const uint_t dat selectors_[data].second->push(b64, (*ps_)[particleIndices_[point]], component); } -void ParticleVtkOutput::addOutput(const std::string& name, std::shared_ptr<IOutputSelector> selector) +void ParticleVtkOutput::addOutput(const std::string& name, const std::shared_ptr<IOutputSelector>& selector) { if ( std::find_if(selectors_.begin(), selectors_.end(), [&name](const auto& item){return item.first==name;} ) != selectors_.end() ) diff --git a/src/mesa_pd/vtk/ParticleVtkOutput.h b/src/mesa_pd/vtk/ParticleVtkOutput.h index 651403cea4cc750620486306a6debb4290dc8eb1..5cbe88a6d546009b550aac368d836c56aa79f68e 100644 --- a/src/mesa_pd/vtk/ParticleVtkOutput.h +++ b/src/mesa_pd/vtk/ParticleVtkOutput.h @@ -55,7 +55,7 @@ public: template <typename T> void addOutput(const std::string& name) { addOutput(name, std::make_shared<OutputSelector<T>>(T())); } - void addOutput(const std::string& name, std::shared_ptr<IOutputSelector> selector); + void addOutput(const std::string& name, const std::shared_ptr<IOutputSelector>& selector); ///sets a function which decides which particles should be written to file void setParticleSelector( const ParticleSelectorFunc& func) {particleSelector_ = func;} diff --git a/src/pe/ccd/HashGrids.cpp b/src/pe/ccd/HashGrids.cpp index b73a25548efae620d69f6310df736e3900d922e0..0f9d4908dc4ad4c2dea8dab6c95534ebbab13dc3 100644 --- a/src/pe/ccd/HashGrids.cpp +++ b/src/pe/ccd/HashGrids.cpp @@ -788,7 +788,7 @@ void HashGrids::update(WcTimingTree* tt) if (tt != nullptr) tt->start("AddNewBodies"); // Finally add all bodies that were temporarily stored in 'bodiesToAdd_' to the data structure. - if( bodiesToAdd_.size() > 0 ) + if( !bodiesToAdd_.empty() ) { for( auto bodyIt = bodiesToAdd_.begin(); bodyIt < bodiesToAdd_.end(); ++bodyIt ) { diff --git a/src/pe/collision/EPA.cpp b/src/pe/collision/EPA.cpp index 85775e5a89893aa2638ac7ae0cb58d64c750d7a6..6e7eb97551070580c1686139876341868d5923d7 100644 --- a/src/pe/collision/EPA.cpp +++ b/src/pe/collision/EPA.cpp @@ -263,7 +263,7 @@ bool EPA::doEPA( GeomPrimitive &geom1, GeomPrimitive &geom2, const GJK& gjk, Vec } } - if(entryHeap.size() == 0) { + if(entryHeap.empty()) { //unrecoverable error. return false; } @@ -461,7 +461,7 @@ bool EPA::doEPA( GeomPrimitive &geom1, GeomPrimitive &geom2, const GJK& gjk, Vec firstTriangle->link(2, lastTriangle, 1); } - } while (entryHeap.size() > 0 && entryHeap[0]->getSqrDist() <= upperBoundSqr); + } while (!entryHeap.empty() && entryHeap[0]->getSqrDist() <= upperBoundSqr); //Normal must be inverted retNormal = -current->getClosest().getNormalized(); diff --git a/src/pe/collision/GJK.cpp b/src/pe/collision/GJK.cpp index e25c62b5e5114fbde39d5e8ee127b12af18f21da..d2320f9ec63d8c9c68b919e149e8a21d360a695f 100644 --- a/src/pe/collision/GJK.cpp +++ b/src/pe/collision/GJK.cpp @@ -286,7 +286,7 @@ inline real_t GJK::calcDistance( Vec3& normal, Vec3& contactPoint ) //its distance to the origin is the distance of the two objects real_t dist= 0.0; - real_t barCoords[3] = { 0.0, 0.0, 0.0}; + std::array<real_t, 3> barCoords = {{ 0.0, 0.0, 0.0 }}; real_t& u = barCoords[0]; real_t& v = barCoords[1]; real_t& w = barCoords[2]; diff --git a/src/pe/cr/HCSITS.h b/src/pe/cr/HCSITS.h index 896f358080c9aed186ecf1d203b503f3285b297c..67056df71da2ccc3b15a3085517e0529c951d502 100644 --- a/src/pe/cr/HCSITS.h +++ b/src/pe/cr/HCSITS.h @@ -84,12 +84,12 @@ private: { void resize(const size_t n); - std::vector<Vec3> r1_, r2_; + std::vector<Vec3> r1_, r2_; // vector pointing from body1/body2 to the contact position std::vector<BodyID> body1_, body2_; - std::vector<Vec3> n_, t_, o_; - std::vector<real_t> dist_; - std::vector<real_t> mu_; - std::vector<Mat3> diag_nto_; + std::vector<Vec3> n_, t_, o_; // contact normal and the two other directions perpendicular to the normal + std::vector<real_t> dist_; // overlap length, a contact is present if dist_ < 0 + std::vector<real_t> mu_; // contact friction + std::vector<Mat3> diag_nto_; std::vector<Mat3> diag_nto_inv_; std::vector<Mat2> diag_to_inv_; std::vector<real_t> diag_n_inv_; @@ -102,9 +102,9 @@ public: enum RelaxationModel { InelasticFrictionlessContact, ApproximateInelasticCoulombContactByDecoupling, -// ApproximateInelasticCoulombContactByOrthogonalProjections, + ApproximateInelasticCoulombContactByOrthogonalProjections, InelasticCoulombContactByDecoupling, -// InelasticCoulombContactByOrthogonalProjections, + InelasticCoulombContactByOrthogonalProjections, InelasticGeneralizedMaximumDissipationContact }; //********************************************************************************************** @@ -136,6 +136,7 @@ public: inline const std::map<IBlockID::IDType, ContactCache> getContactCache() const { return blockToContactCache_; } inline real_t getSpeedLimitFactor() const; inline size_t getMaxIterations() const { return maxIterations_; } + inline real_t getOverRelaxationParameter() const { return overRelaxationParam_; } inline real_t getRelaxationParameter() const { return relaxationParam_; } inline real_t getErrorReductionParameter() const { return erp_; } inline RelaxationModel getRelaxationModel() const { return relaxationModel_; } @@ -145,6 +146,7 @@ public: //**Set functions******************************************************************************* /*!\name Set functions */ //@{ + inline void setOverRelaxationParameter( real_t omega ); inline void setRelaxationParameter( real_t f ); inline void setMaxIterations( size_t n ); inline void setRelaxationModel( RelaxationModel relaxationModel ); @@ -235,6 +237,7 @@ private: size_t maxSubIterations_; //!< Maximum number of iterations of iterative solvers in the one-contact problem. real_t abortThreshold_; //!< If L-infinity iterate difference drops below this threshold the iteration is aborted. RelaxationModel relaxationModel_; //!< The method used to relax unilateral contacts + real_t overRelaxationParam_; //!< Parameter specifying the convergence speed for othogonal projection models. real_t relaxationParam_; //!< Parameter specifying underrelaxation of velocity corrections for boundary bodies. real_t maximumPenetration_; size_t numContacts_; @@ -322,6 +325,28 @@ inline real_t HardContactSemiImplicitTimesteppingSolvers::getSpeedLimitFactor() // //================================================================================================= +//************************************************************************************************* +/*!\brief Sets the relaxation parameter for boundary bodies. + * + * \param f The overrelaxation parameter. + * \return void + * + * The overrelaxation parameter \omega is only used when the relaxation model is one of + * - ApproximateInelasticCoulombContactByOrthogonalProjections + * - InelasticCoulombContactByOrthogonalProjections + * + * It is used to control the convergence of the model. Large values show faster convergence, + * but they can also lead to divergence ("exploding" particles). The default values is 1.0. + */ +inline void HardContactSemiImplicitTimesteppingSolvers::setOverRelaxationParameter( real_t omega ) +{ + WALBERLA_ASSERT_GREATER( omega, 0, "Overrelaxation parameter must be positive." ); + + overRelaxationParam_ = omega; +} +//************************************************************************************************* + + //************************************************************************************************* /*!\brief Sets the relaxation parameter for boundary bodies. * diff --git a/src/pe/cr/HCSITS.impl.h b/src/pe/cr/HCSITS.impl.h index ba8fd6aa29d0ec028448b838838406c9d49c50f2..7218b4580519cd623a74976c678a5786eba69184 100644 --- a/src/pe/cr/HCSITS.impl.h +++ b/src/pe/cr/HCSITS.impl.h @@ -97,6 +97,7 @@ inline HardContactSemiImplicitTimesteppingSolvers::HardContactSemiImplicitTimest , maxSubIterations_ ( 20 ) , abortThreshold_ ( real_c(1e-7) ) , relaxationModel_ ( InelasticFrictionlessContact ) + , overRelaxationParam_( real_c(1.0) ) , relaxationParam_ ( real_c(0.75) ) , maximumPenetration_ ( real_c(0.0) ) , numContacts_ ( 0 ) @@ -388,17 +389,17 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d delta_max = relaxApproximateInelasticCoulombContactsByDecoupling( dtinv, contactCache, bodyCache ); break; - // case ApproximateInelasticCoulombContactByOrthogonalProjections: - // delta_max = relaxInelasticCoulombContactsByOrthogonalProjections( dtinv, true, contactCache, bodyCache ); - // break; + case ApproximateInelasticCoulombContactByOrthogonalProjections: + delta_max = relaxInelasticCoulombContactsByOrthogonalProjections( dtinv, true, contactCache, bodyCache ); + break; case InelasticCoulombContactByDecoupling: delta_max = relaxInelasticCoulombContactsByDecoupling( dtinv, contactCache, bodyCache ); break; - // case InelasticCoulombContactByOrthogonalProjections: - // delta_max = relaxInelasticCoulombContactsByOrthogonalProjections( dtinv, false, contactCache, bodyCache ); - // break; + case InelasticCoulombContactByOrthogonalProjections: + delta_max = relaxInelasticCoulombContactsByOrthogonalProjections( dtinv, false, contactCache, bodyCache ); + break; case InelasticGeneralizedMaximumDissipationContact: delta_max = relaxInelasticGeneralizedMaximumDissipationContacts( dtinv, contactCache, bodyCache ); @@ -1049,55 +1050,68 @@ inline real_t HardContactSemiImplicitTimesteppingSolvers::relaxInelasticCoulombC bodyCache.dv_[contactCache.body2_[i]->index_] += contactCache.body2_[i]->getInvMass() * contactCache.p_[i]; bodyCache.dw_[contactCache.body2_[i]->index_] += contactCache.body2_[i]->getInvInertia() * ( contactCache.r2_[i] % contactCache.p_[i] ); - // Calculate the relative contact velocity in the global world frame (if no contact reaction is present at contact i) - Vec3 gdot ( ( bodyCache.v_[contactCache.body1_[i]->index_] + bodyCache.dv_[contactCache.body1_[i]->index_] ) - ( bodyCache.v_[contactCache.body2_[i]->index_] + bodyCache.dv_[contactCache.body2_[i]->index_] ) + ( bodyCache.w_[contactCache.body1_[i]->index_] + bodyCache.dw_[contactCache.body1_[i]->index_] ) % contactCache.r1_[i] - ( bodyCache.w_[contactCache.body2_[i]->index_] + bodyCache.dw_[contactCache.body2_[i]->index_] ) % contactCache.r2_[i] /* + diag_[i] * p */ ); + // Calculate the relative contact velocity in the global world frame (as if no contact reaction was present at contact i) + Vec3 gdot( ( bodyCache.v_[contactCache.body1_[i]->index_] + bodyCache.dv_[contactCache.body1_[i]->index_] ) + - ( bodyCache.v_[contactCache.body2_[i]->index_] + bodyCache.dv_[contactCache.body2_[i]->index_] ) + + ( bodyCache.w_[contactCache.body1_[i]->index_] + bodyCache.dw_[contactCache.body1_[i]->index_] ) % contactCache.r1_[i] + - ( bodyCache.w_[contactCache.body2_[i]->index_] + bodyCache.dw_[contactCache.body2_[i]->index_] ) % contactCache.r2_[i] /* + diag_[i] * p */ ); // Change from the global world frame to the contact frame Mat3 contactframe( contactCache.n_[i], contactCache.t_[i], contactCache.o_[i] ); Vec3 gdot_nto( contactframe.getTranspose() * gdot ); - //real_t gdot_n ( trans( contactCache.n_[i] ) * gdot ); // The component of gdot along the contact normal n - //Vec3 gdot_t ( gdot - gdot_n * contactCache.n_[i] ); // The components of gdot tangential to the contact normal n - //real_t g_n ( gdot_n * dt /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + contactCache.dist_[i] ); // The gap in normal direction + //real_t gdot_n( trans( contactCache.n_[i] ) * gdot ); // The component of gdot along the contact normal n + //Vec3 gdot_t( gdot - gdot_n * contactCache.n_[i] ); // The components of gdot tangential to the contact normal n + //real_t g_n( gdot_n * dt /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + contactCache.dist_[i] ); // The gap in normal direction // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n - gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + contactCache.dist_[i] ) * dtinv; + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) + - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + contactCache.dist_[i] ) * dtinv; + + // Tasora et al., 2011 starts here + // ------------------------------------------------------------------------------------- - const real_t w( 1 ); // w > 0 Vec3 p_cf( contactframe.getTranspose() * contactCache.p_[i] ); if( approximate ) { - // Calculate next iterate (Anitescu/Tasora). - p_cf = p_cf - w * ( contactCache.diag_nto_[i] * p_cf + gdot_nto ); + // Calculate next iterate (Tasora et al., 2011). + p_cf = p_cf - overRelaxationParam_ * ( contactCache.diag_nto_[i] * p_cf + gdot_nto ); } else { // Calculate next iterate (De Saxce/Feng). Vec3 tmp( contactCache.diag_nto_[i] * p_cf + gdot_nto ); tmp[0] += std::sqrt( math::sq( tmp[1] ) + math::sq( tmp[2] ) ) * contactCache.mu_[i]; - p_cf = p_cf - w * tmp; + p_cf = p_cf - overRelaxationParam_ * tmp; } - // Project. + // Project on friction cone (Tasora et al., 2011; Fig. 2; Eq. (46)). real_t flimit( contactCache.mu_[i] * p_cf[0] ); real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); + if( p_cf[0] > 0 && fsq < flimit * flimit ) { - // Unconstrained minimum is in cone leading to a static contact and no projection - // is necessary. + // Unconstrained minimum is INSIDE THE UPPER CONE, + // with slope n*mu + // leading to a static contact and no projection is necessary. } - else if( p_cf[0] < 0 && fsq < p_cf[0] / math::sq( contactCache.mu_[i] ) ) { - // Unconstrained minimum is in dual cone leading to a separating contact where no contact - // reaction is present (the unconstrained minimum is projected to the tip of the cone). - + else if( p_cf[0] < 0 && fsq < math::sq( p_cf[0] / contactCache.mu_[i] ) ) { + // Unconstrained minimum is INSIDE THE LOWER CONE, + // with slope n/mu + // leading to a separating contact where no contact reaction is present, + // i.e. the unconstrained minimum is projected to the tip of the lower cone. p_cf = Vec3(); } else { - // The contact is dynamic. - real_t f( std::sqrt( fsq ) ); - p_cf[0] = ( f * contactCache.mu_[i] + p_cf[0] ) / ( math::sq( contactCache.mu_[i] ) + 1 ); + // Unconstrained minimum is OUTSIDE THE CONE -> Project on cone surface, Eq. (45) in Tasora et al., 2011 + real_t f( std::sqrt( fsq ) ); // gamma_r + p_cf[0] = ( f * contactCache.mu_[i] + p_cf[0] ) / ( math::sq( contactCache.mu_[i] ) + 1 ); // p_cf[0] = gamma_n + real_t factor( contactCache.mu_[i] * p_cf[0] / f ); p_cf[1] *= factor; p_cf[2] *= factor; } + // Tasora et al., 2011 ends here + // ------------------------------------------------------------------------------------- + Vec3 p_wf( contactframe * p_cf ); Vec3 dp( contactCache.p_[i] - p_wf ); delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) ); diff --git a/src/pe/rigidbody/RigidBody.cpp b/src/pe/rigidbody/RigidBody.cpp index 78743f7969e7efbba1bff7d16ffb180cbd383df6..b58bcad567315b4a725ca65722e13df91f9912e4 100644 --- a/src/pe/rigidbody/RigidBody.cpp +++ b/src/pe/rigidbody/RigidBody.cpp @@ -196,7 +196,7 @@ bool RigidBody::checkInvariants() * * The function calculates the global velocity of a point relative to the body's center of mass. */ -const Vec3 RigidBody::velFromBF( real_t px, real_t py, real_t pz ) const +Vec3 RigidBody::velFromBF( real_t px, real_t py, real_t pz ) const { return velFromBF( Vec3( px, py, pz ) ); } @@ -211,7 +211,7 @@ const Vec3 RigidBody::velFromBF( real_t px, real_t py, real_t pz ) const * * The function calculates the global velocity of a point relative to the body's center of mass. */ -const Vec3 RigidBody::velFromBF( const Vec3& rpos ) const +Vec3 RigidBody::velFromBF( const Vec3& rpos ) const { if( !hasSuperBody() ) return v_ + w_ % ( R_ * rpos ); @@ -230,7 +230,7 @@ const Vec3 RigidBody::velFromBF( const Vec3& rpos ) const * * The function calculates the global velocity of a point in global coordinates. */ -const Vec3 RigidBody::velFromWF( real_t px, real_t py, real_t pz ) const +Vec3 RigidBody::velFromWF( real_t px, real_t py, real_t pz ) const { return velFromWF( Vec3( px, py, pz ) ); } @@ -245,7 +245,7 @@ const Vec3 RigidBody::velFromWF( real_t px, real_t py, real_t pz ) const * * The function calculates the global velocity of a point in global coordinates. */ -const Vec3 RigidBody::velFromWF( const Vec3& gpos ) const +Vec3 RigidBody::velFromWF( const Vec3& gpos ) const { if( !hasSuperBody() ) return v_ + w_ % ( gpos - gpos_ ); diff --git a/src/pe/rigidbody/RigidBody.h b/src/pe/rigidbody/RigidBody.h index 1ac988c750b02d95ce5e552f048c941907e2dcd8..440abac642f2e9f018dcd59c519dd931bcc76f9a 100644 --- a/src/pe/rigidbody/RigidBody.h +++ b/src/pe/rigidbody/RigidBody.h @@ -125,14 +125,14 @@ public: inline const Vec3 vectorFromBFtoWF( const Vec3& v ) const; inline const Vec3 pointFromBFtoWF ( real_t px, real_t py, real_t pz ) const; inline const Vec3 pointFromBFtoWF ( const Vec3& rpos ) const; - virtual const Vec3 velFromBF ( real_t px, real_t py, real_t pz ) const; - virtual const Vec3 velFromBF ( const Vec3& rpos ) const; + virtual Vec3 velFromBF ( real_t px, real_t py, real_t pz ) const; + virtual Vec3 velFromBF ( const Vec3& rpos ) const; inline const Vec3 vectorFromWFtoBF( real_t vx, real_t vy, real_t vz ) const; inline const Vec3 vectorFromWFtoBF( const Vec3& v ) const; inline const Vec3 pointFromWFtoBF ( real_t px, real_t py, real_t pz ) const; inline const Vec3 pointFromWFtoBF ( const Vec3& gpos ) const; - virtual const Vec3 velFromWF ( real_t px, real_t py, real_t pz ) const; - virtual const Vec3 velFromWF ( const Vec3& gpos ) const; + virtual Vec3 velFromWF ( real_t px, real_t py, real_t pz ) const; + virtual Vec3 velFromWF ( const Vec3& gpos ) const; inline const Vec3 accFromWF ( real_t px, real_t py, real_t pz ) const; const Vec3 accFromWF ( const Vec3& gpos ) const; diff --git a/src/pe/rigidbody/Squirmer.cpp b/src/pe/rigidbody/Squirmer.cpp index e9d386c59140f8870a0862b195c6afaf5816748e..4a9f714b5108d37aab16ddef18463cb7445da380 100644 --- a/src/pe/rigidbody/Squirmer.cpp +++ b/src/pe/rigidbody/Squirmer.cpp @@ -89,7 +89,7 @@ Squirmer::~Squirmer() * * The function calculates the global velocity of a point relative to the body's center of mass. */ -const Vec3 Squirmer::velFromBF( real_t px, real_t py, real_t pz ) const +Vec3 Squirmer::velFromBF( real_t px, real_t py, real_t pz ) const { return velFromBF( Vec3( px, py, pz ) ); } @@ -104,7 +104,7 @@ const Vec3 Squirmer::velFromBF( real_t px, real_t py, real_t pz ) const * * The function calculates the global velocity of a point relative to the body's center of mass. */ -const Vec3 Squirmer::velFromBF( const Vec3& rpos ) const +Vec3 Squirmer::velFromBF( const Vec3& rpos ) const { return Sphere::velFromBF( rpos ) + getSquirmerVelocity( getRotation() * rpos ); } @@ -120,7 +120,7 @@ const Vec3 Squirmer::velFromBF( const Vec3& rpos ) const * * The function calculates the global velocity of a point in global coordinates. */ -const Vec3 Squirmer::velFromWF( real_t px, real_t py, real_t pz ) const +Vec3 Squirmer::velFromWF( real_t px, real_t py, real_t pz ) const { return velFromWF( Vec3( px, py, pz ) ); } @@ -133,7 +133,7 @@ const Vec3 Squirmer::velFromWF( real_t px, real_t py, real_t pz ) const * \param rpos The relative global coordinate. * \return The local surface velocity of the squirmer. */ -const Vec3 Squirmer::getSquirmerVelocity( const Vec3& rpos ) const +Vec3 Squirmer::getSquirmerVelocity( const Vec3& rpos ) const { const auto rs = rpos.getNormalized(); const auto e = getQuaternion().rotate(Vec3(0.0,0.0,1.0)).getNormalized(); @@ -152,7 +152,7 @@ const Vec3 Squirmer::getSquirmerVelocity( const Vec3& rpos ) const * * The function calculates the global velocity of a point in global coordinates. */ -const Vec3 Squirmer::velFromWF( const Vec3& gpos ) const +Vec3 Squirmer::velFromWF( const Vec3& gpos ) const { return Sphere::velFromWF( gpos ) + getSquirmerVelocity( gpos - getPosition() ); } diff --git a/src/pe/rigidbody/Squirmer.h b/src/pe/rigidbody/Squirmer.h index 0d9514324a7f2f092af4e36a357fa44f5b6c3335..688894cff8d6f2a9622de3e00247b2f96f106e4b 100644 --- a/src/pe/rigidbody/Squirmer.h +++ b/src/pe/rigidbody/Squirmer.h @@ -53,10 +53,10 @@ public: inline real_t getSquirmerVelocity() const; inline real_t getSquirmerBeta() const; - inline const Vec3 velFromBF ( real_t px, real_t py, real_t pz ) const WALBERLA_OVERRIDE; - inline const Vec3 velFromBF ( const Vec3& rpos ) const WALBERLA_OVERRIDE; - inline const Vec3 velFromWF ( real_t px, real_t py, real_t pz ) const WALBERLA_OVERRIDE; - inline const Vec3 velFromWF ( const Vec3& gpos ) const WALBERLA_OVERRIDE; + inline Vec3 velFromBF ( real_t px, real_t py, real_t pz ) const WALBERLA_OVERRIDE; + inline Vec3 velFromBF ( const Vec3& rpos ) const WALBERLA_OVERRIDE; + inline Vec3 velFromWF ( real_t px, real_t py, real_t pz ) const WALBERLA_OVERRIDE; + inline Vec3 velFromWF ( const Vec3& gpos ) const WALBERLA_OVERRIDE; static inline id_t getStaticTypeID() WALBERLA_OVERRIDE; //@} @@ -67,7 +67,7 @@ protected: //**Get functions******************************************************************************* /*!\name Get functions */ //@{ - const Vec3 getSquirmerVelocity( const Vec3& rpos ) const; + Vec3 getSquirmerVelocity( const Vec3& rpos ) const; //@} //**Member variables**************************************************************************** diff --git a/src/postprocessing/all.h b/src/postprocessing/all.h index 90a82d17a76a8288d30b1f4f9e8fd022cea4a44a..1783d5a2951428bedac75ca1d5ef6bd8e99b2c4c 100644 --- a/src/postprocessing/all.h +++ b/src/postprocessing/all.h @@ -22,6 +22,4 @@ #pragma once -#include "MarchingCubes.h" - -#include "sqlite/SQLite.h" \ No newline at end of file +#include "MarchingCubes.h" \ No newline at end of file diff --git a/src/sqlite/CMakeLists.txt b/src/sqlite/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..621760105711d5a82b476cdbef5870524e66b550 --- /dev/null +++ b/src/sqlite/CMakeLists.txt @@ -0,0 +1,9 @@ +################################################################################################### +# +# Module sqlite +# +################################################################################################### + +waLBerla_add_module( DEPENDS core ) + +################################################################################################### diff --git a/src/postprocessing/sqlite/SQLite.cpp b/src/sqlite/SQLite.cpp similarity index 99% rename from src/postprocessing/sqlite/SQLite.cpp rename to src/sqlite/SQLite.cpp index e6efa739fb073c771f440a8389cc83ae1c9fa208..e4729238e71434d4553e735ed6faabf4c7c240c2 100644 --- a/src/postprocessing/sqlite/SQLite.cpp +++ b/src/sqlite/SQLite.cpp @@ -30,7 +30,7 @@ namespace walberla { -namespace postprocessing { +namespace sqlite { SQLiteDB::SQLiteDB( const string & dbFile, const int busyTimeout ) @@ -513,5 +513,5 @@ void storeTimingTreeInSqliteDB ( const string & dbFile, uint_t runId, db.storeTimingTree( runId, tt, timingTreeName ); } -} // namespace postprocessing +} // namespace sqlite } // namespace walberla diff --git a/src/postprocessing/sqlite/SQLite.h b/src/sqlite/SQLite.h similarity index 99% rename from src/postprocessing/sqlite/SQLite.h rename to src/sqlite/SQLite.h index 22e66daf8ef4475a479799e216f2d75843d3f802..84ba2746643d120f41fc8e363f3cdd32c4bde118 100644 --- a/src/postprocessing/sqlite/SQLite.h +++ b/src/sqlite/SQLite.h @@ -35,7 +35,7 @@ struct sqlite3; namespace walberla { -namespace postprocessing { +namespace sqlite { using std::string; using std::map; @@ -126,7 +126,7 @@ void storeTimingTreeInSqliteDB( const string & dbFile, uint_t runId, const WcTim -} // namespace postprocessing +} // namespace sqlite } // namespace walberla diff --git a/src/postprocessing/sqlite/extern/sqlite3.c b/src/sqlite/extern/sqlite3.c similarity index 100% rename from src/postprocessing/sqlite/extern/sqlite3.c rename to src/sqlite/extern/sqlite3.c diff --git a/src/postprocessing/sqlite/extern/sqlite3.h b/src/sqlite/extern/sqlite3.h similarity index 100% rename from src/postprocessing/sqlite/extern/sqlite3.h rename to src/sqlite/extern/sqlite3.h diff --git a/src/timeloop/PerformanceMeter.cpp b/src/timeloop/PerformanceMeter.cpp index 6efb92d969bb67da70230c1453a7c0a4cb62db9b..d7986320312cac1969331a2a8678821d09960873 100644 --- a/src/timeloop/PerformanceMeter.cpp +++ b/src/timeloop/PerformanceMeter.cpp @@ -82,7 +82,7 @@ namespace timeloop { * Behaves like addMeasurement() function above, with n= total number of cells * *******************************************************************************************************************/ - void PerformanceMeter::addMeasurement ( const std::string & name, CountFunction countFunction, + void PerformanceMeter::addMeasurement ( const std::string & name, const CountFunction& countFunction, uint_t countFreq, real_t scaling ) { measurements_.emplace_back( countFunction, name, scaling, countFreq ); @@ -126,7 +126,7 @@ namespace timeloop { *******************************************************************************************************************/ void PerformanceMeter::logResultOnRoot( ) { - if( measurements_.size() == 0) + if( measurements_.empty()) return; std::stringstream ss; @@ -148,7 +148,7 @@ namespace timeloop { *******************************************************************************************************************/ void PerformanceMeter::print( std::ostream & os, int targetRank ) { - if( measurements_.size() == 0) + if( measurements_.empty()) return; std::vector<real_t> totalNrCells; @@ -294,7 +294,7 @@ namespace timeloop { *******************************************************************************************************************/ void PerformanceMeter::reduce ( std::vector<real_t> & reduced, int targetRank ) { - if ( measurements_.size() == 0 ) + if ( measurements_.empty() ) return; reduced.clear(); diff --git a/src/timeloop/PerformanceMeter.h b/src/timeloop/PerformanceMeter.h index 731f968fe7046e95e03f7ac2e7953119fce48f57..0efac6514774670b2efae9f08274be25a3e4ebb0 100644 --- a/src/timeloop/PerformanceMeter.h +++ b/src/timeloop/PerformanceMeter.h @@ -95,7 +95,7 @@ namespace timeloop { //** Measurement Definitions ************************************************************************************* /*! \name Measurement Definitions */ //@{ - void addMeasurement( const std::string & name, CountFunction countFunction, + void addMeasurement( const std::string & name, const CountFunction& countFunction, uint_t countFreq = 0, real_t scaling = 1 ); void addMeasurement( const std::string & name, real_t scaling = 1 ); diff --git a/tests/lbm/CMakeLists.txt b/tests/lbm/CMakeLists.txt index 8f735fb94c96198cf6153050a56c8bb89cdaab53..7ffa9167b581853dbef5ffa4d203923f7e2069e1 100644 --- a/tests/lbm/CMakeLists.txt +++ b/tests/lbm/CMakeLists.txt @@ -77,3 +77,7 @@ waLBerla_python_file_generates(codegen/LbCodeGenerationExample.py LbCodeGenerationExample_UBB.cpp ) waLBerla_compile_test( FILES codegen/LbCodeGenerationExample.py codegen/LbCodeGenerationExample.cpp) +waLBerla_python_file_generates(codegen/FluctuatingMRT.py + FluctuatingMRT_LatticeModel.cpp ) +waLBerla_compile_test( FILES codegen/FluctuatingMRT.py + codegen/FluctuatingMRT.cpp) \ No newline at end of file diff --git a/tests/lbm/codegen/FluctuatingMRT.cpp b/tests/lbm/codegen/FluctuatingMRT.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2b9ccdba11237b4b8a3129e02eaae668e2c24e3c --- /dev/null +++ b/tests/lbm/codegen/FluctuatingMRT.cpp @@ -0,0 +1,119 @@ +//====================================================================================================================== +// +// 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 SrtWithForceField.cpp +//! \author Michael Kuron <mkuron@icp.uni-stuttgart.de> +// +//====================================================================================================================== + +#include "blockforest/all.h" +#include "core/all.h" +#include "domain_decomposition/all.h" +#include "field/all.h" +#include "geometry/all.h" +#include "gui/all.h" +#include "timeloop/all.h" + +#include "lbm/field/PdfField.h" +#include "lbm/field/AddToStorage.h" +#include "lbm/communication/PdfFieldPackInfo.h" +#include "lbm/gui/Connection.h" +#include "lbm/vtk/VTKOutput.h" + +#include "FluctuatingMRT_LatticeModel.h" + + +using namespace walberla; + +typedef lbm::FluctuatingMRT_LatticeModel LatticeModel_T; +typedef LatticeModel_T::Stencil Stencil_T; +typedef LatticeModel_T::CommunicationStencil CommunicationStencil_T; +typedef lbm::PdfField< LatticeModel_T > PdfField_T; + +typedef GhostLayerField< real_t, LatticeModel_T::Stencil::D > VectorField_T; +typedef GhostLayerField< real_t, 1 > ScalarField_T; + +typedef walberla::uint8_t flag_t; +typedef FlagField< flag_t > FlagField_T; + + + +int main( int argc, char ** argv ) +{ + walberla::Environment walberlaEnv( argc, argv ); + + auto blocks = blockforest::createUniformBlockGridFromConfig( walberlaEnv.config() ); + + // read parameters + auto parameters = walberlaEnv.config()->getOneBlock( "Parameters" ); + + const real_t omega = parameters.getParameter< real_t > ( "omega", real_c( 1.4 ) ); + const Vector3<real_t> initialVelocity = parameters.getParameter< Vector3<real_t> >( "initialVelocity", Vector3<real_t>() ); + const uint_t timesteps = parameters.getParameter< uint_t > ( "timesteps", uint_c( 10 ) ); + const real_t temperature = real_t(0.01); + const uint_t seed = uint_t(0); + + const double remainingTimeLoggerFrequency = parameters.getParameter< double >( "remainingTimeLoggerFrequency", 3.0 ); // in seconds + + // create fields + BlockDataID forceFieldId = field::addToStorage<VectorField_T>( blocks, "Force", real_t( 0.0 )); + + LatticeModel_T latticeModel = LatticeModel_T( forceFieldId, omega, omega, seed, temperature, uint_t(0) ); + BlockDataID pdfFieldId = lbm::addPdfFieldToStorage( blocks, "pdf field", latticeModel, initialVelocity, real_t(1) ); + BlockDataID flagFieldId = field::addFlagFieldToStorage< FlagField_T >( blocks, "flag field" ); + + // create and initialize boundary handling + const FlagUID fluidFlagUID( "Fluid" ); + + // create time loop + SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps ); + + // create communication for PdfField + blockforest::communication::UniformBufferedScheme< CommunicationStencil_T > communication( blocks ); + communication.addPackInfo( make_shared< lbm::PdfFieldPackInfo< LatticeModel_T > >( pdfFieldId ) ); + + // add LBM sweep and communication to time loop + timeloop.add() << BeforeFunction( [&](){ latticeModel.time_step = uint32_c(timeloop.getCurrentTimeStep()); }, "set RNG counter" ) + << BeforeFunction( communication, "communication" ) + << Sweep( LatticeModel_T::Sweep( pdfFieldId ), "LB stream & collide" ); + + // LBM stability check + timeloop.addFuncAfterTimeStep( makeSharedFunctor( field::makeStabilityChecker< PdfField_T, FlagField_T >( walberlaEnv.config(), blocks, pdfFieldId, + flagFieldId, fluidFlagUID ) ), + "LBM stability check" ); + + // log remaining time + timeloop.addFuncAfterTimeStep( timing::RemainingTimeLogger( timeloop.getNrOfTimeSteps(), remainingTimeLoggerFrequency ), "remaining time logger" ); + + // add VTK output to time loop + lbm::VTKOutput< LatticeModel_T, FlagField_T >::addToTimeloop( timeloop, blocks, walberlaEnv.config(), pdfFieldId, flagFieldId, fluidFlagUID ); + + // create adaptors, so that the GUI also displays density and velocity + // adaptors are like fields with the difference that they do not store values + // but calculate the values based on other fields ( here the PdfField ) + field::addFieldAdaptor<lbm::Adaptor<LatticeModel_T>::Density> ( blocks, pdfFieldId, "DensityAdaptor" ); + field::addFieldAdaptor<lbm::Adaptor<LatticeModel_T>::VelocityVector>( blocks, pdfFieldId, "VelocityAdaptor" ); + + if( parameters.getParameter<bool>( "useGui", false ) ) + { + GUI gui ( timeloop, blocks, argc, argv ); + lbm::connectToGui<LatticeModel_T> ( gui ); + gui.run(); + } + else + timeloop.run(); + + return EXIT_SUCCESS; +} diff --git a/tests/lbm/codegen/FluctuatingMRT.py b/tests/lbm/codegen/FluctuatingMRT.py new file mode 100644 index 0000000000000000000000000000000000000000..b996fe43c53dbf3fff48452cc02b3b684eaedd15 --- /dev/null +++ b/tests/lbm/codegen/FluctuatingMRT.py @@ -0,0 +1,22 @@ +import sympy as sp +import pystencils as ps +from lbmpy.creationfunctions import create_lb_collision_rule +from pystencils_walberla import CodeGeneration +from lbmpy_walberla import generate_lattice_model + +with CodeGeneration() as ctx: + omega_shear, omega_bulk = sp.symbols("omega_shear, omega_bulk") + temperature = sp.symbols("temperature") + force_field, vel_field = ps.fields("force(3), velocity(3): [3D]", layout='fzyx') + + collision_rule = create_lb_collision_rule( + stencil='D3Q19', compressible=True, fluctuating={ + 'temperature' : temperature, + 'block_offsets' : 'walberla', + }, + method='mrt3', relaxation_rates=[omega_shear, omega_bulk], + force_model='guo', force=force_field.center_vector, + optimization={'cse_global': True} + ) + + generate_lattice_model(ctx, 'FluctuatingMRT_LatticeModel', collision_rule) diff --git a/tests/mesa_pd/CMakeLists.txt b/tests/mesa_pd/CMakeLists.txt index cdaf984c4d6bd7cbdcbcc56e740566f9562aeaa1..5555ac9cfdae03f92baf272ed9c59564d6aed5d5 100644 --- a/tests/mesa_pd/CMakeLists.txt +++ b/tests/mesa_pd/CMakeLists.txt @@ -111,6 +111,9 @@ waLBerla_execute_test( NAME MESA_PD_Kernel_GenerateAnalyticContacts PROCESSES waLBerla_compile_test( NAME MESA_PD_Kernel_GenerateLinkedCells FILES kernel/GenerateLinkedCells.cpp DEPENDS core ) waLBerla_execute_test( NAME MESA_PD_Kernel_GenerateLinkedCells ) +waLBerla_compile_test( NAME MESA_PD_Kernel_HCSITSKernels FILES kernel/HCSITSKernels.cpp DEPENDS core ) +waLBerla_execute_test( NAME MESA_PD_Kernel_HCSITSKernels ) + waLBerla_compile_test( NAME MESA_PD_Kernel_HeatConduction FILES kernel/HeatConduction.cpp DEPENDS core ) waLBerla_execute_test( NAME MESA_PD_Kernel_HeatConduction ) @@ -167,6 +170,9 @@ waLBerla_execute_test( NAME MESA_PD_MPI_ReduceContactHistory PROCESSES 8 ) waLBerla_compile_test( NAME MESA_PD_MPI_ReduceProperty FILES mpi/ReduceProperty.cpp DEPENDS core ) waLBerla_execute_test( NAME MESA_PD_MPI_ReduceProperty PROCESSES 8 ) +waLBerla_compile_test( NAME MESA_PD_MPI_VelocityCorrectionNotification FILES mpi/VelocityCorrectionNotification.cpp DEPENDS core ) +waLBerla_execute_test( NAME MESA_PD_MPI_VelocityCorrectionNotification PROCESSES 8) + waLBerla_compile_test( NAME MESA_PD_Sorting FILES Sorting.cpp DEPENDS core ) waLBerla_execute_test( NAME MESA_PD_Sorting ) diff --git a/tests/mesa_pd/kernel/HCSITSKernels.cpp b/tests/mesa_pd/kernel/HCSITSKernels.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2977c398d114905ad0623fd4057b6ba069179837 --- /dev/null +++ b/tests/mesa_pd/kernel/HCSITSKernels.cpp @@ -0,0 +1,485 @@ +//====================================================================================================================== +// +// 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 HCSITSKernels.cpp +//! \author Tobias Leemann <tobias.leemann@fau.de> +// +//====================================================================================================================== + + +/** Test Collision Detection and Insertion of contacts into the contact storage */ + +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/ContactStorage.h> +#include <mesa_pd/data/ParticleStorage.h> +#include <mesa_pd/data/ShapeStorage.h> +#include <mesa_pd/kernel/DetectAndStoreContacts.h> +#include <mesa_pd/domain/InfiniteDomain.h> +#include <mesa_pd/data/ContactAccessor.h> +#include <mesa_pd/kernel/ParticleSelector.h> +//HCSITS Kernels + +#include "mesa_pd/kernel/InitContactsForHCSITS.h" +#include "mesa_pd/kernel/InitParticlesForHCSITS.h" +#include "mesa_pd/kernel/HCSITSRelaxationStep.h" +#include "mesa_pd/kernel/IntegrateParticlesHCSITS.h" + +// Communication kernels +#include "mesa_pd/mpi/ReduceProperty.h" +#include "mesa_pd/mpi/BroadcastProperty.h" + +#include "mesa_pd/mpi/notifications/VelocityUpdateNotification.h" +#include "mesa_pd/mpi/notifications/VelocityCorrectionNotification.h" + +#include <mesa_pd/data/ParticleAccessor.h> +#include <core/Environment.h> +#include <core/logging/Logging.h> + +#include <iostream> + +namespace walberla { + +using namespace walberla::mesa_pd; + + class ParticleAccessorWithShape : public data::ParticleAccessor + { + public: + ParticleAccessorWithShape(std::shared_ptr<data::ParticleStorage>& ps, std::shared_ptr<data::ShapeStorage>& ss) + : ParticleAccessor(ps) + , ss_(ss) + {} + + const real_t& getMass(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)]->getMass();} + const real_t& getInvMass(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInvMass();} + + const Mat3& getInertia(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInertiaBF();} + const Mat3& getInvInertia(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInvInertiaBF();} + + data::BaseShape* getShape(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)].get();} + private: + std::shared_ptr<data::ShapeStorage> ss_; + }; + + template<typename PStorage, typename CStorage, typename PAccessor, typename CAccessor> + class TestHCSITSKernel { + public: + TestHCSITSKernel(PStorage &ps_, CStorage &cs_, PAccessor &pa_, CAccessor &ca_) : ps(ps_), cs(cs_), pa(pa_), ca(ca_), + erp(real_t(1.0)), model(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticFrictionlessContact), contactThreshold(0), globalAcc(0) {} + + void operator()(real_t dt){ + // Perform Collision detection (call kernel, that stores contacts into cs) + kernel::DetectAndStoreContacts detectAndStore(cs); + cs.clear(); + + domain::InfiniteDomain domain; + collision_detection::AnalyticContactDetection acd; + acd.getContactThreshold() = contactThreshold; + ps.forEachParticlePairHalf(false, kernel::ExcludeInfiniteInfinite(), pa, detectAndStore, pa, domain, acd); + + // Create Kernels + kernel::InitContactsForHCSITS initContacts(1); + initContacts.setFriction(0,0,real_t(0.2)); + initContacts.setErp(real_t(erp)); + + kernel::InitParticlesForHCSITS initParticles; + initParticles.setGlobalAcceleration(globalAcc); + + kernel::HCSITSRelaxationStep relaxationStep; + relaxationStep.setRelaxationModel(model); + relaxationStep.setCor(real_t(0.6)); // Only effective for PGSM + + kernel::IntegrateParticlesHCSITS integration; + + mesa_pd::mpi::ReduceProperty reductionKernel; + mesa_pd::mpi::BroadcastProperty broadcastKernel; + + // Run the HCSITS loop + cs.forEachContact(false, kernel::SelectAll(), ca, initContacts, ca, pa); + ps.forEachParticle(false, kernel::SelectAll(), pa, initParticles, pa, dt); + + VelocityUpdateNotification::Parameters::relaxationParam = real_t(1.0); + reductionKernel.operator()<VelocityCorrectionNotification>(ps); + broadcastKernel.operator()<VelocityUpdateNotification>(ps); + + VelocityUpdateNotification::Parameters::relaxationParam = real_t(0.8); + for(int i = 0; i < 10; i++){ + cs.forEachContact(false, kernel::SelectAll(), ca, relaxationStep, ca, pa, dt); + reductionKernel.operator()<VelocityCorrectionNotification>(ps); + broadcastKernel.operator()<VelocityUpdateNotification>(ps); + } + ps.forEachParticle(false, kernel::SelectAll(), pa, integration, pa, dt); + } + + private: + PStorage &ps; + CStorage &cs; + PAccessor &pa; + CAccessor &ca; + + public: + real_t erp; + kernel::HCSITSRelaxationStep::RelaxationModel model; + real_t contactThreshold; + Vec3 globalAcc; + }; + + +void normalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel model) +{ + //init data structures + auto ps = std::make_shared<data::ParticleStorage>(100); + auto cs = std::make_shared<data::ContactStorage>(100); + auto ss = std::make_shared<data::ShapeStorage>(); + ParticleAccessorWithShape paccessor(ps, ss); + data::ContactAccessor caccessor(cs); + auto density = real_t(7.874); + auto radius = real_t(1.1); + + //Geometries for sphere and half space. + auto smallSphere = ss->create<data::Sphere>( radius ); + auto halfSpace = ss->create<data::HalfSpace>(Vec3(0,0,1)); + + ss->shapes[halfSpace]->updateMassAndInertia( density ); + ss->shapes[smallSphere]->updateMassAndInertia( density ); + + // Create four slightly overlapping spheres in a row (located at x=0,2) + auto p = ps->create(); + p->getPositionRef() = Vec3(real_t(0), real_t(0), real_t(0)); + p->getShapeIDRef() = smallSphere; + p->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank(); + p->getLinearVelocityRef() = Vec3(real_t(5), real_t(5), real_t(6)); + p->getTypeRef() = 0; + //WALBERLA_LOG_INFO(paccessor.ParticleAccessorWithShape::getInvMass(0)); + + + auto p2 = ps->create(); + p2->getPositionRef() = Vec3(real_t(5), real_t(5), real_t(5)); + p2->getShapeIDRef() = halfSpace; + p2->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank(); + p2->getTypeRef() = 0; + data::particle_flags::set(p2->getFlagsRef(), data::particle_flags::FIXED); + data::particle_flags::set(p2->getFlagsRef(), data::particle_flags::GLOBAL); + + TestHCSITSKernel<data::ParticleStorage, data::ContactStorage, ParticleAccessorWithShape, data::ContactAccessor> testHCSITS(*ps, *cs, paccessor, caccessor); + testHCSITS.model = model; + + WALBERLA_LOG_INFO(paccessor.getInvMass(0)) + WALBERLA_LOG_INFO(paccessor.getInvMass(1)) + + // plane at 5,5,5 + // radius 1.1 + p->setPosition( Vec3(5,5,6) ); + p->setLinearVelocity( Vec3(0,0,0) ); + testHCSITS( real_c( real_t(1.0) ) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getPosition() , Vec3(5,5,real_t(6.1)) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getLinearVelocity(), Vec3(0,0,real_t(0.1)) ); + WALBERLA_LOG_INFO(p->getPosition()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); + + p->setPosition( Vec3(5,5,6) ); + p->setLinearVelocity( Vec3(0,0,0) ); + testHCSITS.erp = real_t(0.5) ; + testHCSITS( real_t(1.0) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getPosition() , Vec3(5,5,real_t(6.05)) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getLinearVelocity(), Vec3(0,0,real_t(0.05)) ); + WALBERLA_LOG_INFO(p->getPosition()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); + + p->setPosition( Vec3(5,5,6) ); + p->setLinearVelocity( Vec3(0,0,0) ); + testHCSITS.erp = real_t(0.0) ; + testHCSITS( real_t(1.0) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getPosition() , Vec3(5,5,6) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getLinearVelocity(), Vec3(0,0,0) ); + WALBERLA_LOG_INFO(p->getPosition()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); + + p->setPosition( Vec3(5,5,6) ); + p->setLinearVelocity( Vec3(0,0,-1) ); + testHCSITS.erp = real_t(1.0) ; + testHCSITS( real_t(1.0) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getPosition() , Vec3(5,5,real_t(6.1)) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getLinearVelocity(), Vec3(0,0,real_t(0.1)) ); + WALBERLA_LOG_INFO(p->getPosition()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); + + p->setPosition( Vec3(5,5,6) ); + p->setLinearVelocity( Vec3(0,0,-1) ); + testHCSITS.erp = real_t(0.5) ; + testHCSITS( real_t(1.0) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getPosition() , Vec3(5,5,real_t(6.05)) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getLinearVelocity(), Vec3(0,0,real_t(0.05)) ); + WALBERLA_LOG_INFO(p->getPosition()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); + + p->setPosition( Vec3(5,5,6) ); + p->setLinearVelocity( Vec3(0,0,-1) ); + testHCSITS.erp = real_t(0.0) ; + testHCSITS( real_t(1.0) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getPosition() , Vec3(5,5,6) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getLinearVelocity(), Vec3(0,0,0) ); + WALBERLA_LOG_INFO(p->getPosition()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); + + // No collision + p->setPosition( Vec3(5,5,real_t(6.2)) ); + p->setLinearVelocity( Vec3(0,0,real_t(-0.2)) ); + testHCSITS.erp = real_t(1.0) ; + testHCSITS( real_t(1.0) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getPosition() , Vec3(5,5,real_t(6.0)) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getLinearVelocity(), Vec3(0,0,real_t(-0.2)) ); + + testHCSITS.contactThreshold = real_t(1.0); + p->setPosition( Vec3(5,5,real_t(6.2)) ); + p->setLinearVelocity( Vec3(0,0,real_t(-0.2)) ); + testHCSITS.erp = real_t(1.0) ; + testHCSITS( real_t(1.0) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getPosition() , Vec3(5,5,real_t(6.1)) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getLinearVelocity(), Vec3(0,0,real_t(-0.1)) ); + + p->setPosition( Vec3(5,5,real_t(6.1)) ); + p->setLinearVelocity( Vec3(0,0,real_t(-0.1)) ); + testHCSITS.erp = real_t(1.0) ; + testHCSITS( real_t(1.0) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getPosition() , Vec3(5,5,real_t(6.1)) ); + WALBERLA_CHECK_FLOAT_EQUAL( p->getLinearVelocity(), Vec3(0,0,real_t(0)) ); + WALBERLA_LOG_INFO(p->getPosition()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); +} + + +/**Check hard contact constraints on two overlapping, colliding spheres + * Works only for the solvers that really archieve seperation after a single + * timestep. Use SphereSeperationTest to check for seperation after multiple + * timesteps. + * @param model The collision model to use. + * */ +void SphereSphereTest(kernel::HCSITSRelaxationStep::RelaxationModel model){ + + //init data structures + auto ps = std::make_shared<data::ParticleStorage>(100); + auto cs = std::make_shared<data::ContactStorage>(100); + auto ss = std::make_shared<data::ShapeStorage>(); + ParticleAccessorWithShape paccessor(ps, ss); + data::ContactAccessor caccessor(cs); + auto density = real_t(7.874); + auto radius = real_t(1.1); + + auto smallSphere = ss->create<data::Sphere>( radius ); + ss->shapes[smallSphere]->updateMassAndInertia( density ); + + auto dt = real_t(1); + + // Create two slightly overlapping spheres in a row (located at x=0,2) + auto p = ps->create(); + p->getPositionRef() = Vec3(real_t(0), real_t(0), real_t(0)); + p->getShapeIDRef() = smallSphere; + p->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank(); + p->getLinearVelocityRef() = Vec3(real_t(1), real_t(0), real_t(0)); + p->getTypeRef() = 0; + auto p2 = ps->create(); + p2->getPositionRef() = Vec3(real_t(2), real_t(0), real_t(0)); + p2->getShapeIDRef() = smallSphere; + p2->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank(); + p2->getLinearVelocityRef() = Vec3(real_t(-1), real_t(0), real_t(0)); + p2->getTypeRef() = 0; + TestHCSITSKernel<data::ParticleStorage, data::ContactStorage, ParticleAccessorWithShape, data::ContactAccessor> testHCSITS(*ps, *cs, paccessor, caccessor); + testHCSITS.model = model; + testHCSITS(dt); + + WALBERLA_CHECK_FLOAT_EQUAL(p->getPosition(), Vec3(real_t(-0.1),0,0)); + WALBERLA_CHECK_FLOAT_EQUAL(p->getLinearVelocity(), Vec3(real_t(-0.1),0,0)); + WALBERLA_CHECK_FLOAT_EQUAL(p->getAngularVelocity(), Vec3(0,0,0)); + WALBERLA_CHECK_FLOAT_EQUAL(p2->getPosition(), Vec3(real_t(2.1),0,0)); + WALBERLA_CHECK_FLOAT_EQUAL(p2->getLinearVelocity(), Vec3(real_t(0.1),0,0)) + WALBERLA_CHECK_FLOAT_EQUAL(p2->getAngularVelocity(), Vec3(0,0,0)); + + WALBERLA_LOG_INFO(p->getPosition()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); + WALBERLA_LOG_INFO(p2->getPosition()); + WALBERLA_LOG_INFO(p2->getLinearVelocity()); +} + +/** + * Create two overlapping spheres with opposing velocities, that are in contact. + * Assert that the collision is resolved after a certain (10) number of timesteps. + * @param model The collision model to use. + */ +void SphereSeperationTest(kernel::HCSITSRelaxationStep::RelaxationModel model){ + + //init data structures + auto ps = std::make_shared<data::ParticleStorage>(100); + auto cs = std::make_shared<data::ContactStorage>(100); + auto ss = std::make_shared<data::ShapeStorage>(); + ParticleAccessorWithShape paccessor(ps, ss); + data::ContactAccessor caccessor(cs); + auto density = real_t(7.874); + auto radius = real_t(1.1); + + auto smallSphere = ss->create<data::Sphere>( radius ); + + ss->shapes[smallSphere]->updateMassAndInertia( density ); + auto dt = real_t(0.2); + + // Create two slightly overlapping spheres in a row (located at x=0,2) + auto p = ps->create(); + p->getPositionRef() = Vec3(real_t(0), real_t(0), real_t(0)); + p->getShapeIDRef() = smallSphere; + p->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank(); + p->getLinearVelocityRef() = Vec3(real_t(1), real_t(0), real_t(0)); + p->getTypeRef() = 0; + auto p2 = ps->create(); + p2->getPositionRef() = Vec3(real_t(2.0), real_t(0), real_t(0)); + p2->getShapeIDRef() = smallSphere; + p2->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank(); + p2->getLinearVelocityRef() = Vec3(real_t(-1), real_t(0), real_t(0)); + p2->getTypeRef() = 0; + TestHCSITSKernel<data::ParticleStorage, data::ContactStorage, ParticleAccessorWithShape, data::ContactAccessor> testHCSITS(*ps, *cs, paccessor, caccessor); + + int solveCount = 0; + testHCSITS.model = model; + + // Number of allowed iterations + int maxIter = 10; + while(p2->getPosition()[0]-p->getPosition()[0] < 2.2){ + testHCSITS(dt); + WALBERLA_LOG_INFO(p->getPosition()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); + WALBERLA_LOG_INFO(p2->getPosition()); + WALBERLA_LOG_INFO(p2->getLinearVelocity()); + solveCount ++; + if(solveCount==maxIter){ + WALBERLA_CHECK(false, "Seperation did not occur after " << maxIter << " Iterations performed."); + } + } + WALBERLA_LOG_INFO("Seperation achieved after " << solveCount << " iterations."); +} + +/** + * Create a sphere that slides on a plane with only linear velocity. It is + * put into a spin by the frictional reactions, until it rolls with no slip. + * Assert that the sphere rolls with all slip resolved and at the physically correct + * speed after a certain number of timesteps. Use only with solvers that obey the coulomb friction law. + * @param model The collision model to use. + */ +void SlidingSphereFrictionalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel model){ + + //init data structures + auto ps = std::make_shared<data::ParticleStorage>(100); + auto cs = std::make_shared<data::ContactStorage>(100); + auto ss = std::make_shared<data::ShapeStorage>(); + ParticleAccessorWithShape paccessor(ps, ss); + data::ContactAccessor caccessor(cs); + auto density = real_t(1); + auto radius = real_t(1); + + auto smallSphere = ss->create<data::Sphere>( radius ); + auto halfSpace = ss->create<data::HalfSpace>(Vec3(0,0,1)); + ss->shapes[smallSphere]->updateMassAndInertia( density ); + ss->shapes[halfSpace]->updateMassAndInertia( density ); + auto dt = real_t(0.002); + + // Create a spheres (located at x=0, height = 1) + auto p = ps->create(); + p->getPositionRef() = Vec3(real_t(0), real_t(0), real_t(1)); + p->getShapeIDRef() = smallSphere; + p->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank(); + p->getLinearVelocityRef() = Vec3(real_t(5), real_t(0), real_t(0)); + p->getTypeRef() = 0; + + auto p2 = ps->create(); + p2->getPositionRef() = Vec3(real_t(0), real_t(0), real_t(0)); + p2->getShapeIDRef() = halfSpace; + p2->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank(); + p2->getTypeRef() = 0; + data::particle_flags::set(p2->getFlagsRef(), data::particle_flags::FIXED); + data::particle_flags::set(p2->getFlagsRef(), data::particle_flags::GLOBAL); + + TestHCSITSKernel<data::ParticleStorage, data::ContactStorage, ParticleAccessorWithShape, data::ContactAccessor> testHCSITS(*ps, *cs, paccessor, caccessor); + testHCSITS.model = model; + testHCSITS.globalAcc = Vec3(0,0,-10); + int solveCount = 0; + + + // Number of allowed iterations + int maxIter = 500; + while(!walberla::floatIsEqual(p->getAngularVelocity()[1],p->getLinearVelocity()[0], real_t(0.002))){ + testHCSITS(dt); + if(solveCount % 50 == 0){ + WALBERLA_LOG_INFO(p->getAngularVelocity()); + WALBERLA_LOG_INFO(p->getLinearVelocity()); + } + solveCount ++; + if(solveCount==maxIter){ + WALBERLA_CHECK(false, "End of slip did not occur after " << maxIter << " Iterations performed."); + } + } + WALBERLA_LOG_INFO("Rolling with no slip achieved after " << solveCount << " iterations."); + + // Check if the value obtained values equal the physically correct values + // (which can be determined by newtons equation to be 25/7). + WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(real_t(25.0/7.0), p->getAngularVelocity()[1], real_t(0.01), "Angular velocity is not physically correct"); + WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(real_t(25.0/7.0), p->getLinearVelocity()[0], real_t(0.01), "Linear velocity is not physically correct"); +} + +int main( int argc, char ** argv ) +{ + Environment env(argc, argv); + WALBERLA_UNUSED(env); + + walberla::mpi::MPIManager::instance()->useWorldComm(); + + WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - START ***"); + + + WALBERLA_LOG_INFO_ON_ROOT("InelasticFrictionlessContact"); + normalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticFrictionlessContact); + SphereSphereTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticFrictionlessContact); + + WALBERLA_LOG_INFO_ON_ROOT("ApproximateInelasticCoulombContactByDecoupling"); + normalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::ApproximateInelasticCoulombContactByDecoupling); + SphereSphereTest(kernel::HCSITSRelaxationStep::RelaxationModel::ApproximateInelasticCoulombContactByDecoupling); + SlidingSphereFrictionalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::ApproximateInelasticCoulombContactByDecoupling); + + WALBERLA_LOG_INFO_ON_ROOT("InelasticCoulombContactByDecoupling"); + normalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticCoulombContactByDecoupling); + SphereSphereTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticCoulombContactByDecoupling); + SlidingSphereFrictionalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticCoulombContactByDecoupling); + + WALBERLA_LOG_INFO_ON_ROOT("InelasticGeneralizedMaximumDissipationContact"); + normalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticGeneralizedMaximumDissipationContact); + SphereSphereTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticGeneralizedMaximumDissipationContact); + SlidingSphereFrictionalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticGeneralizedMaximumDissipationContact); + + WALBERLA_LOG_INFO_ON_ROOT("InelasticCoulombContactByOrthogonalProjections"); + SphereSeperationTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticCoulombContactByOrthogonalProjections); + SlidingSphereFrictionalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticCoulombContactByOrthogonalProjections); + + WALBERLA_LOG_INFO_ON_ROOT("ApproximateInelasticCoulombContactByOrthogonalProjections"); + SphereSeperationTest(kernel::HCSITSRelaxationStep::RelaxationModel::ApproximateInelasticCoulombContactByOrthogonalProjections); + SlidingSphereFrictionalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::ApproximateInelasticCoulombContactByOrthogonalProjections); + + WALBERLA_LOG_INFO_ON_ROOT("InelasticProjectedGaussSeidel"); + SphereSeperationTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticProjectedGaussSeidel); + SlidingSphereFrictionalReactionTest(kernel::HCSITSRelaxationStep::RelaxationModel::InelasticProjectedGaussSeidel); + return EXIT_SUCCESS; +} + +} //namespace walberla + +int main( int argc, char ** argv ) +{ + return walberla::main(argc, argv); +} diff --git a/tests/mesa_pd/mpi/VelocityCorrectionNotification.cpp b/tests/mesa_pd/mpi/VelocityCorrectionNotification.cpp new file mode 100644 index 0000000000000000000000000000000000000000..79cd6351519295c35e4ab62dd7106537fdc3752e --- /dev/null +++ b/tests/mesa_pd/mpi/VelocityCorrectionNotification.cpp @@ -0,0 +1,135 @@ +//====================================================================================================================== +// +// 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 VelocityCorrectionNotification.cpp +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + + +#include <blockforest/BlockForest.h> +#include <blockforest/Initialization.h> +#include <core/Environment.h> +#include <core/logging/Logging.h> + +#include <mesa_pd/domain/BlockForestDomain.h> + +#include <mesa_pd/data/ParticleStorage.h> +#include <mesa_pd/data/ShapeStorage.h> + +#include <mesa_pd/mpi/SyncNextNeighbors.h> +#include <mesa_pd/mpi/BroadcastProperty.h> +#include <mesa_pd/mpi/ReduceProperty.h> + +#include <mesa_pd/mpi/notifications/VelocityCorrectionNotification.h> +#include <mesa_pd/mpi/notifications/VelocityUpdateNotification.h> + +#include <iostream> + +namespace walberla { + +int main( int argc, char ** argv ) +{ + + using namespace mesa_pd; + + Environment env(argc, argv); + WALBERLA_UNUSED(env); + int np = walberla::mpi::MPIManager::instance()->numProcesses(); + + // create blocks + shared_ptr< blockforest::BlockForest > forest = blockforest::createBlockForest( + math::AABB(-10,-10,-10, 10, 10, 10), + Vector3<uint_t>(2,2,2), + Vector3<bool>(false, false, false)); + + domain::BlockForestDomain domain(forest); + + //init data structures + data::ParticleStorage ps(10); + + //initialize particle + const auto linVel = Vec3(1,2,3); + const auto angVel = Vec3(-1,-2,-3); + + Vec3 pt(real_t(0.1), real_t(0.1),real_t(0.1)); + + for (auto& iBlk : *forest) + { + if(iBlk.getAABB().contains(pt)) { + auto p = ps.create(); + p->getPositionRef() = pt; + p->getInteractionRadiusRef() = real_t(1.0); + p->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank(); + p->getTypeRef() = 0; + p->getLinearVelocityRef() = linVel; + p->getAngularVelocityRef() = angVel; + WALBERLA_LOG_INFO("Particle created."); + } + } + + // Sync (create ghost particles on the other processes) + mesa_pd::mpi::SyncNextNeighbors snn; + snn(ps, domain); + + auto relax_param = real_t(0.8); + VelocityUpdateNotification::Parameters::relaxationParam = relax_param; + mesa_pd::mpi::ReduceProperty reductionKernel; + mesa_pd::mpi::BroadcastProperty broadcastKernel; + + WALBERLA_LOG_INFO("Particle-Storage size: " << ps.size()); + + // Reduce dv + // dv per process + auto dvprocess = Vec3(real_t(0.1),real_t(0.1),real_t(0.1)); + auto dwprocess = Vec3(real_t(0.05),real_t(0.1),real_t(0.15)); + ps.setDv(0, dvprocess); + ps.setDw(0, dwprocess); + reductionKernel.operator()<VelocityCorrectionNotification>(ps); + + for (auto& iBlk : *forest) + { + if(iBlk.getAABB().contains(pt)) { + WALBERLA_CHECK_FLOAT_EQUAL(ps.getLinearVelocity(0), linVel); + WALBERLA_CHECK_FLOAT_EQUAL(ps.getAngularVelocity(0), angVel); + WALBERLA_CHECK_FLOAT_EQUAL(ps.getDv(0), real_t(np) * dvprocess); + WALBERLA_CHECK_FLOAT_EQUAL(ps.getDw(0), real_t(np) * dwprocess); + } + } + + broadcastKernel.operator()<VelocityUpdateNotification>(ps); + + + // Broadcast v + reductionKernel.operator()<VelocityCorrectionNotification>(ps); + if(np > 1){ + WALBERLA_CHECK_FLOAT_EQUAL(ps.getLinearVelocity(0), linVel + relax_param * real_t(np) * dvprocess); + WALBERLA_CHECK_FLOAT_EQUAL(ps.getAngularVelocity(0), angVel + relax_param * real_t(np) * dwprocess); + WALBERLA_CHECK_FLOAT_EQUAL(ps.getDv(0), Vec3()); + WALBERLA_CHECK_FLOAT_EQUAL(ps.getDw(0), Vec3()); + }else{ + WALBERLA_CHECK_FLOAT_EQUAL(ps.getLinearVelocity(0) + ps.getDv(0), linVel + real_t(np) * dvprocess); + WALBERLA_CHECK_FLOAT_EQUAL(ps.getAngularVelocity(0) + ps.getDw(0), angVel + real_t(np) * dwprocess); + } + + return EXIT_SUCCESS; +} + +} //namespace walberla + +int main( int argc, char ** argv ) +{ + return walberla::main(argc, argv); +} diff --git a/tests/mesh/PeVTKMeshWriterTest.cpp b/tests/mesh/PeVTKMeshWriterTest.cpp index 589f46642b6ca902f52cc4e379b2f32200dbc245..7e7b26e13360f9917b15701bd6b55e1ae8709fb3 100644 --- a/tests/mesh/PeVTKMeshWriterTest.cpp +++ b/tests/mesh/PeVTKMeshWriterTest.cpp @@ -41,7 +41,6 @@ #include <core/waLBerlaBuildInfo.h> #include <core/math/Random.h> #include <core/math/Utility.h> -#include <postprocessing/sqlite/SQLite.h> #include <vtk/VTKOutput.h> #include <functional> diff --git a/tests/postprocessing/CMakeLists.txt b/tests/postprocessing/CMakeLists.txt index 549bea2f00f57755e81dfaf4fc8d787296aa575e..ffe99cd52776ffa370638541c3a22625b834a7fc 100644 --- a/tests/postprocessing/CMakeLists.txt +++ b/tests/postprocessing/CMakeLists.txt @@ -6,7 +6,4 @@ waLBerla_compile_test( FILES SphereTriangulate.cpp ) -waLBerla_execute_test( NAME SphereTriangulate ) - -waLBerla_compile_test( FILES SQLiteTest.cpp ) -waLBerla_execute_test( NAME SQLiteTest ) +waLBerla_execute_test( NAME SphereTriangulate ) \ No newline at end of file diff --git a/tests/sqlite/CMakeLists.txt b/tests/sqlite/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..21513d96b056a643907df3eb4738d50c815796ff --- /dev/null +++ b/tests/sqlite/CMakeLists.txt @@ -0,0 +1,8 @@ +################################################################################################### +# +# Tests for sqlite module +# +################################################################################################### + +waLBerla_compile_test( FILES SQLiteTest.cpp ) +waLBerla_execute_test( NAME SQLiteTest ) \ No newline at end of file diff --git a/tests/postprocessing/SQLiteTest.cpp b/tests/sqlite/SQLiteTest.cpp similarity index 88% rename from tests/postprocessing/SQLiteTest.cpp rename to tests/sqlite/SQLiteTest.cpp index 547bd12e9b2c3a07548a68ca7001c508fb5f8885..b65ad060362d9cc9d33880c88ea2466195bdaf0a 100644 --- a/tests/postprocessing/SQLiteTest.cpp +++ b/tests/sqlite/SQLiteTest.cpp @@ -22,7 +22,7 @@ #include "core/debug/TestSubsystem.h" #include "core/mpi/Environment.h" -#include "postprocessing/sqlite/SQLite.h" +#include "sqlite/SQLite.h" int main( int argc, char ** argv ) @@ -40,7 +40,7 @@ int main( int argc, char ** argv ) strColumns["property2"] = "value2"; strColumns["property3"] = "value3"; intColumns["i"] = int(i); - walberla::postprocessing::storeRunInSqliteDB( "dbFile.sqlite", intColumns, strColumns ); + walberla::sqlite::storeRunInSqliteDB( "dbFile.sqlite", intColumns, strColumns ); } for( int i=0; i< 100; ++i ) @@ -49,7 +49,7 @@ int main( int argc, char ** argv ) strColumns["property2"] = "value2"; strColumns["property3"] = "value3"; largeColumns["i"] = 4294967297 + i; - walberla::postprocessing::storeRunInSqliteDB( "dbFile.sqlite", largeColumns, strColumns ); + walberla::sqlite::storeRunInSqliteDB( "dbFile.sqlite", largeColumns, strColumns ); } return 0; diff --git a/utilities/conda/walberla/meta.yaml b/utilities/conda/walberla/meta.yaml index 869f578214879f354d01732d7eca7c86cefd0524..2148479bbb815ac49d584f8a4d79995adaed5a94 100644 --- a/utilities/conda/walberla/meta.yaml +++ b/utilities/conda/walberla/meta.yaml @@ -15,13 +15,13 @@ requirements: host: - python - boost - - mpich2 [linux] + - mpich [linux] - openmesh run: - python - boost - numpy - - mpich2 [linux] + - mpich [linux] - openmesh source: path: ../../.. diff --git a/utilities/filterCompileCommands.py b/utilities/filterCompileCommands.py index 3b0a08e7f932c224ce36d3845425937fadc08057..cb4813231736c7e40eb6c0f7eb2f664e0ebba522 100755 --- a/utilities/filterCompileCommands.py +++ b/utilities/filterCompileCommands.py @@ -6,6 +6,13 @@ import sys def compileCommandSelector(x): return not (("extern" in x["file"]) or ("tests" in x["file"])) +def removePrecompiler(x): + pos = x.find("clang++") + if (pos != -1): + return x[pos:] + else: + return x + if __name__ == "__main__": if (len(sys.argv) != 2): print("usage: ./filterCompileCommands.py compile_commands.json") @@ -21,6 +28,8 @@ if __name__ == "__main__": print("compile commands read: {}".format(len(cc))) cc_filtered = list( filter(compileCommandSelector, cc) ) + for x in cc_filtered: + x["command"] = removePrecompiler(x["command"]) print("compile commands filtered: {}".format(len(cc_filtered)))