Skip to content
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

ML surrogate example #482

Merged
merged 17 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from 12 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
1 change: 1 addition & 0 deletions docs/source/usage/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This section allows you to **download input files** that correspond to different
examples/kicker/README.rst
examples/thin_dipole/README.rst
examples/aperture/README.rst
examples/pytorch_surrogate_model/README.rst


Unit tests
Expand Down
71 changes: 71 additions & 0 deletions examples/pytorch_surrogate_model/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.. _examples-ml-surrogate:
ax3l marked this conversation as resolved.
Show resolved Hide resolved

9 Stage Laser-Plasma Accelerator Surrogate
==========================================

This example models an electron beam accelerated through nine stages of laser-plasma accelerators
with ideal plasma lenses providing the focusing between stages.
ax3l marked this conversation as resolved.
Show resolved Hide resolved
A schematic with more information can be seen in Fig. `[fig:lpa_schematic] <#fig:lpa_schematic>`__.
ax3l marked this conversation as resolved.
Show resolved Hide resolved

.. figure:: https://user-images.githubusercontent.com/10621396/289956389-c7463b99-fb56-490a-8511-22c43f45cdf8.png
:alt: [fig:lpa_schematic] Schematic of the 9 stages of laser-plasma accelerators.

[fig:lpa_schematic] Schematic of the 9 stages of laser-plasma accelerators.
ax3l marked this conversation as resolved.
Show resolved Hide resolved

The laser-plasma accelerator elements are modeled with neural network surrogates,
previously trained and included in ``models``.
The neural networks require normalized input data; the normalizations can be found in ``datasets``.

We use a 1 GeV electron beam with initial normalized rms emittance of 1 mm-mrad.

In this test, the initial and final values of :math:`\sigma_x`, :math:`\sigma_y`, :math:`\sigma_t`, :math:`\epsilon_x`, :math:`\epsilon_y`, and :math:`\epsilon_t` must agree with nominal values.

Run
---

This example can be **only** be run with **Python**:

* **Python** script: ``python3 run_ml_surrogate.py```

For `MPI-parallel <https://www.mpi-forum.org>`__ runs, prefix these lines with ``mpiexec -n 4 ...`` or ``srun -n 4 ...``, depending on the system.

.. literalinclude:: run_fodo.py
:language: python
:caption: You can copy this file from ``examples/pytorch_surrogate_model/run_ml_surrogate.py``.
ax3l marked this conversation as resolved.
Show resolved Hide resolved

Analyze
-------

We run the following script to analyze correctness:

.. dropdown:: Script ``analyze_ml_surrogate.py``

.. literalinclude:: analyze_ml_surrogate.py
:language: python
:caption: You can copy this file from ``examples/pytorch_surrogate_model/analyze_ml_surrogate.py``.

Visualize
---------

You can run the following script to visualize the beam evolution over time:

.. dropdown:: Script ``visualize_ml_surrogate.py``

.. literalinclude:: visualize_ml_surrogate.py
:language: python
:caption: You can copy this file from ``examples/pytorch_surrogate_model/visualize_ml_surrogate.py``.

.. figure:: https://user-images.githubusercontent.com/10621396/289976300-6f861d19-5a5c-42eb-9435-9f57bd2010bf.png
:alt: [fig:moments] Evolution of beam moments through 9 stage LPA via neural network surrogates.

[fig:moments] Evolution of electron beam moments through 9 stages of LPAs (via neural network surrogates).
ax3l marked this conversation as resolved.
Show resolved Hide resolved

.. figure:: https://user-images.githubusercontent.com/10621396/289956805-49e0a94a-454f-4b48-b448-7ac772edf95a.png
:alt: [fig:initial_phase] Initial phase space projections

[fig:initial_phase] Initial phase space projections going into 9 stage LPA (via neural network surrogates) simulation. Top row: spatial projections, middle row: momentum projections, bottom row: phase spaces.
ax3l marked this conversation as resolved.
Show resolved Hide resolved

.. figure:: https://user-images.githubusercontent.com/10621396/289975961-7d389864-9106-4446-8556-b0ea4bb28145.png
:alt: [fig:final_phase] Final phase space projections after 9 stage LPA (via neural network surrogates) simulation

[fig:final_phase] Final phase space projections after 9 stage LPA (via neural network surrogates) simulation. Top row: spatial projections, middle row: momentum projections, bottom row: phase spaces.
ax3l marked this conversation as resolved.
Show resolved Hide resolved
106 changes: 106 additions & 0 deletions examples/pytorch_surrogate_model/analyze_ml_surrogate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env python3
#
# Copyright 2022-2023 ImpactX contributors
# Authors: Ryan Sandberg, Axel Huebl, Chad Mitchell
# License: BSD-3-Clause-LBNL
#
# -*- coding: utf-8 -*-

import numpy as np
import openpmd_api as io
from scipy.stats import moment


def get_moments(beam):
"""Calculate standard deviations of beam position & momenta
and emittance values

Returns
-------
sigx, sigy, sigt, emittance_x, emittance_y, emittance_t
"""
sigx = moment(beam["position_x"], moment=2) ** 0.5 # variance -> std dev.
sigpx = moment(beam["momentum_x"], moment=2) ** 0.5
sigy = moment(beam["position_y"], moment=2) ** 0.5
sigpy = moment(beam["momentum_y"], moment=2) ** 0.5
sigt = moment(beam["position_t"], moment=2) ** 0.5
sigpt = moment(beam["momentum_t"], moment=2) ** 0.5

epstrms = beam.cov(ddof=0)
emittance_x = (
sigx**2 * sigpx**2 - epstrms["position_x"]["momentum_x"] ** 2
) ** 0.5
emittance_y = (
sigy**2 * sigpy**2 - epstrms["position_y"]["momentum_y"] ** 2
) ** 0.5
emittance_t = (
sigt**2 * sigpt**2 - epstrms["position_t"]["momentum_t"] ** 2
) ** 0.5

return (sigx, sigy, sigt, emittance_x, emittance_y, emittance_t)


# initial/final beam
series = io.Series("diags/openPMD/monitor.bp", io.Access.read_only)
last_step = list(series.iterations)[-1]
initial = series.iterations[1].particles["beam"].to_df()
final = series.iterations[last_step].particles["beam"].to_df()

# compare number of particles
num_particles = 10000
assert num_particles == len(initial)
assert num_particles == len(final)

print("Initial Beam:")
sigx, sigy, sigt, emittance_x, emittance_y, emittance_t = get_moments(initial)
print(f" sigx={sigx:e} sigy={sigy:e} sigt={sigt:e}")
print(
f" emittance_x={emittance_x:e} emittance_y={emittance_y:e} emittance_t={emittance_t:e}"
)

atol = 0.0 # ignored
rtol = num_particles**-0.5 # from random sampling of a smooth distribution
print(f" rtol={rtol} (ignored: atol~={atol})")

assert np.allclose(
[sigx, sigy, sigt, emittance_x, emittance_y],
[
7.488319e-07,
7.501963e-07,
9.996533e-08,
5.052374e-10,
5.130370e-10,
],
rtol=rtol,
atol=atol,
)

atol = 1.0e-6
print(f" atol~={atol}")
assert np.allclose([emittance_t], [0.0], atol=atol)

print("")
print("Final Beam:")
sigx, sigy, sigt, emittance_x, emittance_y, emittance_t = get_moments(final)
print(f" sigx={sigx:e} sigy={sigy:e} sigt={sigt:e}")
print(
f" emittance_x={emittance_x:e} emittance_y={emittance_y:e} emittance_t={emittance_t:e}"
)

atol = 0.0 # ignored
rtol = num_particles**-0.5 # from random sampling of a smooth distribution
print(f" rtol={rtol} (ignored: atol~={atol})")

assert np.allclose(
[sigx, sigy, sigt, emittance_x, emittance_y, emittance_t],
[
3.062763e-07,
2.873031e-07,
1.021142e-07,
9.090898e-12,
9.579053e-12,
2.834852e-11,
],
rtol=rtol,
atol=atol,
)
Loading
Loading