Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ namespace picongpu
floatD_X laserOrigin = precisionCast<float_X>(halfSimSize);
laserOrigin.y() = float_X(focus_y_SI / cellDimensions.y());
if constexpr(simDim == DIM3)
laserOrigin.z() += float_X(focus_z_offset_SI / cellDimensions.z());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@psychocoderHPC why does the original version seems not to work?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler does attempt a name lookup despite the compile-time condition. Since the .z() do not exist in 2D, this throws an error.

laserOrigin[simDim - 1] += float_X(focus_z_offset_SI / cellDimensions[simDim - 1]);

/* For staggered fields (e.g. Yee-grid), obtain the fractional cell index components and add
* that to the total cell indices. The physical field coordinate origin is transversally
Expand Down
179 changes: 179 additions & 0 deletions include/picongpu/fields/incidentField/profiles/TWTSPulse.def
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you need to add this file? I thought we already have a TWTS implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The one we have is only via background fields. There seems to be no incident field TWTS mainline yet. Correct @BeyondEspresso?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rest of file looks okay - I have to trust @BeyondEspresso that the equations are correct. Perhaps @steindev could have a second look at this file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BeyondEspresso Since you added now this TWTS incident field pulse, please add the option to our incidentField.param files. E.g.

* - profiles::Wavepacket<> : wavepacket with Gaussian spatial and temporal envelope profile with given
* parameters

and the examples and tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TWTS implementation can be used for both background and incidentFields. With TWTSPulse.def, I basically reduced the boiler plate code I had to include in the param-files production runs. The question here would be, whether a more extended version (not relying on the Free-profile and including metadata methods) should be included in this PR already or whether this can deferred to a later PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PrometheusPi Just to clarify: we already have a working incident field implementation in mainline. The TWEAC-FOM setup also makes use of that. This change is about reducing boiler plate code and does not change any equations of the existing TWTSTight model.

Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/* Copyright 2014-2025 Axel Huebl, Alexander Debus, Richard Pausch, Sergei Bastrakov
*
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "picongpu/fields/background/templates/twtstight/TWTSTight.hpp"

#ifndef PARAM_COMPONENTWISE
# define PARAM_COMPONENTWISE 1
#endif

namespace picongpu::fields::incidentField
{
namespace profiles
{
struct window
{
HDINLINE static float_X switchAt(
float_X const currentStep,
float_X const switchStartStep,
float_X const switchEndStep,
float_X const length)
{
/* Blackman - Nuttall window */
constexpr float_X a0 = 0.3635819;
constexpr float_X a1 = 0.4891775;
constexpr float_X a2 = 0.1365995;
constexpr float_X a3 = 0.0106411;
Comment on lines +41 to +44
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these constants? Could you add a comment what these magic numbers represent?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the window type. I should add a comment.

if(length < 0.0)
{
return float_X(1.0);
}
float_X const n_on = currentStep - switchStartStep;
float_X const n_off = switchEndStep - currentStep;
if((n_on < 0) || (n_off < 0))
{
return float_X(0.0);
}
else if((n_on >= 0) && (n_on <= length))
{
return a0 - a1 * math::cos(float_X(2.0) * PI * n_on / length / float_X(2.0))
+ a2 * math::cos(float_X(4.0) * PI * n_on / length / float_X(2.0))
- a3 * math::cos(float_X(6.0) * PI * n_on / length / float_X(2.0));
}
else if((switchEndStep >= switchStartStep) && (n_off >= 0) && (n_off <= length))
{
return a0 - a1 * math::cos(float_X(2.0) * PI * n_on / length / float_X(2.0))
+ a2 * math::cos(float_X(4.0) * PI * n_on / length / float_X(2.0))
- a3 * math::cos(float_X(6.0) * PI * n_on / length / float_X(2.0));
}
return float_X(1.0);
}
};

template<typename T_Params>
struct TWTSFunctorIncidentE
{
public:
float_X const m_currentStep;
PMACC_ALIGN(m_unitField, float3_64 const);
templates::twtstight::EField const twtsFieldE;

HINLINE TWTSFunctorIncidentE(float_X const currentStep, float3_64 const unitField)
: m_currentStep(currentStep)
, m_unitField(unitField)
, twtsFieldE(
T_Params::FOCUS_Y_SI,
T_Params::WAVE_LENGTH_SI,
T_Params::PULSE_DURATION_SI,
T_Params::W0_SI,
T_Params::PHI,
T_Params::BETA_0,
T_Params::TDELAY,
T_Params::AUTO_TDELAY,
T_Params::FOCUS_Z_OFFSET_SI,
T_Params::Polarization)
{
}

HDINLINE float3_X operator()(floatD_X const& totalCellIdx) const
{
float3_64 const invUnitField
= float3_64(1.0 / m_unitField[0], 1.0 / m_unitField[1], 1.0 / m_unitField[2]);
float3_X const amplitude = precisionCast<float_X>(T_Params::AMPLITUDE_SI * invUnitField);
return window::switchAt(
m_currentStep,
T_Params::windowStart,
T_Params::windowEnd,
T_Params::windowLength)
* amplitude * twtsFieldE(totalCellIdx, m_currentStep);
}

#if PARAM_COMPONENTWISE
template<uint32_t T_component>
HDINLINE float_X getComponent(floatD_X const& totalCellIdx) const
{
float_64 const invUnitField = 1.0 / m_unitField[T_component];
float_X const amplitude = precisionCast<float_X>(T_Params::AMPLITUDE_SI * invUnitField);
return window::switchAt(
m_currentStep,
T_Params::windowStart,
T_Params::windowEnd,
T_Params::windowLength)
* amplitude * twtsFieldE.getComponent<T_component>(totalCellIdx, m_currentStep);
}
#endif
};

