Genesis4 Particles¶
This shows examples of the various ways to input particle data into Genesis4.
import logging
import os
from math import pi, sqrt
import matplotlib.pyplot as plt
import numpy as np
from scipy.constants import c
import genesis.version4 as g4
logging.basicConfig()
# logging.getLogger("genesis").setLevel("DEBUG")
%config InlineBackend.figure_format = 'retina'
Lattice¶
Create a simple drift lattice
D1 = g4.Drift(L=1)
lattice = g4.Lattice(elements={"D1": D1, "LAT": g4.Line(elements=[D1])})
profile_gauss
¶
This profile will make a Gaussian distribition. Here we do some calculations to make the correct bunch length for a given bunch charge to provide a peak current.
PEAK_CURRENT = 1000
BUNCH_CHARGE = 100e-12
SIGMA_T = BUNCH_CHARGE / (sqrt(2 * pi) * PEAK_CURRENT)
SIGMA_Z = SIGMA_T * c
SLEN = 6 * SIGMA_Z
S0 = 3 * SIGMA_Z
SIGMA_T, SIGMA_Z, SLEN
(3.989422804014327e-14, 1.1959988684167075e-05, 7.175993210500245e-05)
main = g4.MainInput(
namelists=[
g4.Setup(
rootname="drift_test",
# lattice="LATFILE",
beamline="LAT",
gamma0=1000,
lambda0=1e-07,
delz=0.026,
seed=123456,
npart=128,
),
g4.Time(slen=SLEN),
g4.ProfileGauss(
label="beamcurrent",
c0=PEAK_CURRENT,
s0=S0,
sig=SIGMA_Z,
),
g4.Beam(
gamma=1000,
delgam=1,
current="beamcurrent",
),
g4.Track(zstop=1),
g4.Write(beam="end"),
],
)
G = g4.Genesis4(main, lattice, verbose=True)
output = G.run()
Configured to run in: /tmp/tmp513pys1t Running Genesis4 in /tmp/tmp513pys1t /home/runner/miniconda3/envs/lume-genesis-dev/bin/genesis4 -l genesis.lat genesis4.in
--------------------------------------------- GENESIS - Version 4.6.7 has started... Compile info: Compiled by conda at 2025-05-20 22:47:32 [UTC] from Git Commit ID: 88f9e172efe23614bf17667bbec6774b723d37cd Starting Time: Mon Jun 2 16:37:51 2025 MPI-Comm Size: 1 node Opened input file genesis4.in Parsing lattice file genesis.lat ... Setting up time window of 71.8 microns with 718 sample points... Adding profile with label: beamcurrent Generating input particle distribution... Running Core Simulation... Time-dependent run with 718 slices for a time window of 71.8 microns Initial analysis of electron beam and radiation field... Calculation: 0% done Writing output file... Core Simulation done. End of Track Writing particle distribution to file: end.par.h5 ...
Program is terminating... Ending Time: Mon Jun 2 16:37:51 2025 Total Wall Clock Time: 0.282958 seconds ------------------------------------- Success - execution took 1.46s.
G.input.main.setup.delz
0.026
print(G.output.run.output_log)
--------------------------------------------- GENESIS - Version 4.6.7 has started... Compile info: Compiled by conda at 2025-05-20 22:47:32 [UTC] from Git Commit ID: 88f9e172efe23614bf17667bbec6774b723d37cd Starting Time: Mon Jun 2 16:37:51 2025 MPI-Comm Size: 1 node Opened input file genesis4.in Parsing lattice file genesis.lat ... Setting up time window of 71.8 microns with 718 sample points... Adding profile with label: beamcurrent Generating input particle distribution... Running Core Simulation... Time-dependent run with 718 slices for a time window of 71.8 microns Initial analysis of electron beam and radiation field... Calculation: 0% done Writing output file... Core Simulation done. End of Track Writing particle distribution to file: end.par.h5 ... Program is terminating... Ending Time: Mon Jun 2 16:37:51 2025 Total Wall Clock Time: 0.282958 seconds -------------------------------------
output.load_particles()
P1 = output.particles["end"]
P1.drift_to_z()
P1.plot("t", "energy")
P1
<ParticleGroup with 91904 particles at 0x7f890d9bb8c0>
output.particles["end"]
<ParticleGroup with 91904 particles at 0x7f890d9bb8c0>
Check the charge
P1.charge
np.float64(9.973150081144012e-11)
profile_file
¶
LUME-Genesis automatically makes an HDF5 file with ProfileArray
.
NPTS = 100
SLEN = 100e-6
S = np.linspace(0, SLEN, NPTS)
CURRENT = np.linspace(1, 1000.0, NPTS)
plt.plot(S, CURRENT)
[<matplotlib.lines.Line2D at 0x7f890d38a5d0>]
main = g4.MainInput(
namelists=[
g4.Setup(
rootname="drift_test",
# lattice=lattice,
beamline="LAT",
gamma0=1000,
lambda0=1e-07,
delz=0.026,
seed=123456,
npart=128,
),
g4.Time(slen=SLEN),
g4.ProfileArray(label="beamcurrent", xdata=S, ydata=CURRENT),
g4.Beam(
gamma=1000,
delgam=1,
current="beamcurrent",
ex=1e-06,
ey=1e-06,
betax=7.910909406464387,
betay=16.881178621346898,
alphax=-0.7393217413918415,
alphay=1.3870723536888105,
),
g4.Track(zstop=1),
g4.Write(beam="end"),
]
)
G = g4.Genesis4(main, lattice, verbose=True)
output = G.run()
Configured to run in: /tmp/tmpou6b6xgh Running Genesis4 in /tmp/tmpou6b6xgh /home/runner/miniconda3/envs/lume-genesis-dev/bin/genesis4 -l genesis.lat genesis4.in
--------------------------------------------- GENESIS - Version 4.6.7 has started... Compile info: Compiled by conda at 2025-05-20 22:47:32 [UTC] from Git Commit ID: 88f9e172efe23614bf17667bbec6774b723d37cd Starting Time: Mon Jun 2 16:37:54 2025 MPI-Comm Size: 1 node Opened input file genesis4.in Parsing lattice file genesis.lat ... Setting up time window of 100 microns with 1000 sample points... Adding profile with label: beamcurrent Generating input particle distribution... Running Core Simulation... Time-dependent run with 1000 slices for a time window of 100 microns Initial analysis of electron beam and radiation field... Calculation: 0% done Writing output file... Core Simulation done. End of Track Writing particle distribution to file: end.par.h5 ...
Program is terminating... Ending Time: Mon Jun 2 16:37:54 2025 Total Wall Clock Time: 0.377004 seconds ------------------------------------- Success - execution took 1.56s.
Inspect the input and output¶
print(main.to_genesis())
&setup rootname = drift_test lattice = genesis.lat beamline = LAT gamma0 = 1000.0 lambda0 = 1e-07 delz = 0.026 seed = 123456 npart = 128 &end &time slen = 0.0001 &end &profile_file label = beamcurrent xdata = ProfileArray_0.h5/x ydata = ProfileArray_0.h5/y &end &beam gamma = 1000.0 delgam = 1.0 current = @beamcurrent ex = 1e-06 ey = 1e-06 betax = 7.910909406464387 betay = 16.881178621346898 alphax = -0.7393217413918415 alphay = 1.3870723536888105 &end &track zstop = 1.0 &end &write beam = end &end
print(lattice.to_genesis())
D1: drift = {l=1.0}; LAT: LINE = {D1};
print(output.run.output_log)
--------------------------------------------- GENESIS - Version 4.6.7 has started... Compile info: Compiled by conda at 2025-05-20 22:47:32 [UTC] from Git Commit ID: 88f9e172efe23614bf17667bbec6774b723d37cd Starting Time: Mon Jun 2 16:37:54 2025 MPI-Comm Size: 1 node Opened input file genesis4.in Parsing lattice file genesis.lat ... Setting up time window of 100 microns with 1000 sample points... Adding profile with label: beamcurrent Generating input particle distribution... Running Core Simulation... Time-dependent run with 1000 slices for a time window of 100 microns Initial analysis of electron beam and radiation field... Calculation: 0% done Writing output file... Core Simulation done. End of Track Writing particle distribution to file: end.par.h5 ... Program is terminating... Ending Time: Mon Jun 2 16:37:54 2025 Total Wall Clock Time: 0.377004 seconds -------------------------------------
output.meta
OutputMeta( extra={}, beamdumps=OutputMetaDumps(extra={}, filenames={}, intstep=array([], dtype=float64)), fielddumps=OutputMetaDumps(extra={}, filenames={}, intstep=array([], dtype=float64)), host='Undefined', input_file='&setup\n rootname = drift_test\n lattice = genesis.lat\n beamline = LAT\n gamma0 = 1000.0\n lambda0 = 1e-07\n delz = 0.026\n seed... lattice_file='D1: drift = {l=1.0};\nLAT: LINE = {D1};\n', time_stamp='Mon Jun 2 16:37:54 2025\n', user='runner', version=OutputMetaVersion( extra={}, build_info='Compiled by conda at 2025-05-20 22:47:32 [UTC] from Git Commit ID: 88f9e172efe23614bf17667bbec6774b723d37cd', major=4.0, minor=6.0, revision=7.0, ), cwd='/tmp/tmpou6b6xgh', mpisize=1.0, )
output.load_particles()
P1 = output.particles["end"]
P1.drift_to_z()
P1.plot("t", "energy")
P1
<ParticleGroup with 128000 particles at 0x7f890d3d5a90>
Resample particles for equal weights. This is neccessary when reading from a distribution file.
NSAMPLE = len(P1)
P1r = P1.resample(NSAMPLE)
P1r.plot("t", "energy")
P1r
<ParticleGroup with 128000 particles at 0x7f890147bed0>
Make a more interesting distribution from this:
P1r.pz[0 : len(P1) // 2] *= 1.1
P1r.plot("t", "energy")
ParticleGroup can write to a file for Genesis4.
Please note that LUME-Genesis will write the distribution for you prior to running Genesis4, so this step is not necessary.
DIST_FILE = "genesis4_distribution.h5"
P1r.write_genesis4_distribution(DIST_FILE, verbose=True)
Resampling 128000 weighted particles
Datasets x, xp, y, yp, t, p written to: genesis4_distribution.h5
ParticleGroup¶
Genesis4Input directly supports OpenPMD-beamphysics ParticleGroup
instances.
When using the MainInput.initial_particles
property setter, LUME-Genesis will ensure the namelist is added before the first "Track" or "Write" namelist in the main input.
It will implicitly set the import_distribution
charge and the time.slen
to the calculated time window from the particles, equivalent to the following:
import_distribution.charge = particles.charge
main.time.slen = max(
c_light * np.ptp(particles.t),
np.ptp(particles.z),
)
Additionally, the appropriate input file for Genesis4 will be written automatically when Genesis4 is executed.
main = g4.MainInput(
namelists=[
g4.Setup(
rootname="drift_test",
# lattice=full_path(LATFILE),
beamline="LAT",
gamma0=1000,
lambda0=1e-07,
delz=0.026,
seed=123456,
npart=512,
),
g4.Time(slen=0), # This will change slen to span all particles
g4.Track(zstop=1),
g4.Write(beam="end"),
],
)
G1 = g4.Genesis4(main, lattice, verbose=True, initial_particles=P1r)
output = G1.run()
WARNING:genesis.version4.input.core:Updating time namelist slen: 0.000100 (was 0.000000) to span all particles
Configured to run in: /tmp/tmptm2sxd8s Running Genesis4 in /tmp/tmptm2sxd8s /home/runner/miniconda3/envs/lume-genesis-dev/bin/genesis4 -l genesis.lat genesis4.in
--------------------------------------------- GENESIS - Version 4.6.7 has started... Compile info: Compiled by conda at 2025-05-20 22:47:32 [UTC] from Git Commit ID: 88f9e172efe23614bf17667bbec6774b723d37cd Starting Time: Mon Jun 2 16:38:33 2025 MPI-Comm Size: 1 node Opened input file genesis4.in Parsing lattice file genesis.lat ... Setting up time window of 100 microns with 1000 sample points... Importing distribution file... Charge of external distribution: 1.66782e-10 Particles in external distribution: 128000 Analysing external distribution... Analysis of the imported distribution Total Bunch Length (microns): 99.5548 Length for Matching (microns): 99.5548 Energy (MeV): 536.475 Norm. Emittance in x (micron): 1.00634 Norm. Emittance in y (micron): 1.00625 Beta Function in x (m): 10.0386 Beta Function in y (m): 14.8975 Alpha Function in x : -0.9364 Alpha Function in y : 1.2109 Beam center in x (micron): -0.549456 Beam center in y (micron): 0.0593406 Beam center in px : -6.02225e-08 Beam center in py : 3.4135e-08 Sorting external distribution... Global Sorting: Slicelength: 0 - Send backwards for theta < 4.97774e-07 - Send forward for theta > 9.94022e-05 *** Non-matching PArticle Transfar: Rank: 0 Deleted: 0 Forward: 395 Backward: 6 Generating internal particle distribution...
Running Core Simulation... Time-dependent run with 1000 slices for a time window of 100 microns Initial analysis of electron beam and radiation field... Calculation: 0% done Writing output file... Core Simulation done. End of Track Writing particle distribution to file: end.par.h5 ...
Program is terminating... Ending Time: Mon Jun 2 16:38:34 2025 Total Wall Clock Time: 1.01766 seconds ------------------------------------- Success - execution took 15.16s.
output.run
RunInfo( error_reason='', run_script='/home/runner/miniconda3/envs/lume-genesis-dev/bin/genesis4 -l genesis.lat genesis4.in', run_time=15.160122841999964, )
output.load_particles()
P2 = output.particles["end"]
P2.z
array([1.37818754e-08, 4.49490871e-08, 7.63733781e-08, ..., 9.99259177e-05, 9.99504745e-05, 9.99753343e-05], shape=(512000,))
P2.drift_to_z()
P2.plot("t", "energy")
P2
<ParticleGroup with 512000 particles at 0x7f890d46c5f0>
P2.plot("weight", bins=100)
Notice that importdistribution
is filled in:
print(G1.input.to_genesis())
# Main input &setup rootname = drift_test lattice = genesis.lat beamline = LAT gamma0 = 1000.0 lambda0 = 1e-07 delz = 0.026 seed = 123456 npart = 512 &end &time slen = 9.999850899316347e-05 &end &importdistribution file = initial_particles.h5 charge = 1.667822143811236e-10 &end &track zstop = 1.0 &end &write beam = end &end # Lattice D1: drift = {l=1.0}; LAT: LINE = {D1};
Cleanup¶
G1.input.initial_particles
<ParticleGroup with 128000 particles at 0x7f890147bed0>
os.remove(DIST_FILE)