diff --git a/include/picongpu/particles/atomicPhysics/kernel/UpdateElectricField.kernel b/include/picongpu/particles/atomicPhysics/kernel/UpdateElectricField.kernel new file mode 100644 index 0000000000..99ddffd57c --- /dev/null +++ b/include/picongpu/particles/atomicPhysics/kernel/UpdateElectricField.kernel @@ -0,0 +1,130 @@ +/* Copyright 2024 Brian Marre + * + * This file is part of PIConGPU. + * + * PIConGPU is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PIConGPU is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PIConGPU. + * If not, see . + */ + +#pragma once + +#include "picongpu/defines.hpp" +#include "picongpu/particles/atomicPhysics/KernelIndexation.hpp" + +#include + +#include + +namespace picongpu::particles::atomicPhysics::kernel +{ + /** update electric field due to field ionization energy use + * + * @tparam T_numberAtomicPhysicsIonSpecies specialization template parameter used to prevent compilation of all + * atomicPhysics kernels if no atomic physics species is present. + */ + template + struct UpdateElectricFieldKernel + { + /** call operator + * + * called by UpdateElectricField atomic physics sub-stage + * + * @param worker object containing the device and block information, passed by PMACC_KERNEL call + * @param areMapping mapping of blockIndex to block superCell index + * @param timeRemainingBox deviceDataBox giving access to the time remaining in the atomicPhysics step for each + * local superCell + * @param eFieldBox deviceDataBox giving access to eField values for all local superCells + * @param fieldEnergyUseCacheBox deviceDataBox giving access to the field energy use cache for each local + * superCell + */ + template< + typename T_Worker, + typename T_AreaMapping, + typename T_TimeRemainingBox, + typename T_EFieldBox, + typename T_FieldEnergyUseCacheBox> + HDINLINE void operator()( + T_Worker const& worker, + T_AreaMapping const areaMapping, + T_TimeRemainingBox const timeRemainingBox, + T_EFieldBox eFieldBox, + T_FieldEnergyUseCacheBox const fieldEnergyUseCacheBox) const + { + auto const superCellIdx = KernelIndexation::getSuperCellIndex(worker, areaMapping); + auto const superCellFieldIdx = KernelIndexation::getSuperCellFieldIndex(worker, areaMapping, superCellIdx); + + auto const timeRemaining = timeRemainingBox(superCellFieldIdx); + if(timeRemaining <= 0._X) + return; + + T_FieldEnergyUseCache const& eFieldEnergyUseCache = fieldEnergyUseCacheBox(superCellFieldIdx); + DataSpace const superCellCellOffset = superCellIdx * picongpu::SuperCellSize::toRT(); + + auto forEachCell = pmacc::lockstep::makeForEach(worker); + forEachCell( + [&worker, + &superCellCellOffset, + &eFieldBox, + &eFieldEnergyUseCache, + &sharedResourcesOverSubscribed, + &rejectionProbabilityCache](uint32_t const linearCellIndex) + { + DataSpace const localCellIndex + = pmacc::math::mapToND(picongpu::SuperCellSize::toRT(), static_cast(linearCellIndex)); + DataSpace const cellIndex = localCellIndex + superCellCellOffset; + + // ((sim.unit.mass() * sim.unit.length())/(sim.unit.time()^2 * sim.unit.charge()))^2 + auto const oldEFieldVector = eFieldBox(cellIndex); + auto const eFieldNormSquared = pmacc::math::l2norm2(oldEFieldVector); + + /// @note zero field also means at most zero energy use + if(eFieldNormSquared <= 0) + return; + + // eV * 1 = eV * sim.unit.energy()/sim.unit.energy() = (ev / sim.unit.energy()) * sim.unit.energy() + // sim.unit.energy() + float_X const eFieldEnergyUse + = picongpu::sim.pic.get_eV() * eFieldEnergyUseCache.energyUsed(linearCellIndex); + + // sim.unit.charge()^2 * sim.unit.time()^2 / (sim.unit.mass() * sim.unit.length()^3) + // * sim.unit.length()^3 + // = sim.unit.charge()^2 * sim.unit.time()^2 / sim.unit.mass() + constexpr float_X eps0HalfTimesCellVolume + = (picongpu::sim.pic.getEps0() / 2._X) * picongpu::sim.pic.getCellSize().productOfComponents(); + + /* (((sim.unit.mass() * sim.unit.length())/(sim.unit.time()^2 * sim.unit.charge()))^2 + * - sim.unit.energy() / (sim.unit.charge()^2 * sim.unit.time()^2 / sim.unit.mass()))^1/2 + * = (((sim.unit.mass() * sim.unit.length())/(sim.unit.time()^2 * sim.unit.charge()))^2 + * - (sim.unit.mass() * sim.unit.length()^2/sim.unit.time()^2 + * / (sim.unit.charge()^2 * sim.unit.time()^2 / sim.unit.mass())) + * )^1/2 + * = (((sim.unit.mass() * sim.unit.length())/(sim.unit.time()^2 * sim.unit.charge()))^2 + * - (sim.unit.mass()^2 * sim.unit.length()^2/(sim.unit.time()^4 * sim.unit.charge()^2)) + * )^1/2 + * = ((sim.unit.mass()^2 * sim.unit.length()^2)/(sim.unit.time()^4 * sim.unit.charge()^2) + * - (sim.unit.mass()^2 * sim.unit.length()^2/(sim.unit.time()^4 * sim.unit.charge()^2)) + * )^1/2 + * = ((sim.unit.mass()^2 * sim.unit.length()^2)/(sim.unit.time()^4 * sim.unit.charge()^2))^1/2 + * = (sim.unit.mass() * sim.unit.length())/(sim.unit.time()^2 * sim.unit.charge()) + * = sim.unit.eField() + */ + // sim.unit.eField() + float_X const newEFieldNorm + = pmacc::math::sqrt(eFieldNormSquared - eFieldEnergyUse / eps0HalfTimesCellVolume); + + eFieldBox(cellIndex) = oldEFieldVector / pmacc::math::sqrt(eFieldNormSquared) * newEFieldNorm; + }); + } + }; +} // namespace picongpu::particles::atomicPhysics::kernel diff --git a/include/picongpu/particles/atomicPhysics/stage/CheckForOverSubscription.hpp b/include/picongpu/particles/atomicPhysics/stage/CheckForOverSubscription.hpp index cf708f5202..54323bb524 100644 --- a/include/picongpu/particles/atomicPhysics/stage/CheckForOverSubscription.hpp +++ b/include/picongpu/particles/atomicPhysics/stage/CheckForOverSubscription.hpp @@ -26,6 +26,7 @@ #include "picongpu/fields/FieldE.hpp" #include "picongpu/particles/atomicPhysics/electronDistribution/LocalHistogramField.hpp" #include "picongpu/particles/atomicPhysics/kernel/CheckForOverSubscription.kernel" +#include "picongpu/particles/atomicPhysics/localHelperFields/FieldEnergyUseCacheField.hpp" #include "picongpu/particles/atomicPhysics/localHelperFields/RejectionProbabilityCacheField.hpp" #include "picongpu/particles/atomicPhysics/localHelperFields/SharedResourcesOverSubscribedField.hpp" #include "picongpu/particles/atomicPhysics/localHelperFields/TimeRemainingField.hpp" diff --git a/include/picongpu/particles/atomicPhysics/stage/UpdateElectricField.hpp b/include/picongpu/particles/atomicPhysics/stage/UpdateElectricField.hpp new file mode 100644 index 0000000000..86c33084f3 --- /dev/null +++ b/include/picongpu/particles/atomicPhysics/stage/UpdateElectricField.hpp @@ -0,0 +1,78 @@ +/* Copyright 2024 Brian Marre + * + * This file is part of PIConGPU. + * + * PIConGPU is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PIConGPU is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PIConGPU. + * If not, see . + */ + +//! @file update electric field with field ionization energy use + +#pragma once + +// need picongpu::atomicPhysics::ElectronHistogram from atomicPhysics.param +#include "picongpu/defines.hpp" +#include "picongpu/fields/FieldE.hpp" +#include "picongpu/particles/atomicPhysics/kernel/UpdateElectricField.kernel" +#include "picongpu/particles/atomicPhysics/localHelperFields/FieldEnergyUseCacheField.hpp" +#include "picongpu/particles/atomicPhysics/localHelperFields/TimeRemainingField.hpp" + +#include +#include +#include + +#include + +namespace picongpu::particles::atomicPhysics::stage +{ + /** UpdateElectricField atomic physics sub-stage + * + * remove the used field energy from the electric field by reducing the norm of the local E-Field vector to match + * the energy use + * + * @tparam T_numberAtomicPhysicsIonSpecies specialization template parameter used to prevent compilation of all + * atomicPhysics kernels if no atomic physics species is present. + */ + template + struct UpdateElectricField + { + //! call of kernel for every superCell + HINLINE void operator()(picongpu::MappingDesc const mappingDesc) const + { + // full local domain, no guards + pmacc::AreaMapping mapper(mappingDesc); + pmacc::DataConnector& dc = pmacc::Environment<>::get().DataConnector(); + + auto& timeRemainingField = *dc.get< + picongpu::particles::atomicPhysics::localHelperFields::TimeRemainingField>( + "TimeRemainingField"); + + auto& eField = *dc.get(FieldE::getName()); + + using FieldEnergyUseCacheField + = picongpu::particles::atomicPhysics::localHelperFields::FieldEnergyUseCacheField< + picongpu::MappingDesc>; + auto& fieldEnergyUseCacheField = *dc.get("FieldEnergyUseCacheField"); + + // macro for call of kernel for every superCell, see pull request #4321 + PMACC_LOCKSTEP_KERNEL( + picongpu::particles::atomicPhysics::kernel::UpdateElectricField()) + .template config(mapper.getGridDim())( + mapper, + timeRemainingField.getDeviceDataBox(), + eField.getDeviceDataBox(), + fieldEnergyUseCacheField.getDeviceDataBox()); + } + }; +} // namespace picongpu::particles::atomicPhysics::stage diff --git a/include/picongpu/simulation/stage/AtomicPhysics.x.cpp b/include/picongpu/simulation/stage/AtomicPhysics.x.cpp index e0ba1c2cc2..f48266a811 100644 --- a/include/picongpu/simulation/stage/AtomicPhysics.x.cpp +++ b/include/picongpu/simulation/stage/AtomicPhysics.x.cpp @@ -46,6 +46,7 @@ #include "picongpu/particles/atomicPhysics/stage/ResetTimeStepField.hpp" #include "picongpu/particles/atomicPhysics/stage/RollForOverSubscription.hpp" #include "picongpu/particles/atomicPhysics/stage/SpawnIonizationElectrons.hpp" +#include "picongpu/particles/atomicPhysics/stage/UpdateElectricField.hpp" #include "picongpu/particles/atomicPhysics/stage/UpdateTimeRemaining.hpp" #include "picongpu/particles/filter/filter.hpp" @@ -440,6 +441,12 @@ namespace picongpu::simulation::stage ForEachIonSpeciesSpawnIonizationElectrons{}(mappingDesc, currentStep); } + HINLINE static void updateElectricField(picongpu::MappingDesc const& mappingDesc) + { + picongpu::particles::atomicPhysics::stage::UpdateElectricField()( + mappingDesc); + } + template HINLINE static void doIPDIonization( picongpu::MappingDesc const& mappingDesc, @@ -576,6 +583,7 @@ namespace picongpu::simulation::stage recordChanges(mappingDesc); updateElectrons(mappingDesc, currentStep); + updateElectricField(mappingDesc); doIPDIonization(mappingDesc, currentStep, deviceLocalReduce); updateTimeRemaining(mappingDesc); isSubSteppingComplete = isSubSteppingFinished(mappingDesc, deviceLocalReduce);