Skip to content

Commit bdb86e4

Browse files
committed
Removed parallel max finding algorithm because it contained a bug. Changed logic of Fusion multiplier parameter to be better (updated documentation as well). Added some debug printouts.
1 parent 886fde7 commit bdb86e4

File tree

5 files changed

+117
-66
lines changed

5 files changed

+117
-66
lines changed

docs/source/usage/workflows/fusionReactions.rst

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -181,21 +181,30 @@ Simulation Parameters
181181
182182
.. note:: Fusion production multiplier (Fmult)
183183

184-
Increasing ``maxFmult`` allows more, lighter products to be created, down to
185-
the ``productMinWeighting`` threshold. Decreasing ``maxFmult`` results in fewer,
186-
heavier products. ``Fmult`` controls how the consumed reactant weight (the
184+
The ``Fmult`` parameter controls how the consumed reactant weight (the
187185
minimum of the two reactant weights in a pair) is split into multiple product
188-
macro-particles while keeping total weight conserved.
186+
macro-particles while keeping total weight conserved. Increasing ``maxFmult``
187+
allows more, lighter products to be created. Decreasing ``maxFmult`` results
188+
in fewer, heavier products.
189189

190190
Algorithm (per fused pair):
191191

192-
- Start with ``Fmult = maxFmult``
193-
- Compute ``productWeighting = minWeighting / Fmult``
194-
- If ``productWeighting < productMinWeighting``, reduce ``Fmult`` to
195-
``max(1, minWeighting / productMinWeighting)`` and recompute ``productWeighting``
192+
1. Start with ``Fmult = maxFmult``
193+
2. Calculate the base fusion probability ``P``
194+
3. Adjust ``Fmult`` to ensure valid Monte Carlo sampling:
196195

197-
The base ``productWeighting`` is then distributed across the two reactant
198-
sites and product species using the site fractions ``W₁…W₄``.
196+
- If ``P > 1.0``: Set ``Fmult = 1.0`` and ``P = 1.0`` (a warning is issued)
197+
- If ``0 < P ≤ 1.0``: Calculate the maximum allowed multiplier as
198+
``Fmult = min(maxFmult, 0.99/P)`` to ensure ``P * Fmult ≤ 0.99``
199+
200+
4. Update the fusion probability: ``P = P * Fmult``
201+
5. Compute ``productWeighting = minWeighting / Fmult``
202+
6. Distribute the ``productWeighting`` across the two reactant sites and
203+
product species using the site fractions ``W₁…W₄``
204+
205+
This approach ensures that the fusion probability remains below 1.0 (required
206+
for proper Monte Carlo sampling) while maximizing the number of product
207+
particles created per fusion event.
199208

200209
See also
201210
--------

include/picongpu/particles/fusion/InterCollision.hpp

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ namespace picongpu::particles::fusion
9494
T_Reactant2ParBox reactant2Box,
9595
T_Product1ParBox product1Box,
9696
T_Product2ParBox product2Box,
97-
IdGenerator& idGen,
97+
IdGenerator idGen,
9898
T_Mapping const mapper,
9999
T_DeviceHeapHandle deviceHeapHandle,
100100
T_RngHandle rngHandle,
@@ -187,11 +187,6 @@ namespace picongpu::particles::fusion
187187
[&]()
188188
{
189189
maxNumParticlesInCell = nppc[0];
190-
191-
if constexpr(debugFusion)
192-
{
193-
printf("worker %d: maxNumParticlesInCell = %d\n", worker.workerIdx(), maxNumParticlesInCell);
194-
}
195190
});
196191
// don't need sync
197192

@@ -500,24 +495,17 @@ namespace picongpu::particles::fusion
500495
bool const isWeightingR1Greater = (weightingR1 >= weightingR2);
501496
float_X const minWeighting = isWeightingR1Greater ? weightingR2 : weightingR1;
502497

