Example
Refer to the following examples to get started with simplex-ui.
Python Script Examples
The following python scripts instruct the usage of the classes and functions in simplex-ui to set the parameters and configurations and how to visualize the results of pre- and post-processing. The sample files are available here.
In the 1st example, SIMPLEX is launched in the interactive mode with the Chrome browser, and an FEL simulation is executed for 10-keV SASE. Then, the simulation results are plotted in term of the gain curve (undulator length vs. pulse energy), and evolution of radiation profiles.
Example 1
simulate the SASE FEL of 10-keV radiation
plot the gain curve and visualize the growth of the radiation profiles
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
# start with CLI mode
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
# start with default settings
# Mode = interactive, Browser = Chrome, Source Files = remote
simplex.Start()
# open "sample_sase.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)
if isjupyter:
display(HTML("<h2>Gain Curve of SASE FEL"))
simplex.PostProcessCLI.Plot(item="Pulse Energy", config={"yscale":"Logarithmic"})
display(HTML("<h2>Growth of Temporal and Spectral Profiles"))
simplex.PostProcessCLI.Plot(item=["Temporal Profile", "Spectral Profile"])
display(HTML("<h2>Growth of Spatial and Angular Profiles"))
simplex.PostProcessCLI.Plot(item=["Spatial Profile", "Angular Profile"])
else:
# plot the gain curve (pulse energy vs. undulator length)
simplex.PostProcess.Plot("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 temporal and spectral profiles
simplex.PostProcess.Plot("Temporal Profile")
simplex.PostProcess.MultiPlot(item="Spectral Profile")
simplex.PostProcess.MultiPlotCols(2)
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.DuplicatePlot("Growth of Temporal and Spectral Profiles")
# visualize the growth of spatial and angular profiles
simplex.PostProcess.Plot("Spatial Profile")
simplex.PostProcess.MultiPlot(item="Angular Profile")
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.ShowColorScale(False)
simplex.PostProcess.DuplicatePlot("Growth of Spatial and Angular Profiles")
if not isjupyter:
# 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
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
simplex.Start()
# 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)
if isjupyter:
display(HTML("<h2>Gain Curve of SASE FEL"))
simplex.PostProcessCLI.Plot(item="Pulse Energy", config={"yscale":"Logarithmic"})
else:
# plot the gain curve (pulse energy vs. undulator length)
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.PlotScale(y="log")
# turn on the wakefield
simplex.Set("wake", "wakeon", True)
simplex.PreProcess.Plot("Wakefield Temporal Profile")
if not isjupyter:
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
if isjupyter:
display(HTML("<h2>K Value Trend"))
simplex.PostProcessCLI.Plot(item="K Value Trend")
else:
simplex.PostProcess.Plot("K Value Trend")
simplex.PostProcess.DuplicatePlot("K value variation to compensate for the energy loss")
# compare with the former results
if isjupyter:
display(HTML("<h2>Comparison: 1/2: without/with wake, 3 compensation by tapering"))
simplex.PostProcessCLI.Plot(data=["sample2-1", "sample2-2", "sample2-3"], item="Pulse Energy")
else:
simplex.PostProcess.SelectData("sample2-1")
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.PlotScale(y="linear")
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 not isjupyter:
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
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
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)
if isjupyter:
display(HTML("<h2>10-keV SASE FEL with different energies and K values"))
simplex.PostProcessCLI.Plot(data=["sample3-6", "sample3-8", "sample3-10"],
item="Pulse Energy", config={"yscale":"Logarithmic"})
else:
simplex.PostProcess.SelectData("sample3-6")
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.ComparativePlot("sample3-8", "sample3-10")
simplex.PostProcess.PlotScale(y="log")
if not isjupyter:
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 4
shot-to-shot fluctuation of SASE FEL simulated by varying the seed number for the random number
compare the gain curve and spectral/temporal profiles
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
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)
simplex.Set("datadump", "spectral", 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))
if isjupyter:
display(HTML("<h2>Gain Curve of SASE FEL"))
simplex.PostProcessCLI.Plot(data=datanames, item="Pulse Energy", config={"yscale":"Logarithmic"})
display(HTML("<h2>Temporal Profiles"))
simplex.PostProcessCLI.Plot(data=datanames, item="Temporal Profile")
display(HTML("<h2>Spectral Profiles"))
simplex.PostProcessCLI.Plot(data=datanames, item="Temporal Profile")
else:
simplex.PostProcess.SelectData("sample4_1")
# gain curves
simplex.PostProcess.Plot("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.Plot("Temporal Profile")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.DuplicatePlot("Shot-to-shot fluctuation of the temporal profile")
# temporal profiles
simplex.PostProcess.Plot("Spectral Profile")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.SetSlide(-1)
simplex.PostProcess.DuplicatePlot("Shot-to-shot fluctuation of the spectral profile")
if not isjupyter:
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 5
optimization of undulator taper to maximize the pulse energy
2-stage simulation: 1st without taper, 2nd scan taper
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
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
if isjupyter:
display(HTML("<h2>Taper optimization starting from 5/6-th segment"))
simplex.PostProcessCLI.Plot(data=["sample5-1", *datanames], item="Pulse Energy")
else:
simplex.PostProcess.SelectData("sample5-1")
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.PlotScale(y="linear")
simplex.PostProcess.DuplicatePlot("Taper optimization starting from 5/6-th segment")
if not isjupyter:
input("Completed. Press enter to 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
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
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)
if isjupyter:
display(HTML("<h2>Example of double-color SASE FEL"))
simplex.PostProcessCLI.Plot(item="Spectral Profile")
else:
simplex.PostProcess.Plot("Spectral Profile")
simplex.PostProcess.StartAnimation()
if not isjupyter:
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
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
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)
if isjupyter:
display(HTML("<h2>Example of double-pulse SASE FEL"))
simplex.PostProcessCLI.Plot(data=["sample7-1", "sample7-2"], item="Temporal Profile")
else:
# compare the two schemes
simplex.PostProcess.SelectData("sample7-1")
simplex.PostProcess.Plot("Temporal Profile")
simplex.PostProcess.ComparativePlot("sample7-2")
simplex.PostProcess.SetSlide(-1)
if not isjupyter:
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 8
evaluate the effects of the trajectory errors on the FEL gain
errors caused by the BPM misalignment and injection error
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
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
if isjupyter:
display(HTML("<h2>Gain reduction by the BPM misalignment"))
simplex.PostProcessCLI.Plot(data=["sample8-0", *datanames],
item="Pulse Energy", config={"yscale":"Logarithmic"})
else:
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.PlotScale(y="log")
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))
# plot and compare the results
if isjupyter:
display(HTML("<h2>Gain reduction by the injection error"))
simplex.PostProcessCLI.Plot(data=["sample8-0", *datanames],
item="Pulse Energy", config={"yscale":"Logarithmic"})
else:
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.PlotScale(y="log")
simplex.PostProcess.SelectData("sample8-0")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.DuplicatePlot("Gain reduction by the injection error")
if not isjupyter:
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
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
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
if isjupyter:
display(HTML("<h2>Gain reduction by the K value discrepancy between segments"))
simplex.PostProcessCLI.Plot(data=["sample9-0", *datanames], item="Pulse Energy", config={"yscale":"Logarithmic"})
else:
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.PlotScale(y="log")
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))
if isjupyter:
display(HTML("<h2>Gain reduction by the mismatched phase between segments"))
simplex.PostProcessCLI.Plot(data=["sample9-0", *datanames], item="Pulse Energy", config={"yscale":"Logarithmic"})
else:
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.PlotScale(y="log")
simplex.PostProcess.SelectData("sample9-0")
simplex.PostProcess.ComparativePlot(*datanames)
simplex.PostProcess.DuplicatePlot("Gain reduction by the mismatched phase between segments")
if not isjupyter:
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
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
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
if isjupyter:
display(HTML("<h2>E-t Phase Space"))
simplex.PreProcess.PlotParticles(x="s (m)", y="Energy (GeV)", max=50000)
if not isjupyter:
simplex.PreProcess.DuplicatePlot("E-t Phase Space")
if isjupyter:
display(HTML("<h2>Expected Saturation Power"))
simplex.PreProcess.PlotSliceParameter("Saturation Power")
if not isjupyter:
simplex.PreProcess.DuplicatePlot("Expected Saturation 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)
if isjupyter:
display(HTML("<h2>Temporal Profile"))
simplex.PostProcessCLI.Plot(item="Temporal Profile")
else:
simplex.PostProcess.Plot("Temporal Profile")
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
if isjupyter:
display(HTML("<h2>Gain Curve"))
simplex.PostProcessCLI.Plot(data=["sample10-1", "sample10-2"], item="Pulse Energy")
else:
simplex.PostProcess.SelectData("sample10-1")
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.PlotScale(y="log")
simplex.PostProcess.ComparativePlot("sample10-2")
simplex.PostProcess.DuplicatePlot()
# compare the two results: growth of the spatial profile
if isjupyter:
display(HTML("<h2>Spatial Profile"))
simplex.PostProcessCLI.Plot(data=["sample10-1", "sample10-2"], item="Spatial Profile")
else:
simplex.PostProcess.Plot("Spatial Profile")
simplex.PostProcess.PlotScale(y="log")
simplex.PostProcess.ComparativePlot("sample10-2")
simplex.PostProcess.ComparativePlotCols(2)
simplex.PostProcess.StartAnimation()
if not isjupyter:
input("Completed. Press enter to exit. ")
simplex.Exit()
Example 11
HGHG (high gain harmonic generation) FEL
2-stage simulation: 1st modulator, 2nd radiator
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
simplex.Start()
# 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
if isjupyter:
display(HTML("<h2>Particle distribution in E-t space with 10um R<sub>56</sub>"))
simplex.PostProcessCLI.RunDataProcessing(\
data="sample11-1", item="Particle Motion", timerange="Set Window", timewindow=[-1, 1], r56pp=1e-5)
else:
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 = []
enh = 1240/266*harmonic # central photon energy
for sn in range(1, nr):
datanames.append("sample11-"+str(harmonic)+"_"+str(sn))
if isjupyter:
display(HTML("<h2>Spectrum of "+str(harmonic)+"th-harmonic radiation"))
simplex.PostProcessCLI.Plot(data=["sample11-"+str(harmonic)+"_0", *datanames],
item="Spectral Profile", config={"xrange":[enh*(1-fr),enh*(1+fr)]})
else:
simplex.PostProcess.SelectData("sample11-"+str(harmonic)+"_0")
simplex.PostProcess.Plot("Spectral Profile")
simplex.PostProcess.ComparativePlot(*datanames)
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 not isjupyter:
input("Completed. Press enter to exit.")
simplex.Exit()
Example 12
EEHG (echo enhanced harmonic generation) FEL
3-stage simulation: 1st/2nd modulator, 3rd radiator
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
simplex.Start()
# open "sample_mod.json" to simulate the particle motion in the modulator 1&2
simplex.Open("sample_mod.json")
# reduce the bunch charge and length
simplex.Set("ebeam", "bunchleng", 5e-6)
simplex.Set("ebeam", "bunchcharge", 0.05)
# reduce the seed power
simplex.Set("seed", "pkpower", 5e7)
# increase particles/beamlet to cover up to 50th harmonic
simplex.Set("condition", "particles", 102)
# shrink the simulation temporal window
simplex.Set("condition", "simrange", [-12e-6,12e-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
if isjupyter:
display(HTML("<h2>Particle distribution at the 1st modulator exit with R<sub>56</sub>=5mm"))
simplex.PostProcessCLI.RunDataProcessing(
item="Particle Motion", timerange="Set Window", timewindow=[0, 0], r56pp=8.4e-3,
config={"type":"Symbol", "size":1})
else:
simplex.PostProcess.SetDataProcessing("item", "Particle Motion")
simplex.PostProcess.SetDataProcessing("timerange", "Set Window")
simplex.PostProcess.SetDataProcessing("timewindow", [0, 0])
simplex.PostProcess.SetDataProcessing("r56pp", 8.4e-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=8.4mm
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", 8.4e-3)
simplex.Set("spxout", "spxfile", "./output/sample12-1.json")
simplex.Set("seed", "pkpower", 5e6)
simplex.Set("seed", "timing", -4)
# start simulation in the 2nd modulator
simplex.StartSimulation(folder="./output", prefix="sample12", serial=2)
# verify the energy modulation
if isjupyter:
display(HTML("<h2>Particle distribution at the 2nd modulator exit with R<sub>56</sub>=0.24mm"))
simplex.PostProcessCLI.RunDataProcessing(r56pp=3.6e-4, config={"type":"Symbol", "size":1})
else:
simplex.PostProcess.SetDataProcessing("r56pp", 3.6e-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")
# shorte period and more segments for higher harmonics
simplex.Set("undulator", "lu", 30)
simplex.Set("undulator", "segments", 8)
# enable "Higher Precision" to evaluate the bunch factor more precisely
# needed because the beamlet expands across a large number of slices
simplex.Set("condition", "hpcond", True)
simplex.Set("condition", "step", 10)
# shrink the simulation temporal window
simplex.Set("condition", "simrange", [-5e-6,5e-6])
harmonics = [24, 48] # evaluate the 25th and 50th harmonics
nr = 5 # number of R56 scan for each harmonics
r56 = [3.3e-4, 3.7e-4] # scan R56 from 330um to 370um
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 = []
enh = 1240/266*harmonic # central photon energy
for sn in range(1, nr):
datanames.append("sample12-"+str(harmonic)+"_"+str(sn))
if isjupyter:
display(HTML("<h2>Spectrum of "+str(harmonic)+"th-harmonic radiation"))
simplex.PostProcessCLI.Plot(data=["sample12-"+str(harmonic)+"_0", *datanames], item="Spectral Profile",
config={"xrange":[enh*(1-fr),enh*(1+fr)]})
else:
simplex.PostProcess.SelectData("sample12-"+str(harmonic)+"_0")
simplex.PostProcess.Plot("Spectral Profile")
simplex.PostProcess.ComparativePlot(*datanames)
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 not isjupyter:
input("Completed. Press enter to 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
import sys
isjupyter = "ipykernel" in sys.modules
import simplex
if isjupyter:
simplex.Start(mode="c")
import plotly.io as pio
pio.renderers.default = "notebook" # to enable exporting as an HTML file
from IPython.display import display, HTML
else:
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)"]
# adjust cavity length
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+1} 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])
# 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=(',', ': '))
if isjupyter:
simplex.PostProcessCLI.Import("./output/sample13.json")
display(HTML("<h2>Turn-by-turn growth of the pulse energy"))
simplex.PostProcessCLI.Plot(item="Pulse Energy", config={"yscale":"Logarithmic"})
display(HTML("<h2>Turn-by-turn evolution of the spectrum"))
simplex.PostProcessCLI.Plot(item="Spectral Profile")
else:
simplex.PostProcess.Import("./output/sample13.json")
simplex.PostProcess.Plot("Pulse Energy")
simplex.PostProcess.PlotScale(y="log")
simplex.PostProcess.DuplicatePlot("Turn-by-turn growth of the pulse energy")
simplex.PostProcess.Plot("Spectral Profile")
simplex.PostProcess.DuplicatePlot("Turn-by-turn evolution of the spectrum")
if not isjupyter:
input("Completed. Press enter to exit. ")
simplex.Exit()
Jupyter Output Examples
The above python scripts can be executed in the Jupyter notebook as well; for reference, Jupyter notebook outputs for respective examples are exported as HTML files and are shown below.