diff --git a/CMakeLists.txt b/CMakeLists.txt index eb67ee611dd7a61f8c9bea64ad247602510f77ef..60ac7739add4250ee20e90f6e032631fb88f2e8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -299,6 +299,7 @@ set(cpp_for_lib ${PROJECT_SOURCE_DIR}/cpp/particles/interpolation/Lagrange_polys.cpp ${PROJECT_SOURCE_DIR}/cpp/particles/interpolation/field_tinterpolator.cpp ${PROJECT_SOURCE_DIR}/cpp/particles/interpolation/particle_set.cpp + ${PROJECT_SOURCE_DIR}/cpp/particles/particle_solver.cpp ${PROJECT_SOURCE_DIR}/cpp/scope_timer.cpp ${PROJECT_SOURCE_DIR}/cpp/full_code/test_interpolation.cpp ${PROJECT_SOURCE_DIR}/cpp/full_code/NSVEparticles.cpp diff --git a/cpp/particles/abstract_particle_rhs.hpp b/cpp/particles/abstract_particle_rhs.hpp index 92f94477eeebea57e569f253677db42a6d673188..a959a6a5414cec60f92eea487e7b25322e328f85 100644 --- a/cpp/particles/abstract_particle_rhs.hpp +++ b/cpp/particles/abstract_particle_rhs.hpp @@ -31,6 +31,8 @@ // children of this class must provide a method to compute the rhs for a given particle set class abstract_particle_rhs { + private: + using particle_rnumber = abstract_particle_set::particle_rnumber; public: // destructor virtual ~abstract_particle_rhs(){} diff --git a/cpp/particles/interpolation/abstract_particle_set.hpp b/cpp/particles/interpolation/abstract_particle_set.hpp index da1ce30b62f4b32b98c4601564109c3fa2e62970..dfac856804ebfbdf8d08968be38aa85ab51b8e37 100644 --- a/cpp/particles/interpolation/abstract_particle_set.hpp +++ b/cpp/particles/interpolation/abstract_particle_set.hpp @@ -29,26 +29,36 @@ #include <array> #include <cassert> #include <vector> +#include <memory> #include "field.hpp" -// the point here is to have a class that stores particle state data, -// knows how to distribute this data among processes, -// and knows how to compute interpolations. -// the processes of MPI distribution and computing interpolations can't really be separated -// so it makes sense to merge the functionalities. + + +/** Brings together particle information with interpolation functionality. + * + * This is an abstract class that defines the functionality required by the + * particle solver code. + * We define methods for: + * - accessing particle set data and miscelaneous information. + * - updating particle information and redistributing data among MPI processes + * accordingly. + * - approximating fields at particle locations ("sampling"). + * + */ + class abstract_particle_set { - protected: + public: using partsize_t = long long int; using particle_rnumber = double; - public: - // destructor + // virtual destructor virtual ~abstract_particle_set(){} // extract particle information virtual const particle_rnumber* getParticleState() const = 0; virtual const partsize_t* getParticleIndex() const = 0; + virtual int setParticleState(particle_rnumber *) = 0; virtual std::unique_ptr<particle_rnumber[]> extractFromParticleState( const int firstComponent, @@ -56,10 +66,33 @@ class abstract_particle_set virtual partsize_t getLocalNumberOfParticles() const = 0; virtual partsize_t getTotalNumberOfParticles() const = 0; - virtual int getStateSize() const = 0; + virtual const int getStateSize() const = 0; + + // generic loop over data + template <class func_type> + int LOOP(func_type expression) + { + for (partsize_t idx = 0; idx < this->getLocalNumberOfParticles()*this->getStateSize(); idx++) + expression(idx); + return EXIT_SUCCESS; + } + template <class func_type> + int LOOP_state(func_type expression) + { + for (partsize_t idx_part = 0; idx_part < this->getLocalNumberOfParticles(); idx_part++) + for (unsigned int cc = 0; cc < this->getStateSize(); cc++) + expression(idx_part, cc); + return EXIT_SUCCESS; + } - // reorganize particles within MPI domain - virtual int redistribute() = 0; + /** Reorganize particles within MPI domain. + * + * Based on current particle locations, redistribute the full state + * data among the MPI processes. + * Optional: also redistribute a list of arrays of the same shape. + */ + virtual int redistribute( + std::vector<std::unique_ptr<particle_rnumber>> &additional_data) = 0; // sample field values at particle location virtual int sample( diff --git a/cpp/particles/interpolation/particle_set.hpp b/cpp/particles/interpolation/particle_set.hpp index 55045e091750387a7bff1fe13e8188120adf97bc..84b958d0e4f55192c94f3da0a69a6e9540d53879 100644 --- a/cpp/particles/interpolation/particle_set.hpp +++ b/cpp/particles/interpolation/particle_set.hpp @@ -29,6 +29,7 @@ #include <array> #include <cassert> #include <vector> +#include <memory> #include "field.hpp" #include "particles/particles_distr_mpi.hpp" #include "particles/particles_output_hdf5.hpp" @@ -36,6 +37,15 @@ #include "particles/interpolation/particles_generic_interp.hpp" #include "particles/interpolation/abstract_particle_set.hpp" + + +/** Practical implementation of particle sets. + * + * Child of `abstract_particle_set`. Brings together the functionality of + * `particles_field_computer` and `particles_distr_mpi`. + * + */ + template <int state_size, int neighbours, int smoothness> @@ -110,6 +120,15 @@ class particle_set: public abstract_particle_set return this->local_state.get(); } + int setParticleState(particle_rnumber *src_local_state) + { + this->LOOP( + [&](const partsize_t idx){ + this->local_state[idx] = src_local_state[idx]; + }); + return EXIT_SUCCESS; + } + const partsize_t* getParticleIndex() const { return this->local_index.get(); @@ -125,6 +144,11 @@ class particle_set: public abstract_particle_set return this->total_number_of_particles; } + const int getStateSize() const + { + return state_size; + } + int setParticleFileLayout(std::vector<hsize_t> input_layout) { this->particle_file_layout.resize(input_layout.size()); @@ -174,9 +198,8 @@ class particle_set: public abstract_particle_set return EXIT_SUCCESS; } - int redistribute() + int redistribute(std::vector<std::unique_ptr<particle_rnumber>> additional_data) { - // todo: ask why the last template parameter is needed this->pdistributor.template redistribute<interpolator_class, state_size, state_size, @@ -185,8 +208,8 @@ class particle_set: public abstract_particle_set this->number_particles_per_partition.get(), &this->local_number_of_particles, &this->local_state, - NULL, - 0, + additional_data.data(), + int(additional_data.size()), &this->local_index); return EXIT_SUCCESS; } diff --git a/cpp/particles/interpolation/particles_field_computer.hpp b/cpp/particles/interpolation/particles_field_computer.hpp index 620b4685fdc140c999a9bce13784f4dc1d87a773..a80a37ceabedfd9e60f2bbd11ea5b11ab284560b 100644 --- a/cpp/particles/interpolation/particles_field_computer.hpp +++ b/cpp/particles/interpolation/particles_field_computer.hpp @@ -33,6 +33,12 @@ #include "scope_timer.hpp" #include "particles/particles_utils.hpp" + + +/** \brief Computes interpolation kernel sum operation. + * + * */ + template <class partsize_t, class real_number, class interpolator_class, diff --git a/cpp/particles/interpolation/particles_generic_interp.hpp b/cpp/particles/interpolation/particles_generic_interp.hpp index e75ae9b3d92b34b170f13bde065eb24afa9dafe8..4966d5fdf66a434a3a51936cc3f00f72cc4d6adc 100644 --- a/cpp/particles/interpolation/particles_generic_interp.hpp +++ b/cpp/particles/interpolation/particles_generic_interp.hpp @@ -26,6 +26,10 @@ #ifndef PARTICLES_GENERIC_INTERP_HPP #define PARTICLES_GENERIC_INTERP_HPP +/** \brief Computes weights for interpolation formulas. + * + * */ + template <class real_number, int interp_neighbours, int smoothness> class particles_generic_interp; diff --git a/cpp/particles/particle_solver.cpp b/cpp/particles/particle_solver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7950bef404d3a64e57ee1f0315af6d903abdc866 --- /dev/null +++ b/cpp/particles/particle_solver.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** +* * +* Copyright 2020 Max Planck Institute for Dynamics and Self-Organization * +* * +* This file is part of TurTLE. * +* * +* TurTLE 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. * +* * +* TurTLE 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 TurTLE. If not, see <http://www.gnu.org/licenses/> * +* * +* Contact: Cristian.Lalescu@ds.mpg.de * +* * +******************************************************************************/ + + + +#include "particles/particle_solver.hpp" + +int particle_solver::Euler( + double dt, + const abstract_particle_rhs &rhs) + { + // temporary array for storing right-hand-side + auto *rhs_val = new particle_rnumber[this->pset->getLocalNumberOfParticles()*(this->pset->getStateSize())]; + // temporary array for new particle state + auto *xx = new particle_rnumber[this->pset->getLocalNumberOfParticles()*(this->pset->getStateSize())]; + + // compute right hand side + rhs(0, *(this->pset), rhs_val); + + // compute new particle state + this->pset->LOOP( + [&](const partsize_t idx){ + xx[idx] = this->pset->getParticleState()[idx] + dt * rhs_val[idx]; + }); + + // update particle set + std::vector<std::unique_ptr<particle_rnumber>> temporary; + this->pset->setParticleState(xx); + this->pset->redistribute(temporary); + + // deallocate temporary arrays + delete[] rhs_val; + delete[] xx; + return EXIT_SUCCESS; + } + diff --git a/cpp/particles/particle_solver.hpp b/cpp/particles/particle_solver.hpp index a8ecff83fff82faec97f670b73e426c72880760a..e46677ba357e7436e342364d28a0fd3ed581f94c 100644 --- a/cpp/particles/particle_solver.hpp +++ b/cpp/particles/particle_solver.hpp @@ -30,31 +30,36 @@ #include "particles/interpolation/abstract_particle_set.hpp" #include "particles/abstract_particle_rhs.hpp" -namespace particle_solver + + +/** Time-stepping and checkpointing functionality for particle systems. + * + * This class implements the universal functionality of a particle solver: + * time-stepping methods and checkpointing, together with the associated + * bookkeeping. + * + * We rely on predefined functionality of particle set objects, as well as + * "particle right-hand-side computers". + */ + +class particle_solver { - template<int state_size> - int Euler( - double dt, - abstract_particle_set &pset, - const abstract_particle_rhs &rhs) - { - auto *rhs_val = new abstract_particle_set::particle_rnumber[pset.getLocalNumberOfParticles()*state_size]; - auto *xx = new abstract_particle_set::particle_rnumber[pset.getLocalNumberOfParticles()*state_size]; - rhs(0, pset, rhs_val); - for (abstract_particle_set::partsize_t idx_part = 0; idx_part < pset.getLocalNumberOfParticles(); idx_part++) - { - for (unsigned int cc = 0; cc < state_size; cc++) - { - xx[idx_part*state_size + cc] = ( - pset.getParticleState()[idx_part*state_size + cc] + - dt * rhs_val[idx_part*state_size + cc]); - } - } - // TODO: replace pset particle state with xx values - delete[] rhs_val; - delete[] xx; - return EXIT_SUCCESS; - } + private: + using particle_rnumber = abstract_particle_set::particle_rnumber; + using partsize_t = abstract_particle_set::partsize_t; + + abstract_particle_set *pset; + + public: + particle_solver( + abstract_particle_set &in_pset): + pset(&in_pset) + {} + ~particle_solver(){} + + int Euler( + double dt, + const abstract_particle_rhs &rhs); }; #endif//PARTICLE_SOLVER_HPP