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/tmp25lf_sz8 Running Genesis4 in /tmp/tmp25lf_sz8 /home/runner/miniconda3/envs/lume-genesis-dev/bin/genesis4 -l genesis.lat genesis4.in
--------------------------------------------- GENESIS - Version 4.6.11 has started... Compile info: Compiled by conda at 2025-12-03 17:39:36 [UTC] from Git Commit ID: b0f5539b491835b483ec1b6fa143ea4209374edc Starting Time: Thu Jan 15 23:26:49 2026 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: Thu Jan 15 23:26:49 2026 Total Wall Clock Time: 0.279312 seconds ------------------------------------- Success - execution took 1.47s.
G.input.main.setup.delz
0.026
print(G.output.run.output_log)
--------------------------------------------- GENESIS - Version 4.6.11 has started... Compile info: Compiled by conda at 2025-12-03 17:39:36 [UTC] from Git Commit ID: b0f5539b491835b483ec1b6fa143ea4209374edc Starting Time: Thu Jan 15 23:26:49 2026 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: Thu Jan 15 23:26:49 2026 Total Wall Clock Time: 0.279312 seconds -------------------------------------
output.load_particles()
P1 = output.particles["end"]
P1.drift_to_z()
P1.plot("t", "energy")
P1
<ParticleGroup with 91904 particles at 0x7f7dcbca3620>
output.particles["end"]
<ParticleGroup with 91904 particles at 0x7f7dcbca3620>
Check the charge
P1.charge
np.float64(9.973150081144013e-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 0x7f7dcb4e1010>]
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/tmpl2_6nm73 Running Genesis4 in /tmp/tmpl2_6nm73 /home/runner/miniconda3/envs/lume-genesis-dev/bin/genesis4 -l genesis.lat genesis4.in
--------------------------------------------- GENESIS - Version 4.6.11 has started... Compile info: Compiled by conda at 2025-12-03 17:39:36 [UTC] from Git Commit ID: b0f5539b491835b483ec1b6fa143ea4209374edc Starting Time: Thu Jan 15 23:26:52 2026 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: Thu Jan 15 23:26:53 2026 Total Wall Clock Time: 0.373453 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.11 has started... Compile info: Compiled by conda at 2025-12-03 17:39:36 [UTC] from Git Commit ID: b0f5539b491835b483ec1b6fa143ea4209374edc Starting Time: Thu Jan 15 23:26:52 2026 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: Thu Jan 15 23:26:53 2026 Total Wall Clock Time: 0.373453 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='Thu Jan 15 23:26:52 2026\n',
user='runner',
version=OutputMetaVersion(
extra={},
build_info='Compiled by conda at 2025-12-03 17:39:36 [UTC] from Git Commit ID: b0f5539b491835b483ec1b6fa143ea4209374edc',
major=4.0,
minor=6.0,
revision=11.0,
),
cwd='/tmp/tmpl2_6nm73',
mpisize=1.0,
)
output.load_particles()
P1 = output.particles["end"]
P1.drift_to_z()
P1.plot("t", "energy")
P1
<ParticleGroup with 128000 particles at 0x7f7dcb4fa990>
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 0x7f7dc3ca5bd0>
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/tmpxxycfdiz Running Genesis4 in /tmp/tmpxxycfdiz /home/runner/miniconda3/envs/lume-genesis-dev/bin/genesis4 -l genesis.lat genesis4.in
--------------------------------------------- GENESIS - Version 4.6.11 has started... Compile info: Compiled by conda at 2025-12-03 17:39:36 [UTC] from Git Commit ID: b0f5539b491835b483ec1b6fa143ea4209374edc Starting Time: Thu Jan 15 23:27:48 2026 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.733 Length for Matching (microns): 99.733 Energy (MeV): 536.484 Norm. Emittance in x (micron): 1.00994 Norm. Emittance in y (micron): 1.00204 Beta Function in x (m): 10.01 Beta Function in y (m): 14.9625 Alpha Function in x : -0.935385 Alpha Function in y : 1.21661 Beam center in x (micron): 0.250069 Beam center in y (micron): 0.375809 Beam center in px : 2.72453e-08 Beam center in py : -1.05295e-08 Sorting external distribution... Global Sorting: Slicelength: 0 - Send backwards for theta < 4.98665e-07 - Send forward for theta > 9.94013e-05 *** Non-matching PArticle Transfar: Rank: 0 Deleted: 0 Forward: 830 Backward: 9 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: Thu Jan 15 23:27:49 2026 Total Wall Clock Time: 1.00336 seconds ------------------------------------- Success - execution took 22.28s.
output.run
RunInfo( error_reason='', run_script='/home/runner/miniconda3/envs/lume-genesis-dev/bin/genesis4 -l genesis.lat genesis4.in', run_time=22.284550974000013, )
output.load_particles()
P2 = output.particles["end"]
P2.z
array([9.80831411e-09, 3.77887088e-08, 6.94278333e-08, ...,
9.99440595e-05, 9.99688483e-05, 9.99940901e-05], shape=(512000,))
P2.drift_to_z()
P2.plot("t", "energy")
P2
<ParticleGroup with 512000 particles at 0x7f7dc3e87530>
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 0x7f7dc3ca5bd0>
os.remove(DIST_FILE)