FBasicParticleContainer.hpp 15.9 KB
Newer Older
1
// See LICENCE file at project root
2 3 4 5 6 7

#ifndef FBASIC_PARTICLE_CONTAINER_HPP_
#define FBASIC_PARTICLE_CONTAINER_HPP_

#include <vector>

COULAUD Olivier's avatar
COULAUD Olivier committed
8
#include "Utils/FPoint.hpp"
9 10 11
#include "Components/FParticleType.hpp"
#include "Components/FBasicParticle.hpp"

12
#include "Adaptive/FVariadicParticleContainer.hpp"
13 14 15

#include "Utils/variadic_container.hpp"
#include "Utils/FAlignedAllocator.hpp"
16
#include "inria/integer_sequence.hpp"
17

18 19 20
namespace scalfmm {
namespace details {
namespace basic_particle_container {
21

22
/** \brief Basic particle container template declaration
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
23
 *
24 25
 * Used to allow automatic indexing of parameter packs during
 * specialization.
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
26
 */
27 28 29 30
template<class Allocator,
         class PosPack,
         class AttributePack,
         class PosIndexList,
31 32 33
         class AttrIndexList,
         class OtherIndexList,
         class... OtherTypes
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
         >
class FBasicParticleContainerImpl;

/** \brief Basic particle container implementation
 *
 * A container that can store AttributeCount particle attributes. Each attribute
 * has type Attribute.
 *
 * \tparam Allocator Class used to manage memory allocation
 * \tparam FReal Type of the position members
 * \tparam Attribute Type of the attributes
 * \tparam Dim Space dimension count
 * \tparam AttributeCount Number of attributes for a particle
 * (i.e. same type repeated for each existing particle attribute).
 * \tparam posIndices Index pack to index the position components
 * \tparam attrIndices Index pack to index the attributes
 *
 * \note The template paramters are deduced from the specilization
 * arguments.
 *
 */
template<
    class Allocator,
    class FReal,
    class Attribute,
    std::size_t _Dim,
    std::size_t _AttributeCount,
    std::size_t... posIndices,
62 63 64
    std::size_t... attrIndices,
    class... OtherTypes,
    std::size_t... otherIndices
65 66 67 68 69
    >
class FBasicParticleContainerImpl<
    Allocator,
    scalfmm::pack<_Dim, FReal>,
    scalfmm::pack<_AttributeCount, Attribute>,
70 71 72
    inria::index_sequence<posIndices...>,
    inria::index_sequence<attrIndices...>,
    inria::index_sequence<otherIndices...>,
73
    OtherTypes...
74 75
    > :
    public FVariadicParticleContainer<
76
        FBasicParticle<FReal, _Dim, OtherTypes..., scalfmm::pack<_AttributeCount, Attribute> >,
77
        Allocator
78
    >
79 80 81
{
public:
    /// Base type
82
    using FParticle = FBasicParticle<FReal, _Dim, OtherTypes..., scalfmm::pack<_AttributeCount, Attribute> >;
83

84
    enum {
85 86 87 88
        Dim = _Dim,                              ///< Space dimension count
        AttributeCount = _AttributeCount,        ///< Primary attribute count
        OtherCount     = sizeof...(OtherTypes),  ///< Count of secondary attributes
        AttributeBegin = _Dim + sizeof...(OtherTypes), ///< First index of the primary attributes
89
    };
90

91 92
private:
    using FBase = FVariadicParticleContainer<FParticle, Allocator>;
93

94

95 96
    /** \brief Proxy array to return positions
     * \warning updated when getPosition is called.
97
     */
98
    mutable FReal* _positions[Dim];
99

100 101
public:
    using value_type = FParticle;
102

103 104
    /// Reuse base constructors
    using FBase::FBase;
105

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    /** \brief Get non const array of position components
     *
     *
     * Returns an array of arrays. Each sub-array stores a component of
     * every particle's position. For instance, `getPositions()[0]` is an
     * array containing all the X components of the particles in the
     * container.
     *
     * ~~~~{.cpp}
     * FReal* Xarray = container.getPositions()[0];
     * FReal* Yarray = container.getPositions()[1];
     * FReal* Zarray = container.getPositions()[2];
     * ~~~~
     *
     * \return The array of position member arrays
     *
     * \note This updates the #_positions proxy
     */
    FReal* const* getPositions() {
        auto l = {(_positions[posIndices] = std::get<posIndices>(this->data())) ...};
        (void)l;
        return _positions;
128
    }
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
129

130 131 132 133 134 135 136 137
    /** \brief Get const array of position components
     *
     * See getPositions().
     */
    const FReal* const* getPositions() const {
        auto l = {(_positions[posIndices] = const_cast<FReal*>(std::get<posIndices>(this->data()))) ...};
        (void)l;
        return _positions;
138 139
    }

PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
140

141 142 143 144 145 146 147

    //DELETE, replace with getPositions
    /** \brief Get non const array of position components
     *
     * See getPositions().
     */
    [[gnu::deprecated]]
148
    FReal* const* getWPositions() {
149
        return getPositions();
150
    }
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
151 152


153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
    /** \brief Attribute getter
     *
     * This gets the underlying array that stores the index_th attribute.
     *
     * ~~~~{.cpp}
     * Attribute* attrArray = container.getAttribute(0);
     * ~~~~
     *
     * \param index The index of the attribute array to get.
     *
     * \return  Index_th attribute array
     *
     * \warning For efficicy purpose, it is highly advised to used the
     * template overload of this member: getAttribute().
     */
    Attribute* getAttribute(const int index) {
        using data_type = decltype(this->data());
        auto my_data = this->data();
        return dynamic_attribute<data_type>::get(index, my_data);
172
    }
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
173

174 175 176 177 178 179 180
    /** \brief Attribute const getter
     * See getAttribute(const int index).
     */
    const Attribute* getAttribute(const int index) const {
        using data_type = decltype(this->data());
        auto my_data = this->data();
        return dynamic_attribute<data_type>::get(index, my_data);
181
    }
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
182

183 184 185 186 187 188 189 190 191 192 193 194 195 196
    /** \brief Compilation time attribute getter
     *
     * This gets the underlying array that stores the index_th attribute.
     *
     * ~~~~{.cpp}
     * Attribute* attrArray = container.getAttribute(0);
     * ~~~~
     *
     * \tparam index The index of the attribute array to get.
     *
     * \return  Index_th attribute array
     */
    template<int index>
    Attribute* getAttribute() {
197
        return std::get<index+AttributeBegin>(FBase::data());
198
    }
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
199

200 201 202 203 204
    /** \brief Compilation time attribute const getter
     * See getAttribute().
     */
    template<int index>
    const Attribute* getAttribute() const {
205
        return std::get<index+AttributeBegin>(FBase::data());
206
    }
207

208 209 210 211 212 213 214 215 216 217 218
    //DELETE
    /** \brief Pushes several particles in the container
     *
     * \param arraySize Size of the arrays given as other arguments, equals
     * the number of particles to add to the array
     * \param args Pack of arrays, one per attribute
     * \param positionArray Array of positions, one per particle
     *
     * \tparam Args Parameter pack of types convertible to the Attribute
     * type.
     */
219
    template<typename... Args>
220 221 222
    void pushArray(const FPoint<FReal,Dim>* positionArray, FSize arraySize, Args*... args) {
        for(FSize idx = 0; idx < arraySize; ++idx) {
            this->push(positionArray[idx], args[idx]...);
223 224
        }
    }
225

226 227
    using FBase::push;

228

229 230 231 232 233 234 235 236 237 238 239
    /** \brief Push a particle in the container
     *
     * \tparam Args Parameter pack of types convertible to the Attribute type.
     *
     * \note The pack size must be at most AttributeCount, if it is less,
     * the remaining attributes are zero constructed (i.e. `Attribute(0)`
     * is called to build them).
     *
     * \param position The particle position
     * \param args The particle attributes
     */
240 241 242
    // template<typename... Args>
    // void push(const FPoint<FReal>& position, Args... args) {
    //     this->push_helper(
243
    //         inria::make_index_sequence<AttributeCount+OtherCount-sizeof...(args)>(),
244 245 246
    //         position, args...
    //         );
    // }
247

248 249 250 251 252
    /** \brief Push a particle in the container (array attributes)
     *
     * \param inParticlePosition The particle position
     * \param values std::array containing the particle's attributes
     */
253 254
    void push(const FPoint<FReal>& inParticlePosition, const OtherTypes&... others, const std::array<Attribute , AttributeCount>& values) {
        FBase::push_back(std::get<posIndices>(inParticlePosition)..., others..., values[attrIndices]...);
255 256
    }

257 258 259 260 261 262 263 264 265 266 267 268
    /** \brief Push a particle in the container (array attributes, type specifier)
     *
     * This method does nothing different thab its simple overload without
     * the FParticleType argument. The argument is unused.
     *
     * \param inParticlePosition Particle position
     * \param Unused
     * \param values std::array containing the particle's attributes
     */
    void push(const FPoint<FReal>& inParticlePosition, const FParticleType /*type*/,
              const std::array<Attribute , AttributeCount>& values) {
        this->push(inParticlePosition, values);
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
269 270
    }

271 272 273 274 275 276 277 278 279 280 281 282
    /** \brief Push a particle in the container (type specifier)
     *
     * This method does nothing different than its simple overload without
     * the FParticleType argument. The argument is unused.
     *
     * \tparam Args Parameter pack for the pushed attributes, automatically
     * deduced from the arguments
     *
     * \param inParticlePosition Particle position
     * \param unnamed Particle type, does nothing
     * \param args Particle attributes
     */
283
    template<typename... Args>
284 285
    void push(const FPoint<FReal>& inParticlePosition, const FParticleType /*particleType*/, Args... args) {
        this->push(inParticlePosition, args...);
286
    }
PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
287 288


289 290 291 292 293 294 295 296 297 298 299 300 301 302
    //DELETE
    /** \brief Removes particles from the container
     *
     * \param indicesToRemove C array containing the indices in the container of
     * the particles to remove
     * \param nbParticlesToRemove Size of the `indicesToRemove array`
     */
    void removeParticles(const FSize indicesToRemove[], const FSize nbParticlesToRemove) {
        std::vector<FSize> removed;
        for(FSize i = 0; i < nbParticlesToRemove; ++i) {
            auto idx = indicesToRemove[i];
            for(auto&& j : removed) {
                if(i < j)
                    --idx;
303
            }
304 305
            removed.push_back(indicesToRemove[i]);
            FBase::erase(this->begin()+idx);
306
        }
307
    }
308

309 310 311 312 313 314
    /** \brief Get a pointer to the beginning of the attributes memory block
     *
     * \return A pointer to the beginning of the attributes memory block
     */
    Attribute* getRawData() {
        return this->getAttribute<0>();
315 316
    }

317 318 319 320 321 322
    /** \brief Get a pointer to the beginning of the attributes memory block
     *
     * \return A pointer to the beginning of the attributes memory block
     */
    const Attribute* getRawData() const {
        return this->getAttribute<0>();
323 324
    }

325 326 327 328 329 330 331 332 333 334 335 336
    /** \brief Get the leading dimension of the data array
     *
     * The data array is allocated as one memory block that is share among
     * attribute subarrays. Since all attributes have the same type, the
     * distance (aka. leading dimension) between the beginning of each subarray is
     * constant.
     *
     * \note The leading dimension may be bigger than `capacity *
     * sizeof(Attribute)` if the memory is aligned.
     *
     * \return The data array leading dimension
     */
337
    FSize getLeadingRawData() const {
338
        return std::get<AttributeBegin+1>(this->data()) - std::get<AttributeBegin>(this->data());
339 340 341
    }


342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
    /** \brief Reset all the particle attributes to 0
     *
     * The particle count and positions are not changed.
     */
    void resetToInitialState() {
        auto l = {(this->resetToInitialState(attrIndices), 0)...};
        (void)l;
    }


    /** \brief Reset an attribute subarray to 0
     *
     * The particle count, positions and other attributes are not changed.
     *
     * \param idxAttribute Index of the subarray to reset
     */
    void resetToInitialState(const unsigned idxAttribute) {
        Attribute* p = this->getAttribute(idxAttribute);
        memset(p, 0, sizeof(Attribute) * this->size());
    }

private:

    template <typename AttributeTuple, std::size_t I = AttributeCount - 1 >
    struct dynamic_attribute : dynamic_attribute<AttributeTuple, I-1>  {
        static_assert(I <= AttributeCount, "Attribute index is greater than attribute count.");
        static auto get(const int& index, AttributeTuple data)
369
            -> typename std::tuple_element<AttributeBegin, AttributeTuple>::type
370 371
        {
            if(I == index) {
372
                return std::get<AttributeBegin+I>(data);
373 374 375
            } else {
                return dynamic_attribute<AttributeTuple, I-1>::get(index, data);
            }
376
        }
377 378 379 380
    };

    template<typename AttributeTuple>
    struct dynamic_attribute<AttributeTuple, 0> {
381
        static auto get(const int& /*index*/, AttributeTuple data)
382
            -> typename std::tuple_element<AttributeBegin, AttributeTuple>::type
383
        {
384
            return std::get<AttributeBegin>(data);
385 386 387 388 389 390
        }
    };


    void update_proxy_arrays() const {
        noop_t(getAttribute(attrIndices)
391
                = const_cast<Attribute*>(std::get<AttributeBegin+attrIndices>(FBase::data())) ...);
392 393
        noop_t(_positions[posIndices]
               = const_cast<FReal*>(std::get<posIndices>(FBase::data())) ...);
394 395

    }
396 397


398 399 400
};


401 402 403 404 405 406 407 408
/** Helper to get FBasicParticleContainer base
 *
 * A bit of meta-programming to find FBasicParticleContainer base.
 */
template<class FReal,
         class Attribute,
         unsigned AttributeCount,
         std::size_t Dim,
409 410
         class Allocator,
         class... OtherTypes
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
         >
struct FBasicParticleContainerChooser {
private:
    /** \brief Returns the base variadic vector for the FBasicParticleContainer
     *
     * This is a declaration that is only meant to be used at compile
     * time to create the right variadic vector.
     *
     * The return type is:
     * ~~~~
     * variadic_vector<FReal,... , Attribute, ...>
     *              Dim times ^                ^ AttributeCount times
     * ~~~~
     *
     * \tparam T The attribute type for the container
     * \tparam dim_indices integer pack for the position indices
     * \tparam attribute_indices integer pack for the attribute indices
     *
     * \param unnamed The position compounds index_sequence
     * \param unnamed The attributes index_sequence
     *
     * \return The class specialization of the container implementation
     */
    template<std::size_t... dim_indices, std::size_t... attribute_indices>
435
    static auto getContainer(inria::index_sequence<dim_indices...>, inria::index_sequence<attribute_indices...>)
436 437 438 439
        -> FBasicParticleContainerImpl<
            Allocator,
            scalfmm::pack<sizeof...(dim_indices), FReal>,
            scalfmm::pack<AttributeCount, Attribute>,
440 441 442
            inria::index_sequence<dim_indices...>,
            inria::index_sequence<attribute_indices...>,
            inria::index_sequence_for<OtherTypes...>,
443 444
            OtherTypes...
        >;
445 446 447
public:
    /** \brief FBasicParticleContainer base type
    */
448 449
    using type = decltype(getContainer(inria::make_index_sequence<Dim>(),
                                       inria::make_index_sequence<AttributeCount>()));
450 451 452
};


453
} } } // close namespace scalfmm::details::basic_particle_container
454