503-
// Fusion multiplier logic
504-
float_X Fmult = maxFmult;
505-
float_X productWeighting = minWeighting / Fmult;
506-
if(productWeighting < productMinWeighting)
507-
{
508-
Fmult = std::max(1._X, minWeighting / productMinWeighting);
509-
productWeighting = minWeighting / Fmult;
510-
}
511-
512-
float3_X product1Momentum{0._X};
513-
float3_X product2Momentum{0._X};
514-
515498
// WU: doi.org/10.1063/5.0051178
516-
// P = n_min * n_a / n_ba * Fmult * minWeighting * dt * (sigma*v_rel*gamma_cm) <- inside fuse()
499+
// P = n_min * n_a / n_ba * minWeighting * dt * (Fmult*sigma*v_rel*gamma_cm) <- inside fuse()
517500
float_X const probabilityCorrectionFactor
518-
= minReactantDensity * correctionFactor[cellIdx] * Fmult * sim.pic.getDt();
501+
= minReactantDensity * correctionFactor[cellIdx] * sim.pic.getDt();
502+
503+
// Fusion multiplier - can be changed inside fuse() if probability > 1
504+
float_X Fmult = maxFmult;
519505

520506
// The actual fusion physics calculation
507+
float3_X product1Momentum{0._X};
508+
float3_X product2Momentum{0._X};
521509
T_SrcCollisionFunctor fuser = collisionFunctor;
522510
fuser().template fuse<T_Product1ParBox, T_Product2ParBox>(
523511
worker,
@@ -526,14 +514,19 @@ namespace picongpu::particles::fusion
526514
weightingR1,
527515
weightingR2,
528516
probabilityCorrectionFactor,
517+
Fmult,
529518
product1Momentum,
530519
product2Momentum,
531520
rngHandle);
532521

522+
533523
// If a reaction occurred, create the product particles
534524
if(product1Momentum != float3_X{0._X} || product2Momentum != float3_X{0._X})
535525
{
536-
weightingArray[i] += productWeighting; // no atomic needed because i is unique per thread
526+
// because we could change Fmult inside fuser (because the probability might have been >1)
527+
float_X productWeighting = minWeighting / Fmult;
528+
529+
weightingArray[i] = productWeighting; // no atomic needed because i is unique per thread
537530

538531
uint32_t freeIndex = alpaka::atomicAdd(
539532
worker.getAcc(),
@@ -618,6 +611,22 @@ namespace picongpu::particles::fusion
618611
i < chunkStart + minNumParticles && i < maxNumParticles;
619612
i += step)
620613
{
614+
// no fusion happened for this pair
615+
if(weightingArray[i] == 0._X)
616+
continue;
617+
618+
if constexpr(debugFusion)
619+
if(weightingArray[i] < 0._X){
620+
printf("Error: negative weighting in fusion reaction! weightingArray[%d] = %f\n", i, weightingArray[i]);
621+
// print fmult and weightingR1 and weightingR2
622+
float_X weightingR1 = accessor1[i % size1][weighting_];
623+
float_X weightingR2 = accessor2[i % size2][weighting_];
624+
printf("weightingR1 = %f, weightingR2 = %f\n", weightingR1, weightingR2);
625+
printf("Fmult = %f\n", maxFmult);
626+
// print the number of particles in longer list
627+
printf("size1 = %d, size2 = %d, weightingArraySize = %d\n", size1, size2, weightingArraySize);
628+
continue;
629+
}
621630
float_X const oldWeighting1 = accessor1[i % size1][weighting_];
622631
float_X const oldWeighting2 = accessor2[i % size2][weighting_];
623632

@@ -630,9 +639,37 @@ namespace picongpu::particles::fusion
630639
accessor2[i % size2][momentum_] *= accessor2[i % size2][weighting_] / oldWeighting2;
631640

632641
// if the weighting is too low or negative we remove the particle with fillGaps()
633-
accessor1[i % size1][multiMask_] = (accessor1[i % size1][weighting_] > 1e-6);
634-
accessor2[i % size2][multiMask_] = (accessor2[i % size2][weighting_] > 1e-6);
642+
accessor1[i % size1][multiMask_] = (accessor1[i % size1][weighting_] > 1e-6_X);
643+
accessor2[i % size2][multiMask_] = (accessor2[i % size2][weighting_] > 1e-6_X);
644+
645+
646+
// print i and weighting array
647+
if constexpr(debugFusion)
648+
if((((i==0 && alwaysFuseQ) || !alwaysFuseQ) || accessor1[i % size1][multiMask_]==0 || accessor2[i % size2][multiMask_]==0)){
649+
650+
printf("worker %d: cell %d, i %d, weightingArray %f, new weighting1 %f, new weighting2 %f, old weighting1 %f, old weighting2 %f, difference 1 %f, difference 2 %f\n",
651+
worker.workerIdx(),
652+
cellIdx,
653+
i,
654+
weightingArray[i],
655+
accessor1[i % size1][weighting_],
656+
accessor2[i % size2][weighting_],
657+
oldWeighting1,
658+
oldWeighting2,
659+
oldWeighting1 - accessor1[i % size1][weighting_],
660+
oldWeighting2 - accessor2[i % size2][weighting_]);
661+
662+
// print the multimask as well
663+
printf("worker %d: cell %d, i %d, multiMask1 %d, multiMask2 %d\n",
664+
worker.workerIdx(),
665+
cellIdx,
666+
i,
667+
accessor1[i % size1][multiMask_],
668+
accessor2[i % size2][multiMask_]);
669+
670+
}
635671
}
672+
worker.sync();
636673
}
637674
worker.sync();
638675