template<typename T_Params>
struct TWTSFunctorIncidentB
{
public:
float_X const m_currentStep;
PMACC_ALIGN(m_unitField, float3_64 const);
templates::twtstight::BField twtsFieldB;

HINLINE TWTSFunctorIncidentB(float_X const currentStep, float3_64 const unitField)
: m_currentStep(currentStep)
, m_unitField(unitField)
, twtsFieldB(
T_Params::FOCUS_Y_SI,
T_Params::WAVE_LENGTH_SI,
T_Params::PULSE_DURATION_SI,
T_Params::W0_SI,
T_Params::PHI,
T_Params::BETA_0,
T_Params::TDELAY,
T_Params::AUTO_TDELAY,
T_Params::FOCUS_Z_OFFSET_SI,
T_Params::Polarization)
{
}

HDINLINE float3_X operator()(floatD_X const& totalCellIdx) const
{
float3_64 const invUnitField
= float3_64(1.0 / m_unitField[0], 1.0 / m_unitField[1], 1.0 / m_unitField[2]);
float3_X const amplitude = precisionCast<float_X>(T_Params::AMPLITUDE_SI * invUnitField);
return window::switchAt(
m_currentStep,
T_Params::windowStart,
T_Params::windowEnd,
T_Params::windowLength)
* amplitude * twtsFieldB(totalCellIdx, m_currentStep);
}

#if PARAM_COMPONENTWISE
template<uint32_t T_component>
HDINLINE float_X getComponent(floatD_X const& totalCellIdx) const
{
float_64 const invUnitField = 1.0 / m_unitField[T_component];
float_X const amplitude = precisionCast<float_X>(T_Params::AMPLITUDE_SI * invUnitField);
return window::switchAt(
m_currentStep,
T_Params::windowStart,
T_Params::windowEnd,
T_Params::windowLength)
* amplitude * twtsFieldB.getComponent<T_component>(totalCellIdx, m_currentStep);
}
#endif
};
} // namespace profiles
} // namespace picongpu::fields::incidentField
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@
#include "picongpu/fields/incidentField/profiles/None.def"
#include "picongpu/fields/incidentField/profiles/PlaneWave.def"
#include "picongpu/fields/incidentField/profiles/Polynom.def"
#include "picongpu/fields/incidentField/profiles/TWTSPulse.def"
#include "picongpu/fields/incidentField/profiles/Traits.hpp"
#include "picongpu/fields/incidentField/profiles/Wavepacket.def"
3 changes: 3 additions & 0 deletions include/picongpu/param/incidentField.param
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
* - profiles::DispersivePulse<> : Gaussian pulse allowing to set first-, second-, and third-order dispersion
* in focus. That is, SD, AD, GDD, and TOD, respectively.
* - profiles::Free<> : custom profile with user-provided functors to calculate incident E and B
* - profiles::Free<profiles::TWTSFunctorIncidentE<>,
* profiles::TWTSFunctorIncidentB<>>
* : Traveling-Wave Thomson Scattering laser (TWTS) with given parameters.
* - profiles::FromOpenPMDPulse<> : reads a field chunk defined in space-time domain (= (2+1)-dimensional) from
* openPMD into the simulation; requires openPMD API dependency
* - profiles::GaussianPulse<> : Pulse with Gaussian profile in all three dimensions with given parameters.
Expand Down
3 changes: 2 additions & 1 deletion lib/python/picongpu/picmi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .simulation import Simulation
from .grid import Cartesian3DGrid
from .solver import ElectromagneticSolver
from .lasers import DispersivePulseLaser, GaussianLaser, PlaneWaveLaser, FromOpenPMDPulseLaser
from .lasers import DispersivePulseLaser, GaussianLaser, PlaneWaveLaser, FromOpenPMDPulseLaser, TWTSLaser
from .species import Species
from .layout import PseudoRandomLayout, GriddedLayout
from . import constants
Expand Down Expand Up @@ -43,6 +43,7 @@
"DispersivePulseLaser",
"FromOpenPMDPulseLaser",
"GaussianLaser",
"TWTSLaser",
"PlaneWaveLaser",
"Species",
"PseudoRandomLayout",
Expand Down
10 changes: 9 additions & 1 deletion lib/python/picongpu/picmi/lasers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,13 @@
from .gaussian_laser import GaussianLaser
from .plane_wave_laser import PlaneWaveLaser
from .polarization_type import PolarizationType
from .twts_laser import TWTSLaser

__all__ = ["DispersivePulseLaser", "FromOpenPMDPulseLaser", "GaussianLaser", "PlaneWaveLaser", "PolarizationType"]
__all__ = [
"DispersivePulseLaser",
"FromOpenPMDPulseLaser",
"GaussianLaser",
"PlaneWaveLaser",
"PolarizationType",
"TWTSLaser",
]
Loading