455
/**
456
 * @author Quentin Khan (quentin.khan@inria.fr)
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
 *
 * This container which can hold one type (AttributeClass) for each particle.
 * The memory is allocated in one block and stores the positions and the requested type.
 *
 * For example if one wants to store a struct for each particle:
 * \code
 * struct AStruct{ ... };
 * FBasicParticleContainer<FReal, 1, AStruct> container;
 * \endcode
 * And then access is done using:
 * \code
 * AStruct* strucs = container.getAttributes<0>();
 * \endcode
 * For example if one wants to store 4 doubles for each particles:
 * \code
 * FBasicParticleContainer<FReal, 4, double> container;
 * \endcode
 * And then access is done using:
 * \code
 * double* v1 = container.getAttributes<0>();
 * double* v2 = container.getAttributes<1>();
 * double* v3 = container.getAttributes<2>();
 * double* v4 = container.getAttributes<3>();
 * \endcode
 * The memory is aligned to FP2PDefaultAlignement value.
 */
483 484 485 486
template<class FReal,
         unsigned AttributeCount,
         class Attribute,
         std::size_t Dim = 3,
487
         class Allocator = FAlignedAllocator<FP2PDefaultAlignement,char>,
488
         class... OtherTypes
489 490
         >
struct FBasicParticleContainer :
491
    public scalfmm::details::basic_particle_container::FBasicParticleContainerChooser<FReal, Attribute, AttributeCount, Dim, Allocator, OtherTypes... >::type
492 493 494 495
{};



496
#endif