include/picongpu/particles/fusion/detail/Creation.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,8 @@ namespace picongpu::particles::fusion
399399

400400
// Assign multiMask to indicate these are product particles
401401
p1r1[multiMask_] = (W1 > tolerance) ? 1u : 0u;
402-
p1r2[multiMask_] = (W3 > tolerance) ? 1u : 0u;
403402
p2r1[multiMask_] = (W2 > tolerance) ? 1u : 0u;
403+
p1r2[multiMask_] = (W3 > tolerance) ? 1u : 0u;
404404
p2r2[multiMask_] = (W4 > tolerance) ? 1u : 0u;
405405

406406
// Assign momentum (weighted with weights) and weights to product particles
@@ -422,7 +422,7 @@ namespace picongpu::particles::fusion
422422
using UniformFloat = pmacc::random::distributions::Uniform<
423423
pmacc::random::distributions::uniform::ExcludeOne<precision::float_COLL>::Reduced>;
424424
auto rng = rngHandle.template applyDistribution<UniformFloat>();
425-
if(worker.workerIdx() == 0 && rng(worker) < 1e-6)
425+
if(worker.workerIdx() == 0 && rng(worker) < 1e-8)
426426
{
427427
printf("Charges: %f, %f, %f, %f\n", q1, q2, q3, q4);
428428
printf("Masses (A): %f, %f, %f, %f\n", m1, m2, m3, m4);

include/picongpu/particles/fusion/detail/arrayHelpers.hpp

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,20 @@ namespace picongpu::particles::fusion::detail
5252

5353
template<bool debug = false, typename T_worker, typename T_arr>
5454
DINLINE void maxArrayDestroy(T_worker const& worker, T_arr& arr, int const& size)
55-
{
56-
uint32_t pow = 1;
57-
while(pow < size)
55+
{
56+
if(worker.workerIdx() == 0)
5857
{
59-
for(uint32_t i = worker.workerIdx(); pow * (2 * i + 1) < size; i += 2 * pow * worker.numWorkers())
60-
{
61-
arr[2 * i * pow] = std::max(arr[2 * i * pow], arr[pow * (2 * i + 1)]);
62-
}
63-
pow <<= 1; //*2
64-
worker.sync();
65-
if constexpr(debug)
58+
auto maxVal = arr[0];
59+
for(int i = 1; i < size; ++i)
6660
{
67-
if(worker.workerIdx() == 0)
68-
printArray(arr);
69-
worker.sync();
61+
if (arr[i] > maxVal)
62+
{
63+
maxVal = arr[i];
64+
}
7065
}
66+
arr[0] = maxVal;
7167
}
72-
// max is now at arr[0];
68+
worker.sync();
7369
}
7470

7571
template<std::size_t... Is, std::size_t N>

include/picongpu/particles/fusion/relativistic/FusionAlgorithm.hpp

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -199,23 +199,6 @@ namespace picongpu::particles::fusion::relativistic
199199
float3_COLL const u1_cm = p1_cm / mP1;
200200
float3_COLL const u1_lab = u1_cm + (math::dot(V_cm, u1_cm) * factorA + gamma_cm * gamma_p1_cm) * V_cm;
201201
labMomentum1 = u1_lab * mP1;
202-
if constexpr(debugFusion)
203-
{
204-
printf(
205-
" Product 1: mass: %f, momentum: %f, %f, %f, energy: %f\n",
206-
mP0,
207-
labMomentum0[0],
208-
labMomentum0[1],
209-
labMomentum0[2],
210-
energy<float_X>(labMomentum0, mP0));
211-
printf(
212-
" Product 2: mass: %f, momentum: %f, %f, %f, energy: %f\n",
213-
mP1,
214-
labMomentum1[0],
215-
labMomentum1[1],
216-
labMomentum1[2],
217-
energy<float_X>(labMomentum1, mP1));
218-
}
219202
}
220203

221204
/**
@@ -272,6 +255,7 @@ namespace picongpu::particles::fusion::relativistic
272255
* @param weightingR1 The weighting factor of the first reactant.
273256
* @param weightingR2 The weighting factor of the second reactant.
274257
* @param probabilityFactor A factor to adjust the fusion probability.
258+
* @param Fmult A reference to a fusion multiplier.
275259
* @param mom0 The output parameter for the momentum of the first product.
276260
* @param mom1 The output parameter for the momentum of the second product.
277261
* @param rngHandle The random number generator handle.
@@ -290,6 +274,7 @@ namespace picongpu::particles::fusion::relativistic
290274
float_X weightingR1,
291275
float_X weightingR2,
292276
float_X probabilityFactor,
277+
float_X& Fmult,
293278
float3_X& mom0,
294279
float3_X& mom1,
295280
T_RngHandle& rngHandle)
@@ -324,6 +309,21 @@ namespace picongpu::particles::fusion::relativistic
324309

325310
float_X sigma_picArea = crossSection(fusionVar.E_r * convToKeV) * millibarn_to_picArea;
326311
float_X P = probabilityFactor * sigma_picArea * fusionVar.V_rel_mag * fusionVar.gamma_cm;
312+
313+
if(P > 1.0_COLL){
314+
// print warning
315+
printf(
316+
"Warning: Fusion probability exceeds 1.0 (P: %e) the process will be underestimated.\n",
317+
P);
318+
P= 1.0_COLL;
319+
Fmult = 1.0_COLL;
320+
}
321+
else if(P>0._COLL){
322+
// limit P to not more than 0.99_COLL
323+
float_X const maxFmult = 0.99_COLL/P;
324+
Fmult = std::min(Fmult, maxFmult);
325+
P *= Fmult;
326+
}
327327

328328

329329
// print with probability 1e-8
@@ -412,6 +412,15 @@ namespace picongpu::particles::fusion::relativistic
412412
fusionVar.P<T_Product0Box, T_Product1Box>(dir);
413413
mom0 = fusionVar.P0();
414414
mom1 = fusionVar.P1();
415+
416+
if constexpr(debugFusion)
417+
{
418+
if((mom0 == float3_X{0.0_X, 0.0_X, 0.0_X})
419+
|| (mom1 == float3_X{0.0_X, 0.0_X, 0.0_X})){
420+
printf("Error: Fusion produced zero momentum products.\n");
421+
}
422+
}
423+
415424
}
416425
else
417426
{

0 commit comments

Comments
 (0)