Skip to content

Conversation

@BeyondEspresso
Copy link
Member

This draft of a PR implements PICMI capability for TWTS and TWEAC lasers. It successfully runs and builds the Laser-wakefield PICMI example using:

laser_duration = 30.0e-15 / 2.35482
pulse_init = 15.0
laser_focal_position=[
    float(numberCells[0] * cellSize[0] / 2.0),
    4.62e-5,
    float(numberCells[2] * cellSize[2] / 2.0),
]
laser = picmi.TWTSLaser(
    wavelength=0.8e-6,
    waist=1.2e-6 / 1.17741,
    duration=laser_duration,
    focal_position=laser_focal_position,
    centroid_position=[
        float(numberCells[0] * cellSize[0] / 2.0),
        -0.5 * pulse_init * laser_duration * c,
        float(numberCells[2] * cellSize[2] / 2.0),
    ],
    focus_z_offset_si=laser_focal_position[2] - float(numberCells[2] * cellSize[2] / 2.0),
    a0=8.0,
    phi=5.0 * np.pi / 180.0,
    beta0=1.0,
    polarizationAngle=45.0 * np.pi / 180.0
)

As already mentioned, this PR should be still considered a draft, but feedback is welcome!

Current working items and/or questions are:

  • Include metadata routines.
  • Is the use of the laser profile free still appropriate?
  • How shall we integrate the functionality with centroid_position and focal_position? The semantics is not very intuitive for TWTS lasers here.
  • Is the approach for conditional laser insertion at ZMIN and ZMAX good?
  • Are the schema and requirements OK or still "wild"?
  • The traditional laser profile usage pattern suggests further refactoring of the TWTSTight laser by outsourcing the unit rescaling to unitless param-structs. However, this also affects unit-tests.
  • The focus_z_offset_si parameter shows a slight clash of philosophies between a param-file-centered and a PICMI-python-centered workflow, as for PICMI it is much easier to parameterize absolute focal positions in contrast to positions relative to the center of the simulation domain.

@PrometheusPi
Copy link
Member

@BeyondEspresso The CI is seeing compile time errors with your TWTS implementation. For details please see CI log. Seems to be related to how you access fields.

@BeyondEspresso
Copy link
Member Author

BeyondEspresso commented Nov 30, 2025

@PrometheusPi Thanks, I fixed a compile-time bug in the 2D branch.
if constexpr(simDim == DIM3) laserOrigin.z() += float_X(focus_z_offset_SI / cellDimensions.z());
became
if constexpr(simDim == DIM3) laserOrigin[simDim - 1] += float_X(focus_z_offset_SI / cellDimensions[simDim - 1]);

@BeyondEspresso BeyondEspresso force-pushed the topic-twtstight-add-picmi-support branch from cc199e8 to d317d30 Compare November 30, 2025 22:56
Copy link
Member

@PrometheusPi PrometheusPi left a comment

Choose a reason for hiding this comment

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

Minor changes required and some questions.

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.

"exclusiveMinimum": 0
},
"pulse_init": {
"description": " The laser pulse will be initialized pulse_init times of the pulse_duration_si",
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"description": " The laser pulse will be initialized pulse_init times of the pulse_duration_si",
"description": "The laser pulse will be initialized pulse_init times of the pulse_duration_si",

},
"pulse_duration_si": {
"type": "number",
"description": "sigma of std. gauss for intensity (E^2) [s]",
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"description": "sigma of std. gauss for intensity (E^2) [s]",
"description": "sigma of std. Gauss for intensity (E^2) [s]",

}
},
"huygens_surface_positions": {
"description": "position of the huygens surface relative from domain boundaries",
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"description": "position of the huygens surface relative from domain boundaries",
"description": "position of the Huygens surface relative from domain boundaries",

Copy link
Member

Choose a reason for hiding this comment

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

also fix it the original schema you copied them from.

- a0 : float, optional
Normalized vector potential at focus [dimensionless].
Specify either a0 or E0 (E0 takes precedence).
Copy link
Member

Choose a reason for hiding this comment

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

Does E0 take precedence, or is this an outdated comment? I added throwing an error if both were given for the Gauss and Dispersive pulse.

Copy link
Member

Choose a reason for hiding this comment

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

It is outdated. See here:

if (E0 is not None) and (a0 is not None):
raise ValueError("Only one of E0 or a0 should be specified. You set both.")

Please update the comment.

Comment on lines 135 to 136
# def check(self):
# self._validate_common_properties()
Copy link
Member

Choose a reason for hiding this comment

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

Please remove commented out code.

# self._validate_common_properties()
self.pulse_init = self._compute_pulse_init()

"""PICMI object for TWTS Laser"""
Copy link
Member

Choose a reason for hiding this comment

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

Why do you have this comment (string) at the bottom of the code?

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 was how it was structured for other PICMI lasers. However, I had to remove the general laser checks as they do not apply for TWTS lasers. These assume implicitly that lasers enter via the YMIN-plane and thus do not apply here.

Comment on lines 155 to 164
static constexpr float_64 FOCUS_Y_SI = focus_position[1];//56.0e-6;
static constexpr float_64 FOCUS_Z_OFFSET_SI = {{{focus_z_offset_si}}};//8.0e-6;
static constexpr float_64 PHI = {{{phi}}};//5.0 * (PI / 180.);
static constexpr float_64 BETA_0 = 1.0;//{{{beta0}}};
static constexpr float_64 TDELAY = {{{time_offset_si}}};//124.535e-6 / sim.si.getSpeedOfLight();
static constexpr bool AUTO_TDELAY = false;
static constexpr float_X Polarization = {{{polarizationAngle}}};//45.10716816968629 * (PI / 180.);
static constexpr float_X windowStart = 0.0;
static constexpr float_X windowEnd = 10.0e+20;
static constexpr float_X windowLength = 10000.0;//10000.0;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
static constexpr float_64 FOCUS_Y_SI = focus_position[1];//56.0e-6;
static constexpr float_64 FOCUS_Z_OFFSET_SI = {{{focus_z_offset_si}}};//8.0e-6;
static constexpr float_64 PHI = {{{phi}}};//5.0 * (PI / 180.);
static constexpr float_64 BETA_0 = 1.0;//{{{beta0}}};
static constexpr float_64 TDELAY = {{{time_offset_si}}};//124.535e-6 / sim.si.getSpeedOfLight();
static constexpr bool AUTO_TDELAY = false;
static constexpr float_X Polarization = {{{polarizationAngle}}};//45.10716816968629 * (PI / 180.);
static constexpr float_X windowStart = 0.0;
static constexpr float_X windowEnd = 10.0e+20;
static constexpr float_X windowLength = 10000.0;//10000.0;
static constexpr float_64 FOCUS_Y_SI = focus_position[1];
static constexpr float_64 FOCUS_Z_OFFSET_SI = {{{focus_z_offset_si}}}
static constexpr float_64 PHI = {{{phi}}};
static constexpr float_64 BETA_0 = {{{beta0}}};
static constexpr float_64 TDELAY = {{{time_offset_si}}};
static constexpr bool AUTO_TDELAY = false;
static constexpr float_X Polarization = {{{polarizationAngle}}};
static constexpr float_X windowStart = 0.0;
static constexpr float_X windowEnd = 10.0e+20;
static constexpr float_X windowLength = 10000.0;

There was some commented-out code left and I think beta0was never applied.

Comment on lines +36 to +39
constexpr float_X a0 = 0.3635819;
constexpr float_X a1 = 0.4891775;
constexpr float_X a2 = 0.1365995;
constexpr float_X a3 = 0.0106411;
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.

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.

@PrometheusPi PrometheusPi added PICMI pypicongpu and picmi related component: core in PIConGPU (core application) labels Dec 1, 2025
@PrometheusPi PrometheusPi added this to the 0.9.0 / next stable milestone Dec 1, 2025
@chillenzer chillenzer added the CI:no-compile CI is skipping compile/runtime tests but runs PICMI tests label Dec 1, 2025
Comment on lines 162 to 164
static constexpr float_X windowStart = 0.0;
static constexpr float_X windowEnd = 10.0e+20;
static constexpr float_X windowLength = 10000.0;//10000.0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are there hardcoded numbers here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Only for initial testing, I will remove them an add these to the parameters.

else:
self.phiPos = False
self.beta0 = beta0
self.phi0 = 0.0
Copy link
Contributor

Choose a reason for hiding this comment

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

What's this? How is this different from phi?

Copy link
Member Author

Choose a reason for hiding this comment

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

phi0 is the phase inherited from BaseModel and has no function for TWTSTight. I set it to zero to show what it would be for TWTSTight. However, it would be straightforward and easy to add such a capability to the TWTSTight-interface.
phi is the laser incident angle and as such a different physics quantity. I want to keep the naming to remain consistent with all my other code.

Copy link
Member

Choose a reason for hiding this comment

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

I know you will hate that, but it might be better to use laserIncidentAngle as variable name instead of phi, much less likely to cause confusion with the myriad other phi-s already in use in PICMI.
In general I prefer immediately understandable names even though they are longer.

@typeguard.typechecked
class TWTSLaser(BaseLaser):
"""
Specifies a TWTS laser
Copy link
Member

Choose a reason for hiding this comment

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

what does TWTS stand for? Please add the full length form of the acronym at least one to the PICMI class definition to ensure that other users understand the difference to the other laser models.
It would also make sense to add one ore more reference and/or a short description of the TWTS laser model.

Comment on lines +29 to +30
- wavelength: float
Central wavelength of the laser [m].
Copy link
Member

Choose a reason for hiding this comment

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

Current form is fine, but I would prefer doxygen style parameter definitions. That will make it easier to autogenerate a API documentation later.

Copy link
Member Author

@BeyondEspresso BeyondEspresso Dec 2, 2025

Choose a reason for hiding this comment

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

@BrianMarre @chillenzer We can use doxygen-style parameter definitions, but this is not yet used in our python code. Before I break some implicit design decision here, is there any style that we intend to enforce in the future?

Copy link
Member

Choose a reason for hiding this comment

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

I would argue for doxygen, no need to support two different tools for auto generating API documentation.

- phi: float
Laser incident angle [rad]
denoting the mean laser phase propagation direction
with respect to the y-axis.
Copy link
Member

Choose a reason for hiding this comment

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

magical axis definition, either requires a fixed coordinate system or should be defined relative to something like the focus movement direction.
Please remember that we may not assume that the focus moves parallel to the y-direction in PICMI, since this might not be true in other PIC-codes.

"waist_si",
"pulse_duration_si",
"focus_pos_si",
"phase",
Copy link
Member

Choose a reason for hiding this comment

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

I thought the TWTS laser does not support a phase?

@psychocoderHPC psychocoderHPC marked this pull request as draft December 2, 2025 12:44
@psychocoderHPC
Copy link
Member

I converted the PR to draft to avoid merging accidentally

@BeyondEspresso
Copy link
Member Author

@PrometheusPi @psychocoderHPC @BrianMarre @chillenzer Thanks for your feedback. In this update I included all suggestions except for the doxygen-style comments in order to avoid breaking style with the other existing python-scripts. In particular, the refactored TWEAC-FOM-benchmark now showcases reduced boiler plate code using TWTSPulse.def.

@BeyondEspresso
Copy link
Member Author

I performed 3 compilation tests on the FOM setup (fieldBackground, incidentField w/o componentwise operators, incidentField with componentwise operators). The envisioned picmi-setup also compiles.

@BeyondEspresso
Copy link
Member Author

The draft tag can now be removed.

@PrometheusPi PrometheusPi marked this pull request as ready for review December 5, 2025 10:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI:no-compile CI is skipping compile/runtime tests but runs PICMI tests component: core in PIConGPU (core application) PICMI pypicongpu and picmi related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants