@@ -48,14 +48,18 @@ namespace picongpu::particles::atomicPhysics::ionizationPotentialDepression
4848 float_X debyeLength;
4949 // unitless
5050 float_X zStar;
51+ // 1/unit_length^3
52+ float_X freeElectronDensity;
5153
5254 constexpr StewartPyattSuperCellConstantInput (
53- float_X temperatureTimesk_Boltzman_i,
54- float_X debyeLength_i,
55- float_X zStar_i)
55+ float_X const temperatureTimesk_Boltzman_i,
56+ float_X const debyeLength_i,
57+ float_X const zStar_i,
58+ float_X const freeElectronDensity_i)
5659 : temperatureTimesk_Boltzman(temperatureTimesk_Boltzman_i)
5760 , debyeLength(debyeLength_i)
5861 , zStar(zStar_i)
62+ , freeElectronDensity(freeElectronDensity_i)
5963 {
6064 }
6165 };
@@ -69,7 +73,7 @@ namespace picongpu::particles::atomicPhysics::ionizationPotentialDepression
6973 * @tparam T_TemperatureFunctor term A to average over for all macro particles according to equi-partition theorem,
7074 * average(A) = k_B * T, must follow
7175 */
72- template <typename T_TemperatureFunctor>
76+ template <typename T_TemperatureFunctor, bool T_useSCFLYextendedStewartPyattIPD >
7377 struct StewartPyattIPD : IPDModel
7478 {
7579 using SuperCellConstantInput = detail::StewartPyattSuperCellConstantInput;
@@ -143,17 +147,23 @@ namespace picongpu::particles::atomicPhysics::ionizationPotentialDepression
143147 = std::make_unique<s_IPD::localHelperFields::DebyeLengthField<picongpu::MappingDesc>>(mappingDesc);
144148 dataConnector.consume (std::move (debyeLengthField));
145149
150+ // local k_Boltzman * Temperature field, in eV
151+ auto temperatureEnergyField
152+ = std::make_unique<s_IPD::localHelperFields::TemperatureEnergyField<picongpu::MappingDesc>>(
153+ mappingDesc);
154+ dataConnector.consume (std::move (temperatureEnergyField));
155+
146156 // z^star IPD input field, z^star = = average(q^2) / average(q) ;for q charge number of ion, unitless,
147157 // not weighted
148158 auto zStarField
149159 = std::make_unique<s_IPD::localHelperFields::ZStarField<picongpu::MappingDesc>>(mappingDesc);
150160 dataConnector.consume (std::move (zStarField));
151161
152- // local k_Boltzman * Temperature field, in eV
153- auto temperatureEnergyField
154- = std::make_unique<s_IPD::localHelperFields::TemperatureEnergyField <picongpu::MappingDesc>>(
162+ // unit: 1/unit_length^3
163+ auto freeElectronDensityField
164+ = std::make_unique<s_IPD::localHelperFields::FreeElectronDensityField <picongpu::MappingDesc>>(
155165 mappingDesc);
156- dataConnector.consume (std::move (temperatureEnergyField ));
166+ dataConnector.consume (std::move (freeElectronDensityField ));
157167 // @}
158168 }
159169
@@ -205,7 +215,7 @@ namespace picongpu::particles::atomicPhysics::ionizationPotentialDepression
205215 T_AtomicPhysicsIonSpeciesList,
206216 s_IPD::stage::ApplyIPDIonization<
207217 boost::mpl::_1,
208- StewartPyattIPD<T_TemperatureFunctor>,
218+ StewartPyattIPD<T_TemperatureFunctor, T_useSCFLYextendedStewartPyattIPD >,
209219 std::integral_constant<bool , T_SkipFinishedSuperCell>>>;
210220 ForEachIonSpeciesApplyIPDIonization{}(mappingDesc);
211221 };
@@ -220,21 +230,28 @@ namespace picongpu::particles::atomicPhysics::ionizationPotentialDepression
220230 * @param zStarBox deviceDataBox giving access to the local z^Star value, = average(q^2) / average(q),
221231 * for all local superCells, unitless, not weighted
222232 */
223- template <typename T_DebyeLengthBox, typename T_TemperatureEnergyBox, typename T_ZStarBox>
233+ template <
234+ typename T_DebyeLengthBox,
235+ typename T_TemperatureEnergyBox,
236+ typename T_ZStarBox,
237+ typename T_FreeElectronDensityBox>
224238 HDINLINE static SuperCellConstantInput getSuperCellConstantInput (
225239 pmacc::DataSpace<simDim> const superCellFieldIdx,
226240 T_DebyeLengthBox const debyeLengthBox,
227241 T_TemperatureEnergyBox const temperatureEnergyBox,
228- T_ZStarBox const zStarBox)
242+ T_ZStarBox const zStarBox,
243+ T_FreeElectronDensityBox const freeElectronDensityBox)
229244 {
230245 // eV, not weighted
231246 float_X temperatureTimesk_Boltzman = temperatureEnergyBox (superCellFieldIdx);
232247 // UNIT_LENGTH, not weighted
233248 float_X debyeLength = debyeLengthBox (superCellFieldIdx);
234249 // unitless, not weighted
235250 float_X zStar = zStarBox (superCellFieldIdx);
251+ // 1/unit_length^3
252+ float_X freeElectronDensity = freeElectronDensityBox (superCellFieldIdx);
236253
237- return SuperCellConstantInput (temperatureTimesk_Boltzman, debyeLength, zStar);
254+ return SuperCellConstantInput (temperatureTimesk_Boltzman, debyeLength, zStar, freeElectronDensity );
238255 }
239256
240257 /* * calculate ionization potential depression
@@ -255,20 +272,57 @@ namespace picongpu::particles::atomicPhysics::ionizationPotentialDepression
255272 // UNIT_ENERGY/eV
256273 constexpr float_X eV = sim.pic .get_eV ();
257274
275+ float_X const chargeState_X = static_cast <float_X>(chargeState);
276+
258277 // UNIT_MASS * UNIT_LENGTH^3 / UNIT_TIME^2 * 1/(eV * UNIT_ENERGY/eV * UNIT_LENGTH)
259278 // = unitless, not weighted
260279 float_X const K
261280 = (pmacc::math::isApproxZero (
262281 superCellConstantInput.temperatureTimesk_Boltzman * superCellConstantInput.debyeLength ))
263282 ? 0 ._X
264- : constFactor * (chargeState + 1 )
283+ : constFactor * (chargeState_X + 1 . _X )
265284 / (superCellConstantInput.temperatureTimesk_Boltzman * eV
266285 * superCellConstantInput.debyeLength );
267286
268287 // eV, not weighted
269- return superCellConstantInput.temperatureTimesk_Boltzman
270- * (math::pow ((3 * (superCellConstantInput.zStar + 1 ) * K + 1 ), 2 ._X / 3 ._X ) - 1 ._X )
271- / (2 ._X * (superCellConstantInput.zStar + 1 ._X ));
288+ float_X const stewartPyattIPD
289+ = superCellConstantInput.temperatureTimesk_Boltzman
290+ * (math::pow ((3 ._X * (superCellConstantInput.zStar + 1 ._X ) * K + 1 ._X ), 2 ._X / 3 ._X ) - 1 ._X )
291+ / (2 ._X * (superCellConstantInput.zStar + 1 ._X ));
292+
293+ float_X result = 0 ._X ;
294+
295+ // additional ipd contribution as implemented in scfly
296+ if constexpr (T_useSCFLYextendedStewartPyattIPD)
297+ {
298+ /* * @details taken directly from the scfly source code,
299+ * https://github.com/ComputationalRadiationPhysics/scfly/blob/5d9b0a997bb6688b04e1a34a7fa8db0f2657106a/code/src/scdrv.f#L3519-L3547
300+ *
301+ * no source known, Brian Marre, 2025
302+ */
303+ // (1/(1/unit_length^3))^(1/3) = unit_length
304+ float_X const ionSphereRadius = math::pow (
305+ 0 .75_X * (chargeState_X + 1 ._X ) / (picongpu::PI * superCellConstantInput.freeElectronDensity ),
306+ 1 . / 3 .);
307+
308+ // (eV * cm) * m/cm * unit_length/m = eV * unit_length
309+ constexpr float_X constFactorSCFLY = 1 .45e-7_X / sim.unit .length () * 1 .e -2_X;
310+
311+ // 1 * eV * unit_length / sqrt(unit_length^2 + unit_length^2) = eV, not weighted
312+ float_X const scfly_secondBranch
313+ = (chargeState_X + 1 ._X ) * constFactorSCFLY
314+ / math::sqrt (
315+ (ionSphereRadius / 1 .5_X) * (ionSphereRadius / 1 .5_X)
316+ + (superCellConstantInput.debyeLength ) * (superCellConstantInput.debyeLength ));
317+
318+ result = math::min (scfly_secondBranch, stewartPyattIPD);
319+ }
320+ else
321+ {
322+ result = stewartPyattIPD;
323+ }
324+
325+ return result;
272326 }
273327
274328 /* * call a kernel, appending the IPD-input to the kernel's input
@@ -290,14 +344,18 @@ namespace picongpu::particles::atomicPhysics::ionizationPotentialDepression
290344 = *dc.get <s_IPD::localHelperFields::TemperatureEnergyField<picongpu::MappingDesc>>(
291345 " TemperatureEnergyField" );
292346 auto & zStarField = *dc.get <s_IPD::localHelperFields::ZStarField<picongpu::MappingDesc>>(" ZStarField" );
347+ auto & freeElectronDensityField
348+ = *dc.get <s_IPD::localHelperFields::FreeElectronDensityField<picongpu::MappingDesc>>(
349+ " FreeElectronDensityField" );
293350
294351 PMACC_LOCKSTEP_KERNEL (T_Kernel ())
295352 .template config <T_chunkSize>(mapper.getGridDim ())(
296353 mapper,
297354 kernelInput...,
298355 debyeLengthField.getDeviceDataBox (),
299356 temperatureEnergyField.getDeviceDataBox (),
300- zStarField.getDeviceDataBox ());
357+ zStarField.getDeviceDataBox (),
358+ freeElectronDensityField.getDeviceDataBox ());
301359 }
302360 };
303361} // namespace picongpu::particles::atomicPhysics::ionizationPotentialDepression
0 commit comments