aboutsummaryrefslogtreecommitdiff
path: root/Source/Particles/Resampling/LevelingThinning.cpp
blob: 33ad87630612822071ff4d2453db05a169a01b36 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/* Copyright 2019-2020 Neil Zaim
 *
 * This file is part of WarpX.
 *
 * License: BSD-3-Clause-LBNL
 */
#include "LevelingThinning.H"

#include "Particles/WarpXParticleContainer.H"
#include "Utils/ParticleUtils.H"
#include "Utils/WarpXUtil.H"

#include <AMReX.H>
#include <AMReX_BLassert.H>
#include <AMReX_DenseBins.H>
#include <AMReX_Extension.H>
#include <AMReX_GpuLaunch.H>
#include <AMReX_GpuQualifiers.H>
#include <AMReX_PODVector.H>
#include <AMReX_ParmParse.H>
#include <AMReX_Particle.H>
#include <AMReX_ParticleTile.H>
#include <AMReX_Particles.H>
#include <AMReX_Random.H>
#include <AMReX_StructOfArrays.H>

#include <AMReX_BaseFwd.H>

LevelingThinning::LevelingThinning (const std::string species_name)
{
    using namespace amrex::literals;

    amrex::ParmParse pp_species_name(species_name);
    queryWithParser(pp_species_name, "resampling_algorithm_target_ratio", m_target_ratio);
    AMREX_ALWAYS_ASSERT_WITH_MESSAGE( m_target_ratio > 0._rt,
                                    "Resampling target ratio should be strictly greater than 0");
    if (m_target_ratio <= 1._rt)
    {
        amrex::Warning("WARNING: target ratio for leveling thinning is smaller or equal to one."
                       " It is possible that no particle will be removed during resampling");
    }

    queryWithParser(pp_species_name, "resampling_algorithm_min_ppc", m_min_ppc);
    AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_min_ppc >= 1,
                                     "Resampling min_ppc should be greater than or equal to 1");
}

void LevelingThinning::operator() (WarpXParIter& pti, const int lev,
                                   WarpXParticleContainer * const pc) const
{
    using namespace amrex::literals;

    auto& ptile = pc->ParticlesAt(lev, pti);
    auto& soa = ptile.GetStructOfArrays();
    amrex::ParticleReal * const AMREX_RESTRICT w = soa.GetRealData(PIdx::w).data();
    WarpXParticleContainer::ParticleType * const AMREX_RESTRICT
                                 particle_ptr = ptile.GetArrayOfStructs()().data();

    // Using this function means that we must loop over the cells in the ParallelFor. In the case
    // of the leveling thinning algorithm, it would have possibly been more natural and more
    // efficient to directly loop over the particles. Nevertheless, this structure with a loop over
    // the cells is more general and can be readily used to implement almost any other resampling
    // algorithm.
    auto bins = ParticleUtils::findParticlesInEachCell(lev, pti, ptile);

    const int n_cells = bins.numBins();
    const auto indices = bins.permutationPtr();
    const auto cell_offsets = bins.offsetsPtr();

    const amrex::Real target_ratio = m_target_ratio;
    const int min_ppc = m_min_ppc;

    // Loop over cells
    amrex::ParallelForRNG( n_cells,
        [=] AMREX_GPU_DEVICE (int i_cell, amrex::RandomEngine const& engine) noexcept
        {
            // The particles that are in the cell `i_cell` are
            // given by the `indices[cell_start:cell_stop]`
            const auto cell_start = cell_offsets[i_cell];
            const auto cell_stop  = static_cast<int>(cell_offsets[i_cell+1]);
            const int cell_numparts = cell_stop - cell_start;

            // do nothing for cells with less particles than min_ppc
            // (this intentionally includes skipping empty cells, too)
            if (cell_numparts < min_ppc)
                return;
            amrex::Real average_weight = 0._rt;

            // First loop over cell particles to compute average particle weight in the cell
            for (int i = cell_start; i < cell_stop; ++i)
            {
                average_weight += w[indices[i]];
            }
            average_weight /= cell_numparts;

            const amrex::Real level_weight = average_weight*target_ratio;


            // Second loop over cell particles to perform the thinning
            for (int i = cell_start; i < cell_stop; ++i)
            {
                // Particles with weight greater than level_weight are left unchanged
                if (w[indices[i]] > level_weight) {continue;}

                amrex::Real const random_number = amrex::Random(engine);
                // Remove particle with probability 1 - particle_weight/level_weight
                if (random_number > w[indices[i]]/level_weight)
                {
                    particle_ptr[indices[i]].id() = -1;
                }
                // Set particle weight to level weight otherwise
                else
                {
                    w[indices[i]] = level_weight;
                }
            }
        }
    );
}