Skip to content

Commit a5c8f9f

Browse files
EyaDammakpre-commit-ci[bot]RemiLehe
authored
Obtain exact time (real) when particles hit boundaries + test (#4695)
* adding the time * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update particle_containers.py * Update ParticleBoundaryBuffer.cpp * Update PICMI_inputs_rz.py * Update PICMI_inputs_rz.py * Update particle_containers.py * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * fix issue * fix issue * issue fixed * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * Update analysis_rz.py * Update analysis_rz_filter.py * Update analysis.py * Update PICMI_inputs_scrape.py * Update PICMI_inputs_reflection.py * Update particle_containers.py * Update PICMI_inputs_reflection.py * Update PICMI_inputs_scrape.py * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * Update ParticleBoundaryBuffer.cpp * Update Source/Particles/ParticleBoundaryBuffer.cpp Co-authored-by: Remi Lehe <remi.lehe@normalesup.org> * Update Source/Particles/ParticleBoundaryBuffer.cpp Co-authored-by: Remi Lehe <remi.lehe@normalesup.org> * Update ParticleBoundaryBuffer.cpp --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Remi Lehe <remi.lehe@normalesup.org>
1 parent e038ef9 commit a5c8f9f

File tree

8 files changed

+78
-38
lines changed

8 files changed

+78
-38
lines changed

Docs/source/usage/parameters.rst

+4-2
Original file line numberDiff line numberDiff line change
@@ -2752,8 +2752,10 @@ The data collected at each boundary is written out to a subdirectory of the diag
27522752
By default, all of the collected particle data is written out at the end of the simulation. Optionally, the ``<diag_name>.intervals`` parameter can be given to specify writing out the data more often.
27532753
This can be important if a large number of particles are lost, avoiding filling up memory with the accumulated lost particle data.
27542754

2755-
In addition to their usual attributes, the saved particles have an integer attribute ``timestamp``, which
2756-
indicates the PIC iteration at which each particle was absorbed at the boundary.
2755+
In addition to their usual attributes, the saved particles have an integer attribute ``step_scraped``, which
2756+
indicates the PIC iteration at which each particle was absorbed at the boundary,
2757+
and a real attribute ``time_scraped``, which indicates the exact time calculated when each
2758+
particle hits the EB.
27572759

27582760
``BoundaryScrapingDiagnostics`` can be used with ``<diag_name>.<species>.random_fraction``, ``<diag_name>.<species>.uniform_stride``, and ``<diag_name>.<species>.plot_filter_function``, which have the same behavior as for ``FullDiagnostics``. For ``BoundaryScrapingDiagnostics``, these filters are applied at the time the data is written to file. An implication of this is that more particles may initially be accumulated in memory than are ultimately written. ``t`` in ``plot_filter_function`` refers to the time the diagnostic is written rather than the time the particle crossed the boundary.
27592761

Examples/Tests/point_of_contact_EB/analysis.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,35 @@
2626
ts_scraping = OpenPMDTimeSeries('./diags/diag2/particles_at_eb/')
2727

2828
it=ts_scraping.iterations
29-
x,y,z=ts_scraping.get_particle( ['x','y','z'], species='electron', iteration=it )
29+
step_scraped, time_scraped, x, y, z=ts_scraping.get_particle( ['stepScraped','timeScraped','x','y','z'], species='electron', iteration=it )
30+
time_scraped_reduced=time_scraped[0]*1e10
3031

3132
# Analytical results calculated
3233
x_analytic=-0.1983
3334
y_analytic=0.02584
3435
z_analytic=0.0000
3536

37+
#result obtained by analysis of simulations
38+
step=3
39+
time_reduced=3.58
40+
3641
print('NUMERICAL coordinates of the point of contact:')
37-
print('x=%5.4f, y=%5.4f, z=%5.4f' % (x[0], y[0], z[0]))
42+
print('step_scraped=%d, time_stamp=%5.4f e-10, x=%5.4f, y=%5.4f, z=%5.4f' % (step_scraped[0],time_reduced,x[0], y[0], z[0]))
3843
print('\n')
3944
print('ANALYTICAL coordinates of the point of contact:')
40-
print('x=%5.4f, y=%5.4f, z=%5.4f' % (x_analytic, y_analytic, z_analytic))
45+
print('step_scraped=%d, time_stamp=%5.4f e-10, x=%5.4f, y=%5.4f, z=%5.4f' % (step, time_reduced, x_analytic, y_analytic, z_analytic))
4146

4247
tolerance=0.001
48+
tolerance_t=0.003
4349
print("tolerance = "+ str(tolerance *100) + '%')
50+
print("tolerance for the time = "+ str(tolerance_t *100) + '%')
4451

52+
diff_step=np.abs((step_scraped[0]-step)/step)
53+
diff_time=np.abs((time_scraped_reduced-time_reduced)/time_reduced)
4554
diff_x=np.abs((x[0]-x_analytic)/x_analytic)
4655
diff_y=np.abs((y[0]-y_analytic)/y_analytic)
4756

4857
print("percentage error for x = %5.4f %%" %(diff_x *100))
4958
print("percentage error for y = %5.4f %%" %(diff_y *100))
5059

51-
assert (diff_x < tolerance) and (diff_y < tolerance) and (np.abs(z[0]) < 1e-8) , 'Test point_of_contact did not pass'
60+
assert (diff_x < tolerance) and (diff_y < tolerance) and (np.abs(z[0]) < 1e-8) and (diff_step < 1e-8) and (diff_time < tolerance_t) , 'Test point_of_contact did not pass'

Examples/Tests/scraping/analysis_rz.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ def n_remaining_particles( iteration ):
5353
w, = ts_full.get_particle(['w'], iteration=iteration)
5454
return len(w)
5555
def n_scraped_particles( iteration ):
56-
timestamp = ts_scraping.get_particle( ['timestamp'], iteration=ts_scraping.iterations[0] )
57-
return (timestamp <= iteration).sum()
56+
step_scraped = ts_scraping.get_particle( ['stepScraped'], iteration=ts_scraping.iterations[0] )
57+
return (step_scraped <= iteration).sum()
5858
n_remaining = np.array([ n_remaining_particles(iteration) for iteration in ts_full.iterations ])
5959
n_scraped = np.array([ n_scraped_particles(iteration) for iteration in ts_full.iterations ])
6060
n_total = n_remaining[0]

Examples/Tests/scraping/analysis_rz_filter.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ def n_remaining_particles( iteration ):
5050
w, = ts_full.get_particle(['w'], iteration=iteration)
5151
return len(w)
5252
def n_scraped_particles( iteration ):
53-
timestamp = ts_scraping.get_particle( ['timestamp'], iteration=ts_scraping.iterations[0] )
54-
return (timestamp <= iteration).sum()
53+
step_scraped = ts_scraping.get_particle( ['stepScraped'], iteration=ts_scraping.iterations[0] )
54+
return (step_scraped <= iteration).sum()
5555
def n_scraped_z_leq_zero( iteration ):
5656
z_pos, = ts_scraping.get_particle( ['z'], iteration=ts_scraping.iterations[0] )
5757
return (z_pos <= 0).sum()

Python/pywarpx/particle_containers.py

+13-11
Original file line numberDiff line numberDiff line change
@@ -772,9 +772,9 @@ def get_particle_boundary_buffer(self, species_name, boundary, comp_name, level)
772772
form x/y/z_hi/lo or eb.
773773
774774
comp_name : str
775-
The component of the array data that will be returned. If
776-
"step_scraped" the special attribute holding the timestep at
777-
which a particle was scraped will be returned.
775+
The component of the array data that will be returned.
776+
"x", "y", "z", "ux", "uy", "uz", "w"
777+
"step_scraped","time_scraped", "nx", "ny", "nz"
778778
779779
level : int
780780
Which AMR level to retrieve scraped particle data from.
@@ -785,18 +785,20 @@ def get_particle_boundary_buffer(self, species_name, boundary, comp_name, level)
785785
species_name, self._get_boundary_number(boundary)
786786
)
787787
data_array = []
788-
if comp_name == 'step_scraped':
789-
# the step scraped is always the final integer component
790-
comp_idx = part_container.num_int_comps - 1
788+
#loop over the real attributes
789+
if comp_name in part_container.real_comp_names:
790+
comp_idx = part_container.real_comp_names[comp_name]
791791
for ii, pti in enumerate(libwarpx.libwarpx_so.BoundaryBufferParIter(part_container, level)):
792+
soa = pti.soa()
793+
data_array.append(xp.array(soa.get_real_data(comp_idx), copy=False))
794+
#loop over the integer attributes
795+
elif comp_name in part_container.int_comp_names:
796+
comp_idx = part_container.int_comp_names[comp_name]
797+
for ii, pti in enumerate(libwarpx.libwarpx_so.BoundaryBufferParIter(part_container, level)):
792798
soa = pti.soa()
793799
data_array.append(xp.array(soa.get_int_data(comp_idx), copy=False))
794800
else:
795-
container_wrapper = ParticleContainerWrapper(species_name)
796-
comp_idx = container_wrapper.particle_container.get_comp_index(comp_name)
797-
for ii, pti in enumerate(libwarpx.libwarpx_so.BoundaryBufferParIter(part_container, level)):
798-
soa = pti.soa()
799-
data_array.append(xp.array(soa.get_real_data(comp_idx), copy=False))
801+
raise RuntimeError('Name %s not found' %comp_name)
800802
return data_array
801803

802804

Source/Diagnostics/BTDiagnostics.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1299,7 +1299,7 @@ BTDiagnostics::InterleaveBufferAndSnapshotHeader ( std::string buffer_Header_pat
12991299
BTDPlotfileHeaderImpl buffer_HeaderImpl(buffer_Header_path);
13001300
buffer_HeaderImpl.ReadHeaderData();
13011301

1302-
// Update timestamp of snapshot with recently flushed buffer
1302+
// Update step_scraped of snapshot with recently flushed buffer
13031303
snapshot_HeaderImpl.set_time( buffer_HeaderImpl.time() );
13041304
snapshot_HeaderImpl.set_timestep( buffer_HeaderImpl.timestep() );
13051305

Source/Particles/ParticleBoundaryBuffer.cpp

+30-16
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ struct IsOutsideDomainBoundary {
4545
};
4646

4747
struct FindEmbeddedBoundaryIntersection {
48-
const int m_index;
48+
const int m_step_index;
49+
const int m_time_index;
4950
const int m_step;
5051
const amrex::Real m_dt;
5152
amrex::Array4<const amrex::Real> m_phiarr;
@@ -69,9 +70,6 @@ struct FindEmbeddedBoundaryIntersection {
6970
dst.m_runtime_idata[j][dst_i] = src.m_runtime_idata[j][src_i];
7071
}
7172

72-
// Also record the integer timestep on the destination
73-
dst.m_runtime_idata[m_index][dst_i] = m_step;
74-
7573
// Modify the position of the destination particle:
7674
// Move it to the point of intersection with the embedded boundary
7775
// (which is found by using a bisection algorithm)
@@ -90,7 +88,6 @@ struct FindEmbeddedBoundaryIntersection {
9088
amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> plo = m_plo;
9189

9290
// Bisection algorithm to find the point where phi(x,y,z)=0 (i.e. on the embedded boundary)
93-
9491
amrex::Real dt_fraction = amrex::bisect( 0.0, 1.0,
9592
[=] (amrex::Real dt_frac) {
9693
int i, j, k;
@@ -102,6 +99,10 @@ struct FindEmbeddedBoundaryIntersection {
10299
return phi_value;
103100
} );
104101

102+
// Also record the real time on the destination
103+
dst.m_runtime_idata[m_step_index][dst_i] = m_step;
104+
dst.m_runtime_rdata[m_time_index][dst_i] = m_step*m_dt + (1- dt_fraction)*m_dt;
105+
105106
// Now that dt_fraction has be obtained (with bisect)
106107
// Save the corresponding position of the particle at the boundary
107108
amrex::ParticleReal x_temp=xp, y_temp=yp, z_temp=zp;
@@ -129,8 +130,10 @@ struct FindEmbeddedBoundaryIntersection {
129130
};
130131

131132
struct CopyAndTimestamp {
132-
int m_index;
133+
int m_step_index;
134+
int m_time_index;
133135
int m_step;
136+
const amrex::Real m_dt;
134137

135138
template <typename DstData, typename SrcData>
136139
AMREX_GPU_HOST_DEVICE
@@ -147,10 +150,12 @@ struct CopyAndTimestamp {
147150
for (int j = 0; j < src.m_num_runtime_int; ++j) {
148151
dst.m_runtime_idata[j][dst_i] = src.m_runtime_idata[j][src_i];
149152
}
150-
dst.m_runtime_idata[m_index][dst_i] = m_step;
153+
dst.m_runtime_idata[m_step_index][dst_i] = m_step;
154+
dst.m_runtime_rdata[m_time_index][dst_i] = m_step*m_dt;
151155
}
152156
};
153157

158+
154159
ParticleBoundaryBuffer::ParticleBoundaryBuffer ()
155160
{
156161
m_particle_containers.resize(numBoundaries());
@@ -328,7 +333,8 @@ void ParticleBoundaryBuffer::gatherParticles (MultiParticleContainer& mypc,
328333
if (!buffer[i].isDefined())
329334
{
330335
buffer[i] = pc.make_alike<amrex::PinnedArenaAllocator>();
331-
buffer[i].AddIntComp("timestamp", false);
336+
buffer[i].AddIntComp("step_scraped", false);
337+
buffer[i].AddRealComp("time_scraped", false);
332338
}
333339
auto& species_buffer = buffer[i];
334340
for (int lev = 0; lev < pc.numLevels(); ++lev)
@@ -368,12 +374,17 @@ void ParticleBoundaryBuffer::gatherParticles (MultiParticleContainer& mypc,
368374
}
369375
{
370376
WARPX_PROFILE("ParticleBoundaryBuffer::gatherParticles::filterAndTransform");
371-
const int timestamp_index = ptile_buffer.NumRuntimeIntComps()-1;
372-
const int timestep = warpx_instance.getistep(0);
377+
auto& warpx = WarpX::GetInstance();
378+
const auto dt = warpx.getdt(pti.GetLevel());
379+
auto string_to_index_intcomp = buffer[i].getParticleRuntimeiComps();
380+
const int step_scraped_index = string_to_index_intcomp.at("step_scraped");
381+
auto string_to_index_realcomp = buffer[i].getParticleRuntimeComps();
382+
const int time_scraped_index = string_to_index_realcomp.at("time_scraped");
383+
const int step = warpx_instance.getistep(0);
373384

374385
amrex::filterAndTransformParticles(ptile_buffer, ptile,
375386
predicate,
376-
CopyAndTimestamp{timestamp_index, timestep},
387+
CopyAndTimestamp{step_scraped_index,time_scraped_index, step,dt},
377388
0, dst_index);
378389
}
379390
}
@@ -393,7 +404,8 @@ void ParticleBoundaryBuffer::gatherParticles (MultiParticleContainer& mypc,
393404
if (!buffer[i].isDefined())
394405
{
395406
buffer[i] = pc.make_alike<amrex::PinnedArenaAllocator>();
396-
buffer[i].AddIntComp("timestamp", false);
407+
buffer[i].AddIntComp("step_scraped", false);
408+
buffer[i].AddRealComp("time_scraped", false);
397409
}
398410
auto& species_buffer = buffer[i];
399411
for (int lev = 0; lev < pc.numLevels(); ++lev)
@@ -444,16 +456,18 @@ void ParticleBoundaryBuffer::gatherParticles (MultiParticleContainer& mypc,
444456
WARPX_PROFILE("ParticleBoundaryBuffer::gatherParticles::resize_eb");
445457
ptile_buffer.resize(dst_index + amrex::get<0>(reduce_data.value()));
446458
}
447-
448-
const int timestamp_index = ptile_buffer.NumRuntimeIntComps()-1;
449-
const int timestep = warpx_instance.getistep(0);
450459
auto& warpx = WarpX::GetInstance();
451460
const auto dt = warpx.getdt(pti.GetLevel());
461+
auto string_to_index_intcomp = buffer[i].getParticleRuntimeiComps();
462+
const int step_scraped_index = string_to_index_intcomp.at("step_scraped");
463+
auto string_to_index_realcomp = buffer[i].getParticleRuntimeComps();
464+
const int time_scraped_index = string_to_index_realcomp.at("time_scraped");
465+
const int step = warpx_instance.getistep(0);
452466

453467
{
454468
WARPX_PROFILE("ParticleBoundaryBuffer::gatherParticles::filterTransformEB");
455469
amrex::filterAndTransformParticles(ptile_buffer, ptile, predicate,
456-
FindEmbeddedBoundaryIntersection{timestamp_index, timestep, dt, phiarr, dxi, plo}, 0, dst_index);
470+
FindEmbeddedBoundaryIntersection{step_scraped_index,time_scraped_index, step, dt, phiarr, dxi, plo}, 0, dst_index);
457471
}
458472
}
459473
}

Source/Python/Particles/PinnedMemoryParticleContainer.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,17 @@ void init_PinnedMemoryParticleContainer (py::module& m)
1515
PinnedMemoryParticleContainer,
1616
amrex::ParticleContainerPureSoA<PIdx::nattribs, 0, amrex::PinnedArenaAllocator>
1717
> pmpc (m, "PinnedMemoryParticleContainer");
18+
pmpc
19+
.def_property_readonly("real_comp_names",
20+
[](PinnedMemoryParticleContainer& pc)
21+
{
22+
return pc.getParticleComps();
23+
}
24+
)
25+
.def_property_readonly("int_comp_names",
26+
[](PinnedMemoryParticleContainer& pc)
27+
{
28+
return pc.getParticleiComps();
29+
}
30+
);
1831
}

0 commit comments

Comments
 (0)