diff --git a/.gitignore b/.gitignore
index f198a0bfc0..3a9cb7f5ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ build*/
.idea/
.project
.settings
+.vscode
# Generated documentation
doxygen_doc/
diff --git a/cmake/modules/mamico.cmake b/cmake/modules/mamico.cmake
index 5d4a6ed549..6939b2c601 100644
--- a/cmake/modules/mamico.cmake
+++ b/cmake/modules/mamico.cmake
@@ -1,18 +1,15 @@
option(MAMICO_COUPLING "Couple with MaMiCo" OFF)
if (MAMICO_COUPLING)
- message(STATUS "MaMiCo coupling enabled. ls1 mardyn will compile as library. No executable will be created.")
- set(MAMICO_COMPILE_DEFINITIONS MAMICO_COUPLING MDDim3)
- option(MAMICO_ENABLE_FPIC "Enable -fPIC flag for MaMiCo python bindings" OFF)
- set(MAMICO_SRC_DIR CACHE PATH "Root directory of the MaMiCo codebase")
+ message(STATUS "MaMiCo coupling enabled. ls1 mardyn will compile as library. No executable will be created.")
+ set(MAMICO_COMPILE_DEFINITIONS MAMICO_COUPLING MDDim3)
+ option(MAMICO_ENABLE_FPIC "Enable -fPIC flag for MaMiCo python bindings" OFF)
+ set(MAMICO_SRC_DIR CACHE PATH "Root directory of the MaMiCo codebase")
if(NOT MAMICO_SRC_DIR)
- message(FATAL_ERROR "MaMiCo source directory not specified.")
- endif()
- if(ENABLE_MPI)
- set(MAMICO_MPI_DEFINITIONS MDCoupledParallel TarchParallel)
- endif()
- if(MAMICO_ENABLE_FPIC)
- set(MAMICO_COMPILE_OPTIONS "${MAMICO_COMPILE_OPTIONS} -fPIC")
- endif()
+ message(FATAL_ERROR "MaMiCo source directory not specified.")
+ endif()
+ if(ENABLE_MPI)
+ set(MAMICO_MPI_DEFINITIONS MDCoupledParallel TarchParallel)
+ endif()
else()
message(STATUS "MaMiCo coupling disabled.")
endif()
\ No newline at end of file
diff --git a/examples/all-options.xml b/examples/all-options.xml
index 389f25f141..855bad744b 100644
--- a/examples/all-options.xml
+++ b/examples/all-options.xml
@@ -184,7 +184,8 @@
0.0 0.0 0.0
-
+
+ true
@@ -196,6 +197,16 @@
False
5
False
+
+
+ reflective
+ outflow
+ periodic
+
+
+ reflectiveoutflowreflective
+
+
+
+
+ 2.2
+
+
+ 1.0e+10
+
+
+
+
+
diff --git a/examples/simple-boundary-test/simple_checkpoint.inp b/examples/simple-boundary-test/simple_checkpoint.inp
new file mode 100644
index 0000000000..1da67c4a9c
--- /dev/null
+++ b/examples/simple-boundary-test/simple_checkpoint.inp
@@ -0,0 +1,21 @@
+mardyn trunk 20120726
+currentTime 0
+Length 10 10 10
+Temperature 1.1
+NumberOfComponents 1
+1 0 0 0 0
+0 0 0 1 1 1 0
+0 0 0
+1e+10
+NumberOfMolecules 10
+MoleculeFormat IRV
+0 7.5 5 5 -3 0 0
+1 2.5 5 5 0 0 0
+2 2.5 7.5 5 0 0 0
+3 2.5 2.5 5 0 0 0
+4 2.5 5 2.5 0 0 0
+5 2.5 5 7.5 0 0 0
+6 2.5 7.5 7.5 0 0 0
+7 2.5 7.5 2.5 0 0 0
+8 2.5 2.5 7.5 0 0 0.69
+9 2.5 2.5 2.5 0 -1.1 -1
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f204718945..d2a8cd8894 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -8,16 +8,16 @@ if(NOT ENABLE_UNIT_TESTS)
list(FILTER MY_SRC EXCLUDE REGEX "/tests/")
endif()
-# if mpi is not enabled, remove the uneeded source files
+# if mpi is not enabled, remove the unneeded source files
if(NOT ENABLE_MPI)
# duplicate the list
set(MY_SRC_BACK ${MY_SRC})
# exclude everything from parallel
list(FILTER MY_SRC EXCLUDE REGEX "/parallel/")
- # but include DomainDecompBase* and LoadCalc*
+ # but include DomainDecompBase*, LoadCalc* and Boundary utilities
list(FILTER MY_SRC_BACK INCLUDE REGEX "/parallel/")
- list(FILTER MY_SRC_BACK INCLUDE REGEX "DomainDecompBase|LoadCalc|Zonal|ForceHelper")
+ list(FILTER MY_SRC_BACK INCLUDE REGEX "boundaries/|DomainDecompBase|LoadCalc|Zonal|ForceHelper")
list(APPEND MY_SRC ${MY_SRC_BACK})
else()
if(NOT ENABLE_ALLLBL)
@@ -54,7 +54,9 @@ if (MAMICO_COUPLING)
TARGET_COMPILE_DEFINITIONS(MarDyn PUBLIC
${MAMICO_COMPILE_DEFINITIONS} ${MAMICO_MPI_DEFINITIONS}
)
- TARGET_COMPILE_OPTIONS(MarDyn PUBLIC ${MAMICO_COMPILE_OPTIONS})
+ if(MAMICO_ENABLE_FPIC)
+ SET_PROPERTY(TARGET MarDyn PROPERTY POSITION_INDEPENDENT_CODE ON)
+ endif()
else()
ADD_EXECUTABLE(MarDyn
${MY_SRC}
diff --git a/src/Domain.cpp b/src/Domain.cpp
index 00a3a3bf8b..80657e2f36 100644
--- a/src/Domain.cpp
+++ b/src/Domain.cpp
@@ -145,9 +145,9 @@ double Domain::getGlobalPressure()
return globalTemperature * _globalRho + _globalRho * getAverageGlobalVirial()/3.;
}
-double Domain::getAverageGlobalVirial() { return _globalVirial/_globalNumMolecules; }
+double Domain::getAverageGlobalVirial() const { return _globalVirial/_globalNumMolecules; }
-double Domain::getAverageGlobalUpot() { return getGlobalUpot()/_globalNumMolecules; }
+double Domain::getAverageGlobalUpot() const { return getGlobalUpot()/_globalNumMolecules; }
double Domain::getGlobalUpot() const { return _globalUpot; }
Comp2Param& Domain::getComp2Params(){
@@ -791,9 +791,9 @@ void Domain::updateMaxMoleculeID(ParticleContainer* particleContainer, DomainDec
#endif
}
-double Domain::getglobalRho(){ return _globalRho;}
+double Domain::getglobalRho() const { return _globalRho;}
-void Domain::setglobalRho(double grho){ _globalRho = grho;}
+void Domain::setglobalRho(double grho) { _globalRho = grho;}
unsigned long Domain::getglobalRotDOF()
{
@@ -832,10 +832,10 @@ double Domain::cv()
//! methods implemented by Stefan Becker
// the following two methods are used by the MmspdWriter (writing the output file in a format used by MegaMol)
-double Domain::getSigma(unsigned cid, unsigned nthSigma){
+double Domain::getSigma(unsigned cid, unsigned nthSigma) const {
return _simulation.getEnsemble()->getComponent(cid)->getSigma(nthSigma);
}
-unsigned Domain::getNumberOfComponents(){
+unsigned Domain::getNumberOfComponents() const {
return _simulation.getEnsemble()->getComponents()->size();
}
@@ -863,10 +863,10 @@ void Domain::submitDU(unsigned /*cid*/, double DU, double* r)
void Domain::setLocalUpotCompSpecific(double UpotCspec){_localUpotCspecif = UpotCspec;}
- double Domain::getLocalUpotCompSpecific(){return _localUpotCspecif;}
+ double Domain::getLocalUpotCompSpecific() const {return _localUpotCspecif;}
-double Domain::getAverageGlobalUpotCSpec() {
+double Domain::getAverageGlobalUpotCSpec() const {
Log::global_log->debug() << "number of fluid molecules = " << getNumFluidMolecules() << "\n";
return _globalUpotCspecif / getNumFluidMolecules();
}
@@ -876,7 +876,7 @@ void Domain::setNumFluidComponents(unsigned nc){_numFluidComponent = nc;}
unsigned Domain::getNumFluidComponents(){return _numFluidComponent;}
-unsigned long Domain::getNumFluidMolecules(){
+unsigned long Domain::getNumFluidMolecules() const {
unsigned long numFluidMolecules = 0;
for(unsigned i = 0; i < _numFluidComponent; i++){
Component& ci=*(global_simulation->getEnsemble()->getComponent(i));
diff --git a/src/Domain.h b/src/Domain.h
index faabdca061..cabc179377 100644
--- a/src/Domain.h
+++ b/src/Domain.h
@@ -140,7 +140,7 @@ class Domain {
unsigned getNumFluidComponents();
//! @brief get the fluid and fluid-solid potential of the local process
- double getLocalUpotCompSpecific();
+ double getLocalUpotCompSpecific() const;
//! @brief set the virial of the local process
void setLocalVirial(double Virial);
@@ -214,28 +214,30 @@ class Domain {
//!
//! Before this method is called, it has to be sure that the
//! global potential has been calculated (method calculateGlobalValues)
- double getAverageGlobalUpot();
+ double getAverageGlobalUpot() const;
double getGlobalUpot() const;
//! by Stefan Becker: return the average global potential of the fluid-fluid and fluid-solid interaction (but NOT solid-solid interaction)
- double getAverageGlobalUpotCSpec();
+ double getAverageGlobalUpotCSpec() const;
//! @brief get the global kinetic energy
//!
- //! Before this method is called, it has to be sure that the
- //! global energies has been calculated (method calculateGlobalValues)
- double getGlobalUkinTrans() { return 0.5*_globalsummv2; }
- double getGlobalUkinRot() { return 0.5*_globalsumIw2; }
+ //! Before this method is called, the user has to be sure that the
+ //! global energy (rot and trans) has been calculated via calculateGlobalValues()
+ //! Since variables _globalsummv2 and _globalsumIw2 store the sum of m_i*(v_i^2).
+ //! Therefore, the constant factor 0.5 has to be applied to yield the kinetic energies
+ double getGlobalUkinTrans() const { return 0.5*_globalsummv2; }
+ double getGlobalUkinRot() const { return 0.5*_globalsumIw2; }
//! by Stefan Becker: determine and return the totel number of fluid molecules
//! this method assumes all molecules with a component-ID less than _numFluidComponent to be fluid molecules
- unsigned long getNumFluidMolecules();
+ unsigned long getNumFluidMolecules() const;
//! @brief get the global average virial per particle
//!
//! Before this method is called, it has to be sure that the
//! global virial has been calculated (method calculateGlobalValues)
- double getAverageGlobalVirial();
+ double getAverageGlobalVirial() const;
//! @brief sets _localSummv2 to the given value
void setLocalSummv2(double summv2, int thermostat);
@@ -250,7 +252,7 @@ class Domain {
}
//! @brief get globalRho
- double getglobalRho();
+ double getglobalRho() const;
//! @brief set globalRho
void setglobalRho(double grho);
@@ -386,9 +388,9 @@ class Domain {
// by Stefan Becker
/* method returning the sigma parameter of a component
=> needed in the output of the MmspdWriter (specifying the particles' radii in a movie) */
- double getSigma(unsigned cid, unsigned nthSigma);
+ double getSigma(unsigned cid, unsigned nthSigma) const;
// needed for the MmspdWriter (MegaMol)
- unsigned getNumberOfComponents();
+ unsigned getNumberOfComponents() const;
void setUpotCorr(double upotcorr){ _UpotCorr = upotcorr; }
void setVirialCorr(double virialcorr){ _VirialCorr = virialcorr; }
diff --git a/src/Simulation.cpp b/src/Simulation.cpp
index ac459db6f8..78b29b737b 100644
--- a/src/Simulation.cpp
+++ b/src/Simulation.cpp
@@ -86,6 +86,8 @@
#include
#endif
+#include "parallel/boundaries/BoundaryUtils.h"
+
Simulation* global_simulation;
Simulation::Simulation()
@@ -424,6 +426,36 @@ void Simulation::readXML(XMLfileUnits& xmlconfig) {
}
_lastTraversalTimeHistory.setCapacity(timerForLoadAveragingLength);
+ if(xmlconfig.changecurrentnode("boundaries")) {
+ std::string xBoundaryFromFile, yBoundaryFromFile, zBoundaryFromFile;
+ xmlconfig.getNodeValue("x", xBoundaryFromFile);
+ xmlconfig.getNodeValue("y", yBoundaryFromFile);
+ xmlconfig.getNodeValue("z", zBoundaryFromFile);
+ BoundaryUtils::BoundaryType xBoundary = BoundaryUtils::convertStringToBoundary(xBoundaryFromFile);
+ BoundaryUtils::BoundaryType yBoundary = BoundaryUtils::convertStringToBoundary(yBoundaryFromFile);
+ BoundaryUtils::BoundaryType zBoundary = BoundaryUtils::convertStringToBoundary(zBoundaryFromFile);
+ _domainDecomposition->setGlobalBoundaryType(DimensionUtils::DimensionType::POSX, xBoundary);
+ _domainDecomposition->setGlobalBoundaryType(DimensionUtils::DimensionType::NEGX, xBoundary);
+ _domainDecomposition->setGlobalBoundaryType(DimensionUtils::DimensionType::POSY, yBoundary);
+ _domainDecomposition->setGlobalBoundaryType(DimensionUtils::DimensionType::NEGY, yBoundary);
+ _domainDecomposition->setGlobalBoundaryType(DimensionUtils::DimensionType::POSZ, zBoundary);
+ _domainDecomposition->setGlobalBoundaryType(DimensionUtils::DimensionType::NEGZ, zBoundary);
+ if (_domainDecomposition->hasGlobalInvalidBoundary()) {
+ Log::global_log->error() << "Invalid boundary type! Please check the config file" << std::endl;
+ exit(1);
+ }
+ if(_overlappingP2P && _domainDecomposition->hasGlobalNonPeriodicBoundary()) {
+ Log::global_log->info() << "Non-periodic boundaries not supported with overlappingP2P enabled! Exiting..." << std::endl;
+ exit(1);
+ }
+ Log::global_log->info() << "Boundary conditions: x - " << BoundaryUtils::convertBoundaryToString(xBoundary)
+ << " y - " << BoundaryUtils::convertBoundaryToString(yBoundary)
+ << " z - " << BoundaryUtils::convertBoundaryToString(zBoundary) << std::endl;
+ // go over all local boundaries, determine which are global
+ _domainDecomposition->setLocalBoundariesFromGlobal(_domain, _ensemble);
+ xmlconfig.changecurrentnode("..");
+ }
+
xmlconfig.changecurrentnode("..");
}
else {
@@ -1014,13 +1046,14 @@ void Simulation::preSimLoopSteps()
global_simulation->timers()->setOutputString("SIMULATION_UPDATE_CACHES", "Cache update took:");
global_simulation->timers()->setOutputString("COMMUNICATION_PARTNER_INIT_SEND", "initSend() took:");
global_simulation->timers()->setOutputString("COMMUNICATION_PARTNER_TEST_RECV", "testRecv() took:");
+ global_simulation->timers()->setOutputString("SIMULATION_BOUNDARY_TREATMENT", "Enforcing boundary conditions took:");
// all timers except the ioTimer measure inside the main loop
//global_simulation->timers()->getTimer("SIMULATION_LOOP")->set_sync(true);
//global_simulation->timers()->setSyncTimer("SIMULATION_LOOP", true);
#ifdef WITH_PAPI
- const char *papi_event_list[] = { "PAPI_TOT_CYC", "PAPI_TOT_INS" /*, "PAPI_VEC_DP", "PAPI_L2_DCM", "PAPI_L2_ICM", "PAPI_L1_ICM", "PAPI_DP_OPS", "PAPI_VEC_INS" }; */
+ const char *papi_event_list[] = { "PAPI_TOT_CYC", "PAPI_TOT_INS" };/*, "PAPI_VEC_DP", "PAPI_L2_DCM", "PAPI_L2_ICM", "PAPI_L1_ICM", "PAPI_DP_OPS", "PAPI_VEC_INS" }; */
int num_papi_events = sizeof(papi_event_list) / sizeof(papi_event_list[0]);
global_simulation->timers()->getTimer("SIMULATION_LOOP")->add_papi_counters(num_papi_events, (char**) papi_event_list);
#endif
@@ -1077,9 +1110,13 @@ void Simulation::simulateOneTimestep()
global_simulation->timers()->stop(plugin->getPluginName());
}
- _ensemble->beforeEventNewTimestep(_moleculeContainer, _domainDecomposition, _simstep);
-
- _integrator->eventNewTimestep(_moleculeContainer, _domain);
+ _ensemble->beforeEventNewTimestep(_moleculeContainer, _domainDecomposition, _simstep);
+
+ global_simulation->timers()->start("SIMULATION_BOUNDARY_TREATMENT");
+ _domainDecomposition->processBoundaryConditions(_moleculeContainer, _integrator->getTimestepLength());
+ global_simulation->timers()->stop("SIMULATION_BOUNDARY_TREATMENT");
+
+ _integrator->eventNewTimestep(_moleculeContainer, _domain);
// beforeForces Plugin Call
Log::global_log -> debug() << "[BEFORE FORCES] Performing BeforeForces plugin call" << std::endl;
@@ -1402,6 +1439,10 @@ void Simulation::updateParticleContainerAndDecomposition(double lastTraversalTim
_domain);
global_simulation->timers()->stop("SIMULATION_MPI_OMP_COMMUNICATION");
+ global_simulation->timers()->start("SIMULATION_BOUNDARY_TREATMENT");
+ _domainDecomposition->removeNonPeriodicHalos(_moleculeContainer);
+ global_simulation->timers()->stop("SIMULATION_BOUNDARY_TREATMENT");
+
// The cache of the molecules must be updated/build after the exchange process,
// as the cache itself isn't transferred
global_simulation->timers()->start("SIMULATION_UPDATE_CACHES");
diff --git a/src/io/ResultWriter.cpp b/src/io/ResultWriter.cpp
index 9212a2b021..4a8d7ff899 100644
--- a/src/io/ResultWriter.cpp
+++ b/src/io/ResultWriter.cpp
@@ -67,9 +67,8 @@ void ResultWriter::endStep(ParticleContainer *particleContainer, DomainDecompBas
// Writing of cavities now handled by CavityWriter
- unsigned long globalNumMolecules = domain->getglobalNumMolecules(true, particleContainer, domainDecomp);
- double cv = domain->cv();
- double ekin = domain->getGlobalUkinTrans()+domain->getGlobalUkinRot();
+ const unsigned long globalNumMolecules = domain->getglobalNumMolecules(true, particleContainer, domainDecomp);
+ const double ekin = domain->getGlobalUkinTrans()+domain->getGlobalUkinRot();
_U_pot_acc->addEntry(domain->getGlobalUpot());
_U_kin_acc->addEntry(ekin);
@@ -91,7 +90,7 @@ void ResultWriter::endStep(ParticleContainer *particleContainer, DomainDecompBas
printOutput(_p_acc->getAverage());
printOutput(domain->getGlobalBetaTrans());
printOutput(domain->getGlobalBetaRot());
- printOutput(cv);
+ printOutput(domain->cv());
printOutput(globalNumMolecules);
resultStream << std::endl;
resultStream.close();
diff --git a/src/io/TimerProfiler.cpp b/src/io/TimerProfiler.cpp
index 520702e376..c8ec9d6803 100644
--- a/src/io/TimerProfiler.cpp
+++ b/src/io/TimerProfiler.cpp
@@ -201,6 +201,7 @@ void TimerProfiler::readInitialTimersFromFile(std::string fileName){
std::make_tuple("QUICKSCHED", std::vector{"SIMULATION_LOOP"}, true),
#endif
std::make_tuple("SIMULATION_PER_STEP_IO", std::vector{"SIMULATION_LOOP"}, true),
+ std::make_tuple("SIMULATION_BOUNDARY_TREATMENT", std::vector{"SIMULATION_LOOP"}, true),
std::make_tuple("SIMULATION_IO", std::vector{"SIMULATION"}, true),
std::make_tuple("SIMULATION_UPDATE_CONTAINER", std::vector{"SIMULATION_DECOMPOSITION"}, true),
std::make_tuple("SIMULATION_MPI_OMP_COMMUNICATION", std::vector{"SIMULATION_DECOMPOSITION"}, true),
diff --git a/src/parallel/DomainDecompBase.cpp b/src/parallel/DomainDecompBase.cpp
index 6a3499e5a1..dd9b2af653 100644
--- a/src/parallel/DomainDecompBase.cpp
+++ b/src/parallel/DomainDecompBase.cpp
@@ -15,6 +15,8 @@
#include "utils/MPI_Info_object.h"
#endif
+#include "boundaries/BoundaryUtils.h"
+
DomainDecompBase::DomainDecompBase() : _rank(0), _numProcs(1) {
}
@@ -24,6 +26,32 @@ DomainDecompBase::~DomainDecompBase() {
void DomainDecompBase::readXML(XMLfileUnits& /* xmlconfig */) {
}
+void DomainDecompBase::setGlobalBoundaryType(DimensionUtils::DimensionType dimension, BoundaryUtils::BoundaryType boundary) {
+ _boundaryHandler.setGlobalWallType(dimension, boundary);
+}
+
+void DomainDecompBase::setLocalBoundariesFromGlobal(Domain* domain, Ensemble* ensemble) {
+ //find which walls to consider
+ double startRegion[3], endRegion[3];
+ getBoundingBoxMinMax(domain, startRegion, endRegion);
+ const double* globStartRegion = ensemble->domain()->rmin();
+ const double* globEndRegion = ensemble->domain()->rmax();
+
+ _boundaryHandler.setLocalRegion(startRegion, endRegion);
+ _boundaryHandler.setGlobalRegion(globStartRegion, globEndRegion);
+ _boundaryHandler.updateGlobalWallLookupTable();
+}
+
+void DomainDecompBase::processBoundaryConditions(ParticleContainer* moleculeContainer, double timestepLength) {
+ if(hasGlobalNonPeriodicBoundary())
+ _boundaryHandler.processGlobalWallLeavingParticles(moleculeContainer, timestepLength);
+}
+
+void DomainDecompBase::removeNonPeriodicHalos(ParticleContainer* moleculeContainer) {
+ if(hasGlobalNonPeriodicBoundary())
+ _boundaryHandler.removeNonPeriodicHalos(moleculeContainer);
+}
+
void DomainDecompBase::addLeavingMolecules(std::vector& invalidMolecules,
ParticleContainer* moleculeContainer) {
for (auto& molecule : invalidMolecules) {
@@ -286,6 +314,11 @@ void DomainDecompBase::handleDomainLeavingParticlesDirect(const HaloRegion& halo
}
void DomainDecompBase::populateHaloLayerWithCopies(unsigned dim, ParticleContainer* moleculeContainer) const {
+
+ //reflecting and outflow boundaries do not expect halo particles
+ if(_boundaryHandler.getGlobalWallType(dim) != BoundaryUtils::BoundaryType::PERIODIC)
+ return;
+
double shiftMagnitude = moleculeContainer->getBoundingBoxMax(dim) - moleculeContainer->getBoundingBoxMin(dim);
// molecules that have crossed the lower boundary need a positive shift
diff --git a/src/parallel/DomainDecompBase.h b/src/parallel/DomainDecompBase.h
index b5fc83d871..b29ee19b1e 100644
--- a/src/parallel/DomainDecompBase.h
+++ b/src/parallel/DomainDecompBase.h
@@ -16,7 +16,8 @@
#include "molecules/MoleculeForwardDeclaration.h"
#include "utils/Logger.h" // is this used?
-
+#include "boundaries/BoundaryHandler.h"
+#include "ensemble/EnsembleBase.h"
class Component;
class Domain;
@@ -289,6 +290,23 @@ class DomainDecompBase: public MemoryProfilable {
virtual void printCommunicationPartners(std::string filename) const {};
+ /* Set the global boundary type for the _boundaryHandler object. */
+ void setGlobalBoundaryType(DimensionUtils::DimensionType dimension, BoundaryUtils::BoundaryType boundary);
+
+ /* Find which boundaries of a subdomain are actually global boundaries, and update _boundaryHandler. */
+ void setLocalBoundariesFromGlobal(Domain* domain, Ensemble* ensemble);
+
+ /* Check if any of the global boundaries are invalid. */
+ bool hasGlobalInvalidBoundary() const { return _boundaryHandler.hasGlobalInvalidBoundary();}
+
+ bool hasGlobalNonPeriodicBoundary() const { return _boundaryHandler.hasGlobalNonPeriodicBoundary();}
+
+ /* Processes leaving particles according to the boundary coundition of the wall the particles would be leaving. */
+ void processBoundaryConditions(ParticleContainer* moleculeContainer, double timestepLength);
+
+ /* Delete all halo particles outside global boundas that are non-periodic. */
+ void removeNonPeriodicHalos(ParticleContainer* moleculeContainer);
+
protected:
void addLeavingMolecules(std::vector& invalidMolecules, ParticleContainer* moleculeContainer);
@@ -338,6 +356,8 @@ class DomainDecompBase: public MemoryProfilable {
//! total number of processes in the simulation
int _numProcs;
+ BoundaryHandler _boundaryHandler;
+
private:
CollectiveCommBase _collCommBase;
int _sendLeavingAndCopiesSeparately = 0;
diff --git a/src/parallel/GeneralDomainDecomposition.cpp b/src/parallel/GeneralDomainDecomposition.cpp
index ec656fbefe..46d14c60b1 100644
--- a/src/parallel/GeneralDomainDecomposition.cpp
+++ b/src/parallel/GeneralDomainDecomposition.cpp
@@ -134,6 +134,8 @@ void GeneralDomainDecomposition::balanceAndExchange(double lastTraversalTime, bo
DomainDecompMPIBase::exchangeMoleculesMPI(moleculeContainer, domain, HALO_COPIES);
}
}
+ _boundaryHandler.setLocalRegion(_boxMin.data(),_boxMax.data());
+ _boundaryHandler.updateGlobalWallLookupTable();
}
++_steps;
}
diff --git a/src/parallel/KDDecomposition.cpp b/src/parallel/KDDecomposition.cpp
index 814cbd6fad..7a44ecd319 100644
--- a/src/parallel/KDDecomposition.cpp
+++ b/src/parallel/KDDecomposition.cpp
@@ -331,6 +331,11 @@ void KDDecomposition::balanceAndExchange(double lastTraversalTime, bool forceReb
initCommunicationPartners(_cutoffRadius, domain, moleculeContainer);
DomainDecompMPIBase::exchangeMoleculesMPI(moleculeContainer, domain, HALO_COPIES, true /*doHaloPositionCheck*/, removeRecvDuplicates);
+
+ double startRegion[3], endRegion[3];
+ getBoundingBoxMinMax(domain, startRegion, endRegion);
+ _boundaryHandler.setLocalRegion(startRegion,endRegion);
+ _boundaryHandler.updateGlobalWallLookupTable();
}
}
diff --git a/src/parallel/boundaries/BoundaryHandler.cpp b/src/parallel/boundaries/BoundaryHandler.cpp
new file mode 100644
index 0000000000..2b02d6029e
--- /dev/null
+++ b/src/parallel/boundaries/BoundaryHandler.cpp
@@ -0,0 +1,177 @@
+/*
+ * BoundaryHandler.cpp
+ *
+ * Created on: 24 March 2023
+ * Author: amartyads
+ */
+
+#include "BoundaryHandler.h"
+
+#include "integrators/Integrator.h"
+
+#include "utils/Logger.h"
+#include "utils/Math.h"
+#include "utils/mardyn_assert.h"
+
+#include
+#include // for ostringstream
+
+BoundaryUtils::BoundaryType BoundaryHandler::getGlobalWallType(DimensionUtils::DimensionType dimension) const {
+ return _boundaries.at(dimension);
+}
+
+BoundaryUtils::BoundaryType BoundaryHandler::getGlobalWallType(int dimension) const {
+ return getGlobalWallType(DimensionUtils::convertLS1DimIndexToEnumPositive(dimension));
+}
+
+void BoundaryHandler::setGlobalWallType(DimensionUtils::DimensionType dimension, BoundaryUtils::BoundaryType value) {
+ if (dimension != DimensionUtils::DimensionType::ERROR)
+ _boundaries[dimension] = value;
+ else {
+ std::ostringstream error_message;
+ error_message << "DimensionType::ERROR received in BoundaryHandler::setGlobalWallType!!" << std::endl;
+ MARDYN_EXIT(error_message.str());
+ }
+}
+
+void BoundaryHandler::setGlobalRegion(const double *start, const double *end) {
+ for (short int i = 0; i < 3; i++) {
+ _globalRegionStart[i] = start[i];
+ _globalRegionEnd[i] = end[i];
+ }
+}
+
+void BoundaryHandler::setLocalRegion(const double *start, const double *end) {
+ for (short int i = 0; i < 3; i++) {
+ _localRegionStart[i] = start[i];
+ _localRegionEnd[i] = end[i];
+ }
+}
+
+void BoundaryHandler::updateGlobalWallLookupTable() {
+ _isGlobalWall[DimensionUtils::DimensionType::POSX] = isNearRel(_localRegionEnd[0], _globalRegionEnd[0]);
+ _isGlobalWall[DimensionUtils::DimensionType::NEGX] = isNearRel(_localRegionStart[0], _globalRegionStart[0]);
+ _isGlobalWall[DimensionUtils::DimensionType::POSY] = isNearRel(_localRegionEnd[1], _globalRegionEnd[1]);
+ _isGlobalWall[DimensionUtils::DimensionType::NEGY] = isNearRel(_localRegionStart[1], _globalRegionStart[1]);
+ _isGlobalWall[DimensionUtils::DimensionType::POSZ] = isNearRel(_localRegionEnd[2], _globalRegionEnd[2]);
+ _isGlobalWall[DimensionUtils::DimensionType::NEGZ] = isNearRel(_localRegionStart[2], _globalRegionStart[2]);
+}
+
+bool BoundaryHandler::hasGlobalInvalidBoundary() const {
+ return std::any_of(_boundaries.begin(), _boundaries.end(), [](const auto &keyVal) {
+ const auto [dim, boundaryType] = keyVal;
+ return boundaryType == BoundaryUtils::BoundaryType::ERROR;
+ });
+}
+
+bool BoundaryHandler::hasGlobalNonPeriodicBoundary() const {
+ return std::any_of(_boundaries.begin(), _boundaries.end(), [](const auto &keyVal) {
+ const auto [dim, boundaryType] = keyVal;
+ return boundaryType != BoundaryUtils::BoundaryType::PERIODIC;
+ });
+}
+
+bool BoundaryHandler::isGlobalWall(DimensionUtils::DimensionType dimension) const {
+ return _isGlobalWall.at(dimension);
+}
+
+bool BoundaryHandler::isGlobalWall(int dimension) const {
+ return isGlobalWall(DimensionUtils::convertLS1DimIndexToEnumPositive(dimension));
+}
+
+void BoundaryHandler::processGlobalWallLeavingParticles(ParticleContainer *moleculeContainer,
+ double timestepLength) const {
+ const auto cutoff = moleculeContainer->getCutoff();
+ for (auto const [currentDim, currentWallIsGlobalWall] : _isGlobalWall) {
+ if (!currentWallIsGlobalWall)
+ continue;
+
+ switch (getGlobalWallType(currentDim)) {
+ case BoundaryUtils::BoundaryType::PERIODIC:
+ // nothing changes from normal ls1 behaviour, so leaving particles not touched by BoundaryHandler and
+ // are processed by DomainDecompBase::handleDomainLeavingParticles()
+ break;
+
+ case BoundaryUtils::BoundaryType::OUTFLOW:
+ [[fallthrough]];
+ case BoundaryUtils::BoundaryType::REFLECTING: {
+ // create region by using getInnerRegionSlab()
+ const auto [curWallRegionBegin, curWallRegionEnd] =
+ RegionUtils::getInnerRegionSlab(_localRegionStart, _localRegionEnd, currentDim, cutoff);
+ // grab an iterator from the converted coords
+ const auto particlesInRegion = moleculeContainer->regionIterator(
+ curWallRegionBegin.data(), curWallRegionEnd.data(), ParticleIterator::ONLY_INNER_AND_BOUNDARY);
+
+ // iterate through all molecules
+ for (auto moleculeIter = particlesInRegion; moleculeIter.isValid(); ++moleculeIter) {
+ // Calculate the change in velocity, which the leapfrog method will
+ // apply in the next velocity update to the dimension of interest.
+ const int currentDimInt = DimensionUtils::convertEnumToLS1DimIndex(currentDim);
+ const double halfTimestep = .5 * timestepLength;
+ const double halfTimestepByMass = halfTimestep / moleculeIter->mass();
+ const double force = moleculeIter->F(currentDimInt);
+ const double nextStepVelAdjustment = halfTimestepByMass * force;
+
+ // check if the molecule would leave the bounds
+ if (RegionUtils::isMoleculeLeaving(*moleculeIter, curWallRegionBegin, curWallRegionEnd, currentDim,
+ timestepLength, nextStepVelAdjustment)) {
+ if (getGlobalWallType(currentDim) == BoundaryUtils::BoundaryType::REFLECTING) {
+ const double currentVel = moleculeIter->v(currentDimInt);
+ // change the velocity in the dimension of interest such that when
+ // the leapfrog integrator adds nextStepVelAdjustment in the next
+ // velocity update, the final result ends up being the intended,
+ // reversed velocity: -(currentVel+nextStepVelAdjustment)
+ moleculeIter->setv(currentDimInt,
+ -currentVel - nextStepVelAdjustment - nextStepVelAdjustment);
+ } else { // outflow, delete the particle if it would leave
+ moleculeContainer->deleteMolecule(moleculeIter, false);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ std::ostringstream error_message;
+ error_message << "BoundaryType::ERROR received in BoundaryHandler::processGlobalWallLeavingParticles!" << std::endl;
+ MARDYN_EXIT(error_message.str());
+ }
+ }
+}
+
+void BoundaryHandler::removeNonPeriodicHalos(ParticleContainer *moleculeContainer) const {
+ // get halo lengths in each dimension
+ const std::array haloWidths = {moleculeContainer->getHaloWidthForDimension(0),
+ moleculeContainer->getHaloWidthForDimension(1),
+ moleculeContainer->getHaloWidthForDimension(2)};
+ for (auto const [currentDim, currentWallIsGlobalWall] : _isGlobalWall) {
+ if (!currentWallIsGlobalWall)
+ continue;
+
+ switch (getGlobalWallType(currentDim)) {
+ case BoundaryUtils::BoundaryType::PERIODIC:
+ // nothing changes from normal ls1 behaviour, so empty case, and halo particles left untouched
+ break;
+
+ case BoundaryUtils::BoundaryType::OUTFLOW:
+ [[fallthrough]];
+ case BoundaryUtils::BoundaryType::REFLECTING: {
+ // create region by using getOuterRegionSlab()
+ auto const [curWallRegionBegin, curWallRegionEnd] =
+ RegionUtils::getOuterRegionSlab(_localRegionStart, _localRegionEnd, currentDim, haloWidths);
+
+ // grab an iterator from the converted coords
+ auto particlesInRegion = moleculeContainer->regionIterator(
+ curWallRegionBegin.data(), curWallRegionEnd.data(), ParticleIterator::ALL_CELLS);
+ for (auto moleculeIter = particlesInRegion; moleculeIter.isValid(); ++moleculeIter) {
+ // delete all halo particles
+ moleculeContainer->deleteMolecule(moleculeIter, false);
+ }
+ break;
+ }
+ default:
+ std::ostringstream error_message;
+ error_message << "BoundaryType::ERROR received in BoundaryHandler::removeNonPeriodicHalos!" << std::endl;
+ MARDYN_EXIT(error_message.str());
+ }
+ }
+}
diff --git a/src/parallel/boundaries/BoundaryHandler.h b/src/parallel/boundaries/BoundaryHandler.h
new file mode 100644
index 0000000000..08bcea4945
--- /dev/null
+++ b/src/parallel/boundaries/BoundaryHandler.h
@@ -0,0 +1,193 @@
+/*
+ * BoundaryHandler.h
+ *
+ * Created on: 24 March 2023
+ * Author: amartyads
+ */
+
+#pragma once
+
+#include "BoundaryUtils.h"
+#include "DimensionUtils.h"
+#include "RegionUtils.h"
+#include "particleContainer/ParticleContainer.h"
+
+#include
+#include