Skip to content

Refactor laser implementation #5318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

steindev
Copy link
Member

@steindev steindev commented Mar 18, 2025

This refactoring straightens the implementation of laser profiles and removes confusing terms being added and later subtracted.

Major changes are:

  • redefinition of the internal coordinate system: The focus becomes the actual 'origin' of the coordinate system, while the origin variable refers to the position on the Huygens surface where the field needs to be evaluated in order to feed it into the simulation
    => This allows to implement expressions for laser fields as they are printed in publications and text books w/o additional phase and envelope terms
  • the GaussianPulse can have different waist sizes along the transverse directions now
  • the pulse-front tilt feature of the GaussianPulse is removed, because it is unknown how pulse-front tilted Laguerre-Gaussian pulses propagate (our old implementation fed some field in the simulation which propagated physically toward the focus, but this (most probably) did not correspond to the expectation for the in-focus field)
    => For pulse-front tilted standard Gaussian pulses the DispersivePulse can and should be used
  • dispersions in the DispersivePulse are applied along the polarization axis, while the other transverse axis is dispersion-free

Draft status of this PR will be removed when the following action items are performed

  • GaussianPulse:
    • Compile test simulation w/ and w/o Laguerre modes and for various propagation directions and polarizations
    • Compare results of test sim to old implementation
    • resolve possible issues
  • DispersivePulse:
    • Compile test simulation for various propagation directions and dispersion values
    • Compare results of test sim to old implementation
    • resolve possible issues

Close #5269

@steindev steindev added refactoring code change to improve performance or to unify a concept but does not change public API component: user input signals changes in user API such as .param files, .cfg syntax, etc. - changelog! changelog PR's marked with this label will be added to the changelog labels Mar 18, 2025
@steindev steindev added this to the 0.9.0 / next stable milestone Mar 18, 2025
@steindev steindev force-pushed the refactor-2025-02_Laser-Impl branch 2 times, most recently from 36092a3 to 83ef24e Compare March 19, 2025 09:48
@steindev steindev linked an issue Mar 21, 2025 that may be closed by this pull request
@steindev steindev force-pushed the refactor-2025-02_Laser-Impl branch from 83ef24e to 4568194 Compare March 21, 2025 18:38
@steindev steindev force-pushed the refactor-2025-02_Laser-Impl branch from ac4d155 to b01cbd0 Compare April 23, 2025 10:09
@steindev steindev marked this pull request as ready for review April 23, 2025 10:11
@steindev
Copy link
Member Author

@psychocoderHPC @PrometheusPi @chillenzer @ikbuibui PR is ready for review now.

It is intentionally split in three commits each of which refactors a specific part of the old implementation.

Test results have been discussed offline already and are documented in #5269.
Main takeaway from the tests: Results are equal between old and refactored implementation in various cases. Additionally, stray fields are reduced in the new implementation.

@steindev steindev force-pushed the refactor-2025-02_Laser-Impl branch from b01cbd0 to 3e9c742 Compare April 23, 2025 15:30
@steindev steindev force-pushed the refactor-2025-02_Laser-Impl branch 4 times, most recently from d1c5a2f to e7ff6a8 Compare May 11, 2025 19:21
@steindev
Copy link
Member Author

steindev commented Jun 2, 2025

ping @PrometheusPi @finnolec @psychocoderHPC @ikbuibui @chillenzer. No review within 3 weeks is actually quite bad... 😞

Copy link
Contributor

@chillenzer chillenzer left a comment

Choose a reason for hiding this comment

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

You are right, here's my first batch of review. I've yet circumvented the two main files but you might want to start with what I've got here. It's mostly questions for getting an overview.

chillenzer
chillenzer previously approved these changes Jun 3, 2025
Copy link
Contributor

@chillenzer chillenzer left a comment

Choose a reason for hiding this comment

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

Can't say much about the physical correctness but I've checked with the formulae from the given reference and it kinda makes sense to me. Some questions for my understanding and a few minor refactoring ideas are in the comments but they are mostly up to personal style I'd guess, so not blocking.

Wouldn't wanna merge myself in order to have a physicist have a look at this as well, so would you, @PrometheusPi, do the honours please?

float_X center = Unitless::SD * (Omega - Unitless::OMEGA0)
- sim.pic.getSpeedOfLight() * alpha * pos[0] / (Unitless::W0 * Unitless::OMEGA0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Could be extracted into an own function.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, could be. But I use it only once and its length is comparable to the other definitions. i.e. not as long as expandedWaveVectorX().

@chillenzer
Copy link
Contributor

PS: Generally very nice documentation. With the given references and the additional hints in the comments it was quite straightforward to follow the formulae (mostly).

@PrometheusPi
Copy link
Member

@steindev Will you first implement @chillenzer's suggestions are do you want to have more reviews at the current state?

@psychocoderHPC
Copy link
Member

@PrometheusPi please already start the review. @chillenzer mostly ask for renamings.

@chillenzer
Copy link
Contributor

Yes, definitely! Please review. My comments were at most low-priority colouring ideas concerning our new bike shed. Did you know that research has shown that bikes in a green shed are 12.657498% less likely to get stolen by people wearing a blue or yellow shirt? (If the shirt is blue and yellow, for example in stripes, the results are inconclusive and further research is needed.)

@chillenzer
Copy link
Contributor

Any movement here?

@PrometheusPi
Copy link
Member

@chillenzer soon (tomorrow)

@steindev
Copy link
Member Author

@chillenzer Thanks for your review 🙏
Your questions are answered. I have had issues with one of your requests. Let's talk offline about this.

Also, it seems I do not see any need for changes from your comments, sorry 😅
But if a certain change is important to you, I will do it. Please tell me.

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.

only minor things and questions

@@ -1,4 +1,4 @@
/* Copyright 2020-2024 Sergei Bastrakov, Finn-Ole Carstens
/* Copyright 2020-2024 Sergei Bastrakov, Finn-Ole Carstens, Klaus Steiniger
Copy link
Member

Choose a reason for hiding this comment

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

Year will be updated next year - fine with me to keep it

// unit: sim.unit.time()
static constexpr float_X INIT_TIME = static_cast<float_X>(
(Params::RAMP_INIT * Params::PULSE_DURATION_SI + Params::LASER_NOFOCUS_CONSTANT_SI)
/ sim.unit.time());
Copy link
Member

Choose a reason for hiding this comment

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

Just for my understanding: where does this get defined now?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nowhere, because it has not been used. But RAMP_INIT is used in the code further below again.

@@ -38,8 +38,9 @@ namespace picongpu
* unit: seconds */
static constexpr float_64 LASER_NOFOCUS_CONSTANT_SI = 13.34e-15;

/** The laser pulse will be initialized after TIME_DELAY_SI half of PULSE_INIT times of the
* PULSE_DURATION before and after the plateau unit: none */
/** The laser pulse will be initialized after TIME_DELAY_SI half of RAMP_INIT times of the
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 I introduced this, but the factor half of ramp-init is confusing since the variable name already states init as the up-ramp in my opinion..

Copy link
Member Author

Choose a reason for hiding this comment

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

🤔 I see what you mean...will change it

*
* unit: none
*/
static constexpr float_X SPECTRAL_SUPPORT = 6._X;
Copy link
Member

Choose a reason for hiding this comment

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

@steindev Why did the spectral support increase from 4 to 6?

Copy link
Member Author

Choose a reason for hiding this comment

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

Results of numerical experiments. See #5269.

*
* unit: none
*/
static constexpr float_64 PULSE_INIT = 17.0;
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 this change from 20 to 17?

Copy link
Member Author

Choose a reason for hiding this comment

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

Results of numerical experiments. See #5269.

/** Calculate incident field E value for the given position
* The implementation is based on a definition of the electric field E given in frequency-space domain,
* which is printed in eq. (6) of ref. [1]. Compared to the reference, higher order derivatives of the
* frequency dependent propagation angle are set to zero, i.e. theta'' = theta''' = 0.
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 the ' standing for here?

Copy link
Member Author

@steindev steindev Jun 17, 2025

Choose a reason for hiding this comment

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

Read ' as 'prime' and it means derivative with respect to frequency. Standard notation. Obviously... 😒 😉

* (Unitless::OMEGA0 * Unitless::AD * (Omega - Unitless::OMEGA0)
+ Unitless::AD * (Omega - Unitless::OMEGA0) * (Omega - Unitless::OMEGA0)
- Unitless::OMEGA0 / 6.0_X * Unitless::AD * Unitless::AD * Unitless::AD
* (Omega - Unitless::OMEGA0) * (Omega - Unitless::OMEGA0)
Copy link
Member

Choose a reason for hiding this comment

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

for this equation, I would suggest storing (Omega - Unitless::OMEGA0) in a const variable before and use it here. This avoids 6-1=5 subtractions.

Copy link
Member Author

Choose a reason for hiding this comment

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

Also did it in the other functions.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hey, 1970 just called: They are super-excited about their latest idea of Common Subexpression Elimination. They say that maybe in a distant future compilers might be capable of applying that to real-world code. I wonder how long we're gonna have to wait for this.

- sim.pic.getSpeedOfLight() * alpha * pos[0] / (Unitless::W0 * Unitless::OMEGA0);

// gaussian envelope in frequency domain
float_X const envFreqExp = -(Omega - Unitless::OMEGA0) * (Omega - Unitless::OMEGA0)
Copy link
Member

Choose a reason for hiding this comment

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

here as well

float_X alpha = expandedWaveVectorX(Omega);

// Center of a frequency's spatial distribution
float_X center = Unitless::SD * (Omega - Unitless::OMEGA0)
Copy link
Member

Choose a reason for hiding this comment

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

here as well

float_X phase = Omega * pos[0] / sim.pic.getSpeedOfLight()
+ 0.5_X * Unitless::GDD * (Omega - Unitless::OMEGA0) * (Omega - Unitless::OMEGA0)
+ Unitless::TOD / 6.0_X * (Omega - Unitless::OMEGA0) * (Omega - Unitless::OMEGA0)
* (Omega - Unitless::OMEGA0)
Copy link
Member

Choose a reason for hiding this comment

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

here as well

@steindev
Copy link
Member Author

Thanks for your review @PrometheusPi 🙏
I made changes to the code accordingly and directly squashed.

steindev added 3 commits June 18, 2025 20:32
* getCurrentTime() -> getTminusXoverC() for separable profiles
* make focus the origin of internal coordinate system
* fix language typos
* rename Unitless::w -> Unitless::OMEGA0
* delete PulseFrontTilt laser
* Perform parameter range checks in ExpRampWithPrepulse
* update, expand, and fix language typos in doc-strings
* use currentTimeOrigin & evaluate for all times
* take Unitless::TIME_DELAY into account
* apply dispersions only along polarization axis
* move doubly performed computations into common place and compute only once
* rename Unitless::w -> Unitless::OMEGA0
* decrease indentation level by namespace nesting
* Add metadata
* update, expand, and fix language typos in doc-strings
* allow for elliptic transverse shape by having different W0 per axis
* remove pulse-front tilt (use via DispersivePulse for TEM_00, for all other modes the propagation is not known anyways)
* adapt to new definition of internalCoordinates and time
* straighten expressions for field computation (implementation follows Pausch et al. paper now)
* rename Unitless::w -> Unitless::OMEGA0
@steindev steindev force-pushed the refactor-2025-02_Laser-Impl branch from 9fd531d to 4115299 Compare June 18, 2025 18:32
@steindev
Copy link
Member Author

@PrometheusPi: The latest changes are force-pushed again and run through the CI now.

Second, here is the comparison between 32bit and 64bit regarding the sqrt(sqrt(...)) discussion.

32bit:
GaussianPulse-refactor_32bit_normal-prop_2050

64bit:
GaussianPulse-refactor_64bit_normal-prop_2050

(no error in labels, really)

@PrometheusPi
Copy link
Member

PrometheusPi commented Jun 19, 2025

@steindev Thanks for providing the comparison between 32bit and 64bit. This look excellent.
Could you please also plot $|E_{32bit} - E_{64bit}| / E_{max}$?
Do you know, what causes the spike at x=[700, 800] and y~30 ?

@steindev
Copy link
Member Author

@PrometheusPi

  1. The difference plot
    32bit-vs64bit

  2. The spike: It lies directly on the incident field plane and does not propagate inside.

@PrometheusPi
Copy link
Member

Thanks @steindev Looks good.

@PrometheusPi PrometheusPi merged commit 70a6709 into ComputationalRadiationPhysics:dev Jun 20, 2025
10 checks passed
@steindev steindev deleted the refactor-2025-02_Laser-Impl branch June 20, 2025 10:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog PR's marked with this label will be added to the changelog component: user input signals changes in user API such as .param files, .cfg syntax, etc. - changelog! refactoring code change to improve performance or to unify a concept but does not change public API
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ToDo in laser refactoring incident laser profiles need improvements
4 participants