Example of python source codes
Refer to the following python source codes to get started with simplex-ui.
The sample files are available here In the 1st example, SIMPLEX is launched in the interactive mode with the Chrome browser, and the calculation simulations are repeated with three different photon energies. Then, the results are plotted to see how the gain curves change. Finally, the comparative data is exported as an ASCII file.
Example 1
SASE FEL of 10-keV radiation and plot the gain curve
visualize the growth of the radiation profile
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start()
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
# export radiation profile data (longer time needed)
simplex.Set("datadump", "temporal", True)
simplex.Set("datadump", "spectral", True)
simplex.Set("datadump", "spatial", True)
simplex.Set("datadump", "angular", True)
# start simulation with an output file of "./output/sample1.json"
simplex.StartSimulation(folder="./output", prefix="sample1", serial=-1)
# plot the gain curve (pulse energy vs. undulator length)
simplex.PostProcess.PlotGainCurve("Pulse Energy")
# switch to semi-log scale
simplex.PostProcess.PlotScale(y="log")
# plot in a new window
simplex.PostProcess.DuplicatePlot("Gain Curve: Pulse Energy vs Undulator Length")
# visualize the growth of the temporal and spectral profiles of radiation
simplex.PostProcess.TemporalProfile()
simplex.PostProcess.MultiPlot("Photon Flux")
simplex.PostProcess.MultiPlotCols(2)
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.DuplicatePlot("Growth of Temporal and Spectral Profiles")
# visualize the growth of the spatial and angular profiles of radiation
simplex.PostProcess.SpatialProfile()
simplex.PostProcess.MultiPlot("Angular Energy Density")
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.DuplicatePlot("Growth of Spatial and Angular Profiles")
if __name__ == "__main__":
# wait for the user's response
input("Completed. Press enter to exit. ")
# exit
simplex.Exit()
Example 2
SASE FEL of 10-keV radiation without/with the wakefield and compensation by undulator tapering
compare the three results in the post-processor and export an ASCII file
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start(src="l")
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
# start simulation with an output file of "./output/sample2-1.json"
simplex.StartSimulation(folder="./output", prefix="sample2", serial=1)
# plot the gain curve (pulse enerby vs. undulator length)
simplex.PostProcess.PlotGainCurve("Pulse Energy")
# turn on the wakefield
simplex.Set("wake", "wakeon", True)
simplex.PreProcess.Plot("Wakefield Temporal Profile")
simplex.PreProcess.DuplicatePlot("Wakefield along the electron bunch")
simplex.StartSimulation()
# compensating the energy loss by undulator tapering (with rough optimization)
simplex.Set("undulator", "taper", "Stair-Like")
simplex.Set("undulator", "opttype", "Compensate Wake")
simplex.StartSimulation()
# check the undulator tapering
simplex.PostProcess.PlotKTrend()
simplex.PostProcess.DuplicatePlot("K value variation to compensate for the energy loss")
# compare with the former results
simplex.PostProcess.SelectData("sample2-1")
simplex.PostProcess.PlotGainCurve("Pulse Energy")
simplex.PostProcess.ComparativePlot("sample2-2", "sample2-3")
simplex.PostProcess.DuplicatePlot("1/2: without/with wake, 3 compensation by tapering")
# export the comparative plot as an ASCII file
simplex.PostProcess.Export("./output/comparative.txt")
if __name__ == "__main__":
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 3
SASE FEL of 10-keV radiation with three different combinations of electron energies and undulator K values
compare the three results in the post-processor
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start()
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
EGeV = [6, 8, 10]
for E in EGeV:
simplex.Set("ebeam", "eenergy", E) # change the electron energy
simplex.Set("felprm", "e1st", 10000) # set the photon energy; K value is automatically set
print(f"K@{E}GeV = {simplex.Get("undulator", "K"):.3f}")
simplex.PreProcess.OptimizeLattice([10,10]) # adjust the beta-matching condition
simplex.StartSimulation(folder="./output", prefix="sample3", serial=E)
# plot the gain curve (pulse energy vs. undulator length)
simplex.PostProcess.SelectData("sample3-6")
simplex.PostProcess.PlotGainCurve("Pulse Energy")
simplex.PostProcess.ComparativePlot("sample3-8", "sample3-10")
simplex.PostProcess.PlotScale(y="log")
if __name__ == "__main__":
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 4
shot-to-shot flucuation of SASE FEL simulated by varying the seed number for the random number
compare the gain curve and spectral/temporal profiles
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start()
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
# export the temporal and spectral proiles
simplex.Set("datadump", "temporal", True)
# number of shots
Nshots = 5
# scan the random number seed to simulate the shot-to-shot fluctuation
simplex.Scan("condition", "randseed", 1, Nshots, interval=1, folder="./output", prefix="sample4", iniSN=1)
# data names
datanames = []
for n in range(Nshots):
datanames.append("sample4"+"_"+str(n+1))
simplex.PostProcess.SelectData("sample4_1")
# gain curves
simplex.PostProcess.PlotGainCurve("Pulse Energy")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.PlotScale(y="log")
simplex.PostProcess.DuplicatePlot("Shot-to-shot fluctuation of the gain curve")
# temporal profiles
simplex.PostProcess.TemporalProfile()
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.DuplicatePlot("Shot-to-shot fluctuation of the temporal profile")
if __name__ == "__main__":
# wait for the user's response
input("Completed. Press enter to exit. ")
# exit
simplex.Exit()
Example 5
optimization of undulator taper to maximize the pulse energy
2-stage simulation: 1st without taper, 2nd scan taper
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start()
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
# stop at the 6th segment and export the raw data
simplex.Set("datadump", "particle", True)
simplex.Set("datadump", "radiation", True)
simplex.Set("datadump", "expstep", "All Segments")
# start simulation with an output file of "./output/sample5-1.json"
simplex.StartSimulation(folder="./output", prefix="sample5", serial=1)
# import the above simulation result
simplex.Set("ebeam", "bmprofile", "SIMPLEX Output")
simplex.Set("seed", "seedprofile", "SIMPLEX Output")
simplex.Set("spxout", "spxfile", "./output/sample5-1.json")
# matching distance = 6.15 (segment interval) - 270 (#periods) x 0.018 (period) = 1.29 m
simplex.Set("spxout", "matching", 1.29)
# kill the raw data export
simplex.Set("datadump", "particle", False)
simplex.Set("datadump", "radiation", False)
# assume stair-like taper from the 1st segment
simplex.Set("undulator", "taper", "Stair-Like")
simplex.Set("undulator", "opttype", "N.A.")
simplex.Set("undulator", "initial", "1")
taperinit = [5, 6] # optimize taper starting after the 5-th or 6-th segment
Mtotal = 12 # total segments
Nt = 4 # taper scan points
datanames = []
for k in taperinit:
# use the result at the exit of the k-th segment
simplex.Set("spxout", "spxstep", k-Mtotal)
# number of segments in the taper
simplex.Set("undulator", "segments", Mtotal-k)
# data names in this run
for n in range(Nt):
datanames.append("sample5-"+str(k)+"_"+str(n))
if k%2 == 0: # starting from an odd-number segment
simplex.Set("lattice", "ltype", "FUDU (QF-U-QD-U)")
else: # starting from an even-number segment
simplex.Set("lattice", "ltype", "DUFU (QD-U-QF-U)")
simplex.PreProcess.OptimizeLattice([10,10])
# scan the taper value
simplex.Scan("undulator", "base", 0, -0.0012, Nt, folder="./output", serial=k, iniSN=0)
# check the result; compare with the former result
simplex.PostProcess.SelectData("sample5-1")
simplex.PostProcess.PlotGainCurve("Pulse Energy")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.PlotScale(y="linear")
simplex.PostProcess.DuplicatePlot("Taper optimization starting from 5/6-th segment")
if __name__ == "__main__":
# wait for the user's response
input("Completed. Press enter to exit.")
# exit
simplex.Exit()
Example 6
double-color SASE FEL by applying different K values in the first and second sections
visualize the variation of spectral profile
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start()
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
simplex.Set("undulator", "taper", "Custom")
simplex.Set("datadump", "spectral", True)
taper = []
for n in range(12):
taper.append(["0.02" if n >= 5 else "-0.02", "0"])
# first 5 segments with lower K, last 7 segments with higher K
simplex.Set("undulator", "tapercustom", taper)
# start simulation with an output file of "./output/sample6.json"
simplex.StartSimulation(folder="./output", prefix="sample6", serial=-1)
simplex.PostProcess.SpectralProfile()
simplex.PostProcess.StartAnimation()
if __name__ == "__main__":
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 7
double-pulse SASE FEL by inserting a chicane to tune the delay in between
visualize the growth of temporal profile
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start()
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
# setup chicane parameters
simplex.Set("chicane", "chicaneon", True)
simplex.Set("chicane", "delay", 10)
simplex.Set("chicane", "chpos", 5)
# expand the temporal window
simplex.Set("condition", "simrange", [-1e-6, 5e-6])
simplex.StartSimulation(folder="./output", prefix="sample7", serial=1)
# rearrange the macroparticle distribution to be consistent with the extinction of microbunching
simplex.Set("chicane", "rearrange", True)
simplex.StartSimulation(folder="./output", prefix="sample7", serial=2)
# compare the two schemes
simplex.PostProcess.SelectData("sample7-1")
simplex.PostProcess.TemporalProfile()
simplex.PostProcess.ComparativePlot("sample7-2")
simplex.PostProcess.SetSlide(-1)
if __name__ == "__main__":
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 8
evaluate the effects due to the trajectory errors on the FEL gain
visualize the variation of spectral profile
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start()
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
# ideal trajectory
simplex.StartSimulation(folder="./output", prefix="sample8", serial=0)
# trajectory error due to BPM misalignment
Ncases = 4
simplex.Set("alignment", "BPMalign", "Specify Tolerance")
# BPM alignment tolerance scanned from 5um to 20um
simplex.ScanXY("alignment", "xytol", 5e-3, 0.02, Ncases, link=True, serial=1, iniSN=1)
# data names
datanames = []
for n in range(Ncases):
datanames.append("sample8-1"+"_"+str(n+1))
# plot and compare the results
simplex.PostProcess.PlotGainCurve("Pulse Energy")
simplex.PostProcess.SelectData("sample8-0")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.DuplicatePlot("Gain reduction by the BPM misalignment")
# betatron oscillation due to injection error
simplex.Set("alignment", "BPMalign", "Ideal")
simplex.Set("dispersion", "einjec", True)
# injection error scanned from 1urad to 4urad
simplex.ScanXY("dispersion", "exyp", 1e-3, 4e-3, Ncases, link=True, serial=2, iniSN=1)
# data names
datanames = []
for n in range(Ncases):
datanames.append("sample8-2"+"_"+str(n+1))
simplex.PostProcess.PlotGainCurve("Pulse Energy")
simplex.PostProcess.SelectData("sample8-0")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.DuplicatePlot("Gain reduction by the injection error")
if __name__ == "__main__":
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 9
evaluate the effects due to misalignment in the undulator line
discrepancy in K values / phase mismatch in the drift section
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start()
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
# ideal alignment
simplex.StartSimulation(folder="./output", prefix="sample9", serial=0)
Ncases = 4
simplex.Set("alignment", "ualign", "Specify Tolerance")
# K-value tolerance scanned from 2e-3 to 8e-3
simplex.Scan("alignment", "Ktol", 2e-3, 8e-3, Ncases, link=True, serial=1, iniSN=1)
# data names
datanames = []
for n in range(Ncases):
datanames.append("sample9-1"+"_"+str(n+1))
# plot and compare the results
simplex.PostProcess.PlotGainCurve("Pulse Energy")
simplex.PostProcess.SelectData("sample9-0")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.DuplicatePlot("Gain reduction by the K value discrepancy between segments")
# slippage tolerance scanned from 30 deg. to 120 deg.
simplex.Scan("alignment", "sliptol", 30, 120, Ncases, link=True, serial=2, iniSN=1)
# data names
datanames = []
for n in range(Ncases):
datanames.append("sample9-2"+"_"+str(n+1))
simplex.PostProcess.PlotGainCurve("Pulse Energy")
simplex.PostProcess.SelectData("sample9-0")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.DuplicatePlot("Gain reduction by the mismatched phase between segments")
if __name__ == "__main__":
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 10
SASE FEL of 10-keV radiation using particle distribution data saved in an ASCII file
adjust the injection condition to eliminate positional and angular errors at the lasing slice
if __name__ == "__main__":
import simplex
simplex.Start()
# open "sample.json" in the current directory
simplex.Open("sample_sase.json")
# change "Bunch Profile" configuration
simplex.Set("ebeam", "bmprofile", "Particle Distribution")
simplex.Set("ebeam", "partfile", "./particles.dat")
# configure the format of the data file
simplex.PreProcess.LoadParticle()
simplex.PreProcess.ParticleDataFormat(unitE="gamma", pcharge=1e-15, bins=25)
# energy unit = gamma, charge/particle = 2fC, #bins/RMS bunch length = 25
simplex.PreProcess.PlotParticles(x="s (m)", y="Energy (GeV)", max=50000)
simplex.PreProcess.DuplicatePlot("E-t Phase Space")
simplex.PreProcess.PlotSliceParameter("Saturation Power")
simplex.PreProcess.DuplicatePlot("Expected Saturated Power")
# export spatial profile of radiation
simplex.Set("datadump", "spatial", True)
simplex.Set("condition", "simrange", [-2e-6,3e-6])
simplex.Set("condition", "beamlets", 2e6)
# start simulation without change
simplex.StartSimulation(folder="./output", prefix="sample10", serial=1)
simplex.PostProcess.TemporalProfile()
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.DuplicatePlot()
# adjust the injection condition; roughly evaluated by the particles distribution
simplex.Set("dispersion", "einjec", True)
simplex.Set("dispersion", "exy", [-3e-2, 0]) # horizozntal position offset 30um
simplex.Set("dispersion", "exyp", [3e-3, 0]) # horizozntal angular offset -3urad
simplex.StartSimulation(folder="./output", prefix="sample10", serial=2)
# compare the two results: gain curve
simplex.PostProcess.SelectData("sample10-1")
simplex.PostProcess.PlotGainCurve("Pulse Energy")
simplex.PostProcess.ComparativePlot("sample10-2")
simplex.PostProcess.DuplicatePlot()
# compare the two results: growth of the spatial profile
simplex.PostProcess.SpatialProfile()
simplex.PostProcess.ComparativePlot("sample10-2")
simplex.PostProcess.ComparativePlotCols(2)
simplex.PostProcess.StartAnimation()
if __name__ == "__main__":
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 11
HGHG (high gain harmonic generation) FEL
2-stage simulation: 1st modulator, 2nd radiator
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start(src="l")
# open "sample_mod.json" to simulate the particle motion in the modulator
simplex.Open("sample_mod.json")
# seed wavelength: 266 nm
simplex.Set("felprm", "l1st", 266)
# 2(h+2) = 44 particles needed to simulate up to 20th harmonic (h=20)
simplex.Set("condition", "particles", 44)
# dump the particle data at the final step
simplex.Set("datadump", "particle", True)
simplex.Set("datadump", "expstep", "Final Step")
# start simulation in the modulator
simplex.StartSimulation(folder="./output", prefix="sample11", serial=1)
# verify the energy modulation
simplex.PostProcess.SetDataProcessing("item", "Particle Motion")
simplex.PostProcess.SetDataProcessing("timerange", "Set Window")
simplex.PostProcess.SetDataProcessing("timewindow", [-1, 1])
simplex.PostProcess.SetDataProcessing("r56pp", 1e-5)
simplex.PostProcess.RunDataProcessing()
simplex.PostProcess.SymbolPlot(1)
simplex.PostProcess.DuplicatePlot("Particle distribution in E-t space with 10um R<sub>56</sub>")
# open "sample_rad.json" to simulate the harmonic generation in the radiator
simplex.Open("sample_rad.json")
# import the simulation result in the modulator
simplex.Set("ebeam", "bmprofile", "SIMPLEX Output")
simplex.Set("spxout", "spxfile", "./output/sample11-1.json")
harmonics = [10, 15, 20] # 10th, 15th and 20th harmonics
nr = 5 # number of R56 scan for each harmonic
r56 = [0, 2e-5] # scan R56 from 0 to 20um
fr = 0.01 # spectral range (+-1%) to show
for harmonic in harmonics:
simplex.Set("felprm", "l1st", 266/harmonic)
simplex.Scan("ebeam", "r56", r56[0], r56[1], nr, folder="./output", prefix="sample11", serial=harmonic)
datanames = []
for sn in range(1, nr):
datanames.append("sample11-"+str(harmonic)+"_"+str(sn))
simplex.PostProcess.SelectData("sample11-"+str(harmonic)+"_0")
simplex.PostProcess.SpectralProfile()
simplex.PostProcess.ComparativePlot(*datanames)
enh = 1240/266*harmonic # central photon energy
simplex.PostProcess.PlotRange(x=[enh*(1-fr),enh*(1+fr)])
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.DuplicatePlot("Spectrum of "+str(harmonic)+"th-harmonic radiation")
if __name__ == "__main__":
# wait for the user's response
input("Completed. Press enter to exit.")
# exit
simplex.Exit()
Example 12
EEHG (echo enhanced harmonic generation) FEL
3-stage simulation: 1st/2nd modulator, 3rd radiator
if __name__ == "__main__":
import simplex
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start(src="l")
# open "sample_mod.json" to simulate the particle motion in the modulator 1&2
simplex.Open("sample_mod.json")
# seed wavelength: 266 nm
simplex.Set("felprm", "l1st", 266)
# reduce the bunch charge and length
simplex.Set("ebeam", "bunchleng", 5e-6)
simplex.Set("ebeam", "bunchcharge", 0.05)
# reduce the seed power and lengthen its pulse length
simplex.Set("seed", "pulselen", 100)
simplex.Set("seed", "pulseenergy", 5e-7)
# disable the quiet loading scheme, because it does not
# work properly after a large longitudinal dispersion (R56)
simplex.Set("condition", "simoption", "Disable Quiet Loading")
# increase the number of beamlets (=macroparticles) to suppress artifacts
simplex.Set("condition", "beamlets", 1e7)
# shrink the simulation temporal window
simplex.Set("condition", "simrange", [-15e-6,15e-6])
# dump the particle data at the final step
simplex.Set("datadump", "particle", True)
simplex.Set("datadump", "expstep", "Final Step")
# start simulation in the 1st modulator
simplex.StartSimulation(folder="./output", prefix="sample12", serial=1)
# verify the energy modulation
simplex.PostProcess.SetDataProcessing("item", "Particle Motion")
simplex.PostProcess.SetDataProcessing("timerange", "Set Window")
simplex.PostProcess.SetDataProcessing("timewindow", [0, 0])
simplex.PostProcess.SetDataProcessing("r56pp", 5e-3)
simplex.PostProcess.RunDataProcessing()
simplex.PostProcess.SymbolPlot(1)
simplex.PostProcess.DuplicatePlot("Particle distribution at the 1st modulator exit with R<sub>56</sub>=5mm")
# 2nd modulator
# import the particle data at the 1st modulator exit with R56=5mm
simplex.Set("condition", "simoption", "N.A.") # disable any option to enable importing SIMPLEX output (next command)
simplex.Set("ebeam", "bmprofile", "SIMPLEX Output")
simplex.Set("ebeam", "r56", 5e-3)
simplex.Set("spxout", "spxfile", "./output/sample12-1.json")
# increase the seed power; other configurations are kept
simplex.Set("seed", "pulseenergy", 1e-6)
# start simulation in the 2nd modulator
simplex.StartSimulation(folder="./output", prefix="sample12", serial=2)
# verify the energy modulation
simplex.PostProcess.SetDataProcessing("r56pp", 2.4e-4)
simplex.PostProcess.RunDataProcessing()
simplex.PostProcess.SymbolPlot(1)
simplex.PostProcess.DuplicatePlot("Particle distribution at the 2nd modulator exit with R<sub>56</sub>=0.24mm")
# open "sample_rad.json" to simulate the harmonic generation in the radiator
simplex.Open("sample_rad.json")
# import the simulation result at the 2nd modulator exit
simplex.Set("ebeam", "bmprofile", "SIMPLEX Output")
simplex.Set("spxout", "spxfile", "./output/sample12-2.json")
# shrink the simulation temporal window
simplex.Set("condition", "simrange", [-15e-6,15e-6])
harmonics = [18, 23] # evaluate the 18th and 23rd harmonics
nr = 5 # number of R56 scan for each harmonic
r56 = [1.6e-4, 3.2e-4] # scan R56 from 160um to 320um
fr = 0.01 # spectral range (+-1%) to show
for harmonic in harmonics:
simplex.Set("felprm", "l1st", 266/harmonic)
simplex.Scan("ebeam", "r56", r56[0], r56[1], nr, folder="./output", prefix="sample12", serial=harmonic)
datanames = []
for sn in range(1, nr):
datanames.append("sample12-"+str(harmonic)+"_"+str(sn))
simplex.PostProcess.SelectData("sample12-"+str(harmonic)+"_0")
simplex.PostProcess.SpectralProfile()
simplex.PostProcess.ComparativePlot(*datanames)
enh = 1240/266*harmonic # central photon energy
simplex.PostProcess.PlotRange(x=[enh*(1-fr),enh*(1+fr)])
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.DuplicatePlot("Spectrum of "+str(harmonic)+"th-harmonic radiation")
if __name__ == "__main__":
# wait for the user's response
input("Completed. Press enter to exit.")
# exit
simplex.Exit()
Example 13
Oscillator FEL at 13.3 nm
perform simulation for a single turn, and use the radiation data for the next turn
if __name__ == "__main__":
import simplex
simplex.Start()
import numpy as np
import json
import math
# constants
Wavelen1eV = 1.23984247e-6
CC = 2.9979246e+8
# keys for radiation profiles
profkeys = ["Gain Curve", "Characteristics", "Temporal Profile", "Spectral Profile", "Spatial Profile", "Angular Profile"]
# compute the phase factor & mirror aperture
def SetApt(Apt, Ld, apt, wavel):
bdr = math.sqrt(dDx*dDy)
aptin = apt-bdr
for m in range(M):
mf = m if m <= M/2 else m-M
qx = mf*dDx
for n in range(N):
nf = n if n <= N/2 else n-N
qy = nf*dDy
q = (math.hypot(qx, qy)-aptin)/bdr
q = min(q, 1)
if q >= 0:
phase = -np.pi/wavel*Ld*(qx**2+qy**2) # phase advance in the distance of Ld
Apt[m][2*n] = q*math.cos(phase)
Apt[m][2*n+1] = q*math.sin(phase)
# transfer from the undulator exit to the entrance with outcoupling
def Transfer(Apt, E):
for m in range(M):
for n in range(N):
dummy = E[m][2*n]
E[m][2*n ] = E[m][2*n]*Apt[m][2*n ]-E[m][2*n+1]*Apt[m][2*n+1]
E[m][2*n+1] = dummy*Apt[m][2*n+1]+E[m][2*n+1]*Apt[m][2*n ]
# export spatial profile (for debugging)
def Export(E, isfar, suf):
Mh = M>>1
Nh = N>>1
dx = dDx if isfar else Dx
dy = dDy if isfar else Dy
with open("./temp/exy"+suf+".dat", "w") as f:
for n in range(-Nh, Nh+1):
nf = n if n >= 0 else n+N
for m in range(-Mh, Mh+1):
mf = m if m >= 0 else m+M
P = E[mf][2*nf]**2+E[mf][2*nf+1]**2
f.write(f'{m*dx*1000}\t{n*dy*1000}\t{E[mf][2*nf]}\t{E[mf][2*nf+1]}\t{P}\n')
# retrieve and store the result after a single turn
def RetrieveData(turn, fpath, profiles):
with open(fpath, "r") as f:
obj = json.load(f)
for prof in profkeys:
if prof in obj:
dim = obj[prof]["dimension"]
profiles[prof]["data"][dim-1].append(turn)
ndata = len(profiles[prof]["data"])
for n in range(dim, ndata):
data = obj[prof]["data"][n]
if dim == 1:
profiles[prof]["data"][n].append(data[-1])
else:
ndata = len(data)
nz = len(obj[prof]["data"][dim-1])
nslice = ndata//nz
sdata = np.array(data).reshape([nz, nslice])
profiles[prof]["data"][n] += sdata[-1].tolist()
# open "sample.json" in the current directory
simplex.Open("sample_felo.json")
# undulator specs.
Nu = simplex.Get("undulator", "periods")
lu = simplex.Get("undulator", "lu")
Lu = Nu*lu*1e-3
# start simulation with an output file of "./output/sample1.json"
simplex.StartSimulation(folder="./output", prefix="sample13", serial=0)
# get parameters used for the simulation
with open("./output/sample13-0.json", "r") as f:
obj = json.load(f)
config = obj["Raw Data Export"]
K = len(config["Steps (m)"]) # steps exported
L = len(config["Slices (m)"]) # total slices
MN = config["Grid Points"]
M = MN[0] # x grids
N = MN[1] # y grids
Nb = config["Beamlets"] # beamlets
Np = config["Particles/Beamlet"] # particles/beamlet
Dxy = config["Grid Intervals (m,rad)"]
Dx = Dxy[0]
Dy = Dxy[1]
dDx = Dxy[2]
dDy = Dxy[3]
wavel = Wavelen1eV/config["Central Photon Energy (eV)"]
# timing between e-bunch and FEL pulse at the undulator entrance w/o cavity detuning
sdif = -wavel*Nu
# adjust cavity detuning (shoten cavity legth by half the slippage)
sdif += wavel*Nu/2
tdif = sdif/CC*1e15 # should be give in "fs"
# modify relevant parameters for the 1st and later turns
simplex.Set("seed", "seedprofile", "SIMPLEX Output")
simplex.Set("seed", "timing", tdif)
simplex.Set("spxout", "matching", 0)
# outcoupling: aperture with a radius of 25urad
apt = 25e-6
Apt = np.full((M, 2*N), 0.0)
SetApt(Apt, -Lu, apt, wavel)
# generate an object to store data at each turn
profiles = json.loads(json.dumps(obj))
for datakey in profkeys:
if datakey in profiles:
ndata = len(profiles[datakey]["titles"])
dim = profiles[datakey]["dimension"]
profiles[datakey]["titles"][dim-1] = "Turns"
profiles[datakey]["units"][dim-1] = "-"
for n in range(dim-1, ndata):
profiles[datakey]["data"][n] = []
# save the 0th turn
RetrieveData(0, f"./output/sample13-0.json", profiles)
# data counts per step
nc = 4*2*M*N*L # bytes = 4(byte)*2(re,im)*M(x)*N(y)*L(slice)
# number of turns
turns = 30
for nt in range(turns):
print(f"-- {nt} turn started --")
fdata = f"./output/sample13-{nt%2}"
# retrive the radiation data from the binary file "*-1.fld"
x = np.fromfile(fdata+"-1.fld", dtype=np.float32, count=nc)
E = x.reshape([L, M, 2*N]) # E[l][m][2*n] real part, [2*n+1] imaginary part
# transfer the radiation field to the entrance of the undulator
for l in range(L):
print(f"Transferring wavefront: {l+1}/{L} slice", end="\r")
Transfer(Apt, E[l])
print("")
# save the data in the binary file
x = E.reshape(2*L*M*N)
x.tofile(fdata+"-1.fld")
# modify the input SIMPLEX file
simplex.Set("spxout", "spxfile", fdata+".json")
# change the shot noise condition
simplex.Set("condition", "randseed", nt+1)
# start simulation for the (nt+1)-th turn
simplex.StartSimulation(folder="./output", prefix="sample13", serial=(nt+1)%2)
# save the (nt+1)-th turn result
RetrieveData(nt+1, f"./output/sample13-{(nt+1)%2}.json", profiles)
# export the bundled data (growth of radiation vs. number of turns) and export in the post-processor
with open("./output/sample13.json", "w") as f:
json.dump(profiles, f, indent=4, sort_keys=True, separators=(',', ': '))
simplex.PostProcess.Import("./output/sample13.json")
if __name__ == "__main__":
input("Completed. Press enter to exit. ")
simplex.Exit()