"""
Simframe GPT Module
Various objects and functions to handle GPT lattices and commands.
Classes:
- :class:`~SimulationFramework.Codes.GPT.GPT.gptLattice`: The GPT lattice object, used for
converting the :class:`~SimulationFramework.Framework_elements.frameworkObject` s defined in the
:class:`~SimulationFramework.Framework_elements.frameworkLattice` into a string representation of
the lattice suitable for GPT input and lattice files.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_element`: Base class for defining
commands in a GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_setfile`: Class for defining the
input files for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_charge`: Class for defining the
bunch charge for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_setreduce`: Class for reducing the
number of particles for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_accuracy`: Class for setting the
accuracy for GPT tracking.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_spacecharge`: Class for defining the
space charge setup for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_tout`: Class for defining the
number of steps for particle distribution output for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_csr1d`: Class for defining the
CSR calculations for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_writefloorplan`: Class for setting up the
writing of the lattice floor plan for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_Zminmax`: Class for defining the
minimum and maximum z-positions for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_forwardscatter`: Class for defining
scattering parameters for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_scatterplate`: Class for defining a
scattering object for the GPT input file.
- :class:`~SimulationFramework.Codes.GPT.GPT.gpt_dtmaxt`: Class for defining the
step size(s) for the GPT input file.
"""
import os
from copy import deepcopy
import subprocess
import numpy as np
from ...Framework_objects import (
frameworkLattice,
frameworkElement,
elementkeywords,
getGrids,
)
from ...Framework_elements import screen, marker, gpt_ccs, cavity, wakefield
from ...FrameworkHelperFunctions import saveFile, expand_substitution
from ...Modules import Beams as rbf
from ...Modules.merge_two_dicts import merge_two_dicts
from ...Modules.Fields import field
from ...Modules.units import UnitValue
from typing import Dict, Literal
gpt_defaults = {}
[docs]
class gptLattice(frameworkLattice):
"""
Class for defining the GPT lattice object, used for
converting the :class:`~SimulationFramework.Framework_elements.frameworkObject`s defined in the
:class:`~SimulationFramework.Framework_elements.frameworkLattice` into a string representation of
the lattice suitable for a GPT input file.
"""
code: str = "gpt"
"""String indicating the lattice object type"""
allow_negative_drifts: bool = True
"""Flag to indicate whether negative drifts are allowed"""
bunch_charge: float | None = None
"""Bunch charge"""
headers: Dict = {}
"""Headers to be included in the GPT lattice file"""
ignore_start_screen: screen | None = None
"""Flag to indicate whether to ignore the first screen in the lattice"""
screen_step_size: float = 0.1
"""Step size for screen output"""
time_step_size: str = "0.1/c"
"""Step size for tracking"""
override_meanBz: float | int | None = None
"""Set the average particle longitudinal velocity manually"""
override_tout: float | int | None = None
"""Set the time step output manually"""
accuracy: int = 6
"""Tracking accuracy"""
endScreenObject: screen | None = None
"""Final screen object for dumping particle distributions"""
Brho: UnitValue | None = None
def model_post_init(self, __context):
super().model_post_init(__context)
if (
"input" in self.file_block
and "particle_definition" in self.file_block["input"]
):
if (
self.file_block["input"]["particle_definition"]
== "initial_distribution"
):
self.particle_definition = "laser"
else:
self.particle_definition = self.file_block["input"][
"particle_definition"
]
else:
self.particle_definition = self.elementObjects[self.start].objectname
self.headers["setfile"] = gpt_setfile(
set='"beam"', filename='"' + self.name + '.gdf"'
)
self.headers["floorplan"] = gpt_writefloorplan(
filename='"' + self.objectname + '_floor.gdf"'
)
@property
def space_charge_mode(self) -> str | None:
"""
Get the space charge mode based on
:attr:`~SimulationFramework.Framework_objects.frameworkLattice.globalSettings` or
:attr:`~SimulationFramework.Framework_objects.frameworkLattice.file_block`.
Returns
-------
str
Space charge mode as string, or None if not provided.
"""
if (
"charge" in self.file_block
and "space_charge_mode" in self.file_block["charge"]
):
return self.file_block["charge"]["space_charge_mode"]
elif (
"charge" in self.globalSettings
and "space_charge_mode" in self.globalSettings["charge"]
):
return self.globalSettings["charge"]["space_charge_mode"]
else:
return None
@space_charge_mode.setter
def space_charge_mode(self, mode: Literal["2d", "3d", "2D", "3D"]) -> None:
"""
Set the space charge mode manually ["2D", "3D"].
Parameters
----------
mode: Literal["2d", "3d", "2D", "3D"]
The space charge calculation mode
"""
if "charge" not in self.file_block:
self.file_block["charge"] = {}
self.file_block["charge"]["space_charge_mode"] = mode
[docs]
def endScreen(self, **kwargs) -> screen:
"""
Make the final position in the lattice a
:class:`~SimulationFramework.Elements.screen.screen` object.
Returns
-------
:class:`~SimulationFramework.Elements.screen.screen`
The final screen in the lattice
"""
return screen(
objectname=self.endObject.objectname,
objecttype="screen",
centre=self.endObject.centre,
position_start=self.endObject.position_start,
position_end=self.endObject.position_start,
global_rotation=self.endObject.global_rotation,
global_parameters=self.global_parameters,
**kwargs,
)
[docs]
def writeElements(self) -> str:
"""
Write the lattice elements defined in this object into a GPT-compatible format; see
:attr:`~SimulationFramework.Framework_objects.frameworkLattice.elementObjects`.
The appropriate headers required for GPT are written at the top of the file, see the `write_GPT`
function in :class:`~SimulationFramework.Codes.GPT.gpt_element`.
Returns
-------
str
The lattice represented as a string compatible with GPT
"""
ccs = gpt_ccs("wcs", [0, 0, 0], [0, 0, 0])
fulltext = ""
self.headers["accuracy"] = gpt_accuracy(accuracy=self.accuracy)
if "charge" not in self.file_block:
self.file_block["charge"] = {}
if "charge" not in self.globalSettings:
self.globalSettings["charge"] = {}
space_charge_dict = merge_two_dicts(
self.file_block["charge"],
self.globalSettings["charge"],
)
if self.particle_definition == "laser" and self.space_charge_mode is not None:
self.headers["spacecharge"] = gpt_spacecharge(
**merge_two_dicts(self.global_parameters, space_charge_dict)
)
self.headers["spacecharge"].npart = len(self.global_parameters["beam"].x)
self.headers["spacecharge"].sample_interval = self.sample_interval
# self.headers["spacecharge"].space_charge_mode = "cathode"
else:
self.headers["spacecharge"] = gpt_spacecharge(
**merge_two_dicts(self.global_parameters, space_charge_dict)
)
if (
self.csr_enable
and len(self.dipoles) > 0
and max([abs(d.angle) for d in self.dipoles]) > 0
): # and not os.name == 'nt':
self.headers["csr1d"] = gpt_csr1d()
# print('CSR Enabled!', self.objectname, len(self.dipoles))
# self.headers['forwardscatter'] = gpt_forwardscatter(ECS='"wcs", "I"', name='cathode', probability=0)
# self.headers['scatterplate'] = gpt_scatterplate(ECS='"wcs", "z", -1e-6', model='cathode', a=1, b=1)
for header in self.headers:
fulltext += self.headers[header].write_GPT()
for i, element in enumerate(list(self.elements.values())):
if i == 0:
screen0pos = element.start[2]
ccs = element.gpt_ccs(ccs)
if i == 0 and isinstance(element, screen):
self.ignore_start_screen = element
else:
fulltext += element.write_GPT(self.Brho, ccs=ccs)
if (
isinstance(element, cavity)
and hasattr(element, "wakefield_definition")
and isinstance(element.wakefield_definition, field)
):
original_properties = deepcopy(element.objectproperties)
original_properties.objectname = f"{element.objectname}_wake"
original_properties.objecttype = "wakefield"
setattr(
original_properties,
"field_definition",
original_properties.wakefield_definition,
)
wake_element = wakefield(
**{
k: getattr(original_properties, k)
for k in original_properties.model_fields_set
}
)
wake_element.cells = original_properties.get_cells()
fulltext += wake_element.write_GPT(self.Brho, ccs=ccs)
new_ccs = element.gpt_ccs(ccs)
if not new_ccs == ccs:
# print('ccs = ', ccs, ' new_ccs = ', new_ccs)
relpos, relrot = ccs.relative_position(
element.middle, element.global_rotation
)
if self.particle_definition == "laser":
fulltext += (
"screen( "
+ ccs.name
+ ', "I", '
+ str(screen0pos + self.screen_step_size)
+ ", "
+ str(relpos[2])
+ ", "
+ str(float(self.screen_step_size))
+ ', "OutputCCS",'
+ ccs.name
+ ");\n"
)
else:
fulltext += (
"screen( "
+ ccs.name
+ ', "I", '
+ str(screen0pos)
+ ", "
+ str(relpos[2])
+ ", "
+ str(float(self.screen_step_size))
+ ', "OutputCCS",'
+ ccs.name
+ ");\n"
)
screen0pos = 0
ccs = new_ccs
if not isinstance(element, (screen, marker)):
element = self.endScreenObject = self.endScreen()
fulltext += self.endScreenObject.write_GPT(
self.Brho, ccs=ccs, output_ccs="wcs"
)
else:
# print('End screen', element.objectname)
self.endScreenObject = None
relpos, relrot = ccs.relative_position(
element.position_end, element.global_rotation
)
if self.particle_definition == "laser":
fulltext += (
"screen( "
+ ccs.name
+ ', "I", '
+ str(screen0pos + self.screen_step_size)
+ ", "
+ str(relpos[2])
+ ", "
+ str(float(self.screen_step_size))
+ ', "OutputCCS",'
+ ccs.name
+ ");\n"
)
else:
fulltext += (
"screen( "
+ ccs.name
+ ', "I", '
+ str(screen0pos)
+ ", "
+ str(relpos[2])
+ ", "
+ str(float(self.screen_step_size))
+ ', "OutputCCS",'
+ ccs.name
# + ", \"GroupName\","
# + "\"SCREEN-" + ccs.name.strip("\"").upper() + "-END-01\""
+ ");\n"
)
zminmax = gpt_Zminmax(
ECS='"wcs", "I"',
zmin=self.startObject.position_start[2] - 0.1,
zmax=self.endObject.position_end[2] + 1,
)
fulltext += zminmax.write_GPT()
return fulltext
[docs]
def write(self) -> str:
"""
Writes the GPT input file from :func:`~SimulationFramework.Codes.GPT.gptLattice.writeElements`
to <master_subdir>/<self.objectname>.in.
"""
code_file = (
self.global_parameters["master_subdir"] + "/" + self.objectname + ".in"
)
saveFile(code_file, self.writeElements())
return self.writeElements()
[docs]
def preProcess(self) -> None:
"""
Convert the beam file from the previous lattice section into GPT format and set the number of
particles based on the input distribution, see
:func:`~SimulationFramework.Codes.GPT.GPT.gptLattice.hdf5_to_astra`.
"""
super().preProcess()
self.headers["setfile"].particle_definition = self.objectname + ".gdf"
prefix = self.get_prefix()
self.hdf5_to_gdf(prefix)
[docs]
def run(self) -> None:
"""
Run the code with input 'filename'
`GPTLICENSE` must be provided in
:attr:`~SimulationFramework.Framework_objects.frameworkLattice.global_parameters`.
Average properties of the distribution are also calculated and written
to an `<>emit.gdf` file in `master_subdir`.
"""
main_command = (
self.executables[self.code]
+ ["-o", self.objectname + "_out.gdf"]
+ ["GPTLICENSE=" + self.global_parameters["GPTLICENSE"]]
+ [self.objectname + ".in"]
)
my_env = os.environ.copy()
my_env["LD_LIBRARY_PATH"] = (
my_env["LD_LIBRARY_PATH"] + ":/opt/GPT3.3.6/lib/"
if "LD_LIBRARY_PATH" in my_env
else "/opt/GPT3.3.6/lib/"
)
my_env["OMP_WAIT_POLICY"] = "PASSIVE"
post_command = (
[self.executables[self.code][0].replace("gpt", "gdfa")]
+ ["-o", self.objectname + "_emit.gdf"]
+ [self.objectname + "_out.gdf"]
+ [
"position",
"Q",
"avgx",
"avgy",
"avgz",
"stdx",
"stdBx",
"stdy",
"stdBy",
"stdz",
"stdt",
"nemixrms",
"nemiyrms",
"nemizrms",
"numpar",
"nemirrms",
"avgG",
"avgp",
"stdG",
"avgt",
"avgBx",
"avgBy",
"avgBz",
"CSalphax",
"CSalphay",
"CSbetax",
"CSbetay",
]
)
post_command_t = (
[self.executables[self.code][0].replace("gpt", "gdfa")]
+ ["-o", self.objectname + "_emitt.gdf"]
+ [self.objectname + "_out.gdf"]
+ [
"time",
"Q",
"avgx",
"avgy",
"avgz",
"stdx",
"stdBx",
"stdy",
"stdBy",
"stdz",
"nemixrms",
"nemiyrms",
"nemizrms",
"numpar",
"nemirrms",
"avgG",
"avgp",
"stdG",
"avgBx",
"avgBy",
"avgBz",
"CSalphax",
"CSalphay",
"CSbetax",
"CSbetay",
"avgfBx",
"avgfEx",
"avgfBy",
"avgfEy",
"avgfBz",
"avgfEz",
]
)
post_command_traj = (
[self.executables[self.code][0].replace("gpt", "gdfa")]
+ ["-o", self.objectname + "traj.gdf"]
+ [self.objectname + "_out.gdf"]
+ ["time", "Q", "avgx", "avgy", "avgz"]
)
with open(
os.path.abspath(
self.global_parameters["master_subdir"] + "/" + self.objectname + ".bat"
),
"w",
) as batfile:
for command in [
main_command,
post_command,
post_command_t,
post_command_traj,
]:
output = '"' + command[0] + '" '
for c in command[1:]:
output += c + " "
output += "\n"
batfile.write(output)
with open(
os.path.abspath(
self.global_parameters["master_subdir"] + "/" + self.objectname + ".log"
),
"w",
) as f:
# print('gpt command = ', command)
subprocess.call(
main_command,
stdout=f,
cwd=self.global_parameters["master_subdir"],
env=my_env,
)
subprocess.call(
post_command, stdout=f, cwd=self.global_parameters["master_subdir"]
)
subprocess.call(
post_command_t, stdout=f, cwd=self.global_parameters["master_subdir"]
)
subprocess.call(
post_command_traj, stdout=f, cwd=self.global_parameters["master_subdir"]
)
[docs]
def postProcess(self) -> None:
"""
Convert the beam file(s) from the GPT output into HDF5 format, see
:func:`~SimulationFramework.Elements.screen.screen.gdf_to_hdf5`.
"""
super().postProcess()
cathode = self.particle_definition == "laser"
gdfbeam = rbf.gdf.read_gdf_beam_file_object(
self.global_parameters["beam"],
self.global_parameters["master_subdir"]
+ "/"
+ self.objectname
+ "_out.gdf",
)
for e in self.screens_and_markers_and_bpms:
if not e == self.ignore_start_screen:
e.gdf_to_hdf5(
self.objectname + "_out.gdf", cathode=cathode, gdfbeam=gdfbeam
)
# else:
# print('Ignoring', self.ignore_start_screen.objectname)
if self.endScreenObject is not None:
self.endScreenObject.gdf_to_hdf5(
self.objectname + "_out.gdf", cathode=cathode
)
[docs]
def hdf5_to_gdf(self, prefix: str="") -> None:
"""
Convert the HDF5 beam distribution to GDF format.
Certain properties of this class, including
:attr:`~SimulationFramework.Codes.GPT.GPT.gptLattice.sample_interval`,
:attr:`~SimulationFramework.Codes.GPT.GPT.gptLattice.override_meanBz`,
:attr:`~SimulationFramework.Codes.GPT.GPT.gptLattice.override_tout` are also
used to update
:attr:`~SimulationFramework.Codes.GPT.GPT.gptLattice.headers`.
Parameters
----------
prefix: str
HDF5 file prefix
"""
HDF5filename = prefix + self.particle_definition + ".hdf5"
if os.path.isfile(expand_substitution(self, HDF5filename)):
filepath = expand_substitution(self, HDF5filename)
else:
filepath = self.global_parameters["master_subdir"] + "/" + HDF5filename
rbf.hdf5.read_HDF5_beam_file(
self.global_parameters["beam"],
filepath,
)
if self.sample_interval > 1:
self.headers["setreduce"] = gpt_setreduce(
set='"beam"',
setreduce=int(
len(self.global_parameters["beam"].x) / self.sample_interval
),
)
if self.override_meanBz is not None and isinstance(
self.override_meanBz, (int, float)
):
meanBz = self.override_meanBz
else:
meanBz = np.mean(self.global_parameters["beam"].Bz)
if meanBz < 0.5:
meanBz = 0.75
if self.override_tout is not None and isinstance(
self.override_tout, (int, float)
):
self.headers["tout"] = gpt_tout(
starttime=0, endpos=self.override_tout, step=str(self.time_step_size)
)
else:
endpos = (self.findS(self.endObject.objectname)[0][1] -
self.findS(self.startObject.objectname)[0][1])
self.headers["tout"] = gpt_tout(
starttime=0,
endpos=endpos
/ meanBz
/ 2.998e8,
step=str(self.time_step_size),
)
self.global_parameters["beam"].beam.rematchXPlane(
**self.initial_twiss["horizontal"]
)
self.global_parameters["beam"].beam.rematchYPlane(
**self.initial_twiss["vertical"]
)
gdfbeamfilename = self.objectname + ".gdf"
cathode = self.particle_definition == "laser"
rbf.gdf.write_gdf_beam_file(
self.global_parameters["beam"],
self.global_parameters["master_subdir"] + "/" + gdfbeamfilename,
normaliseX=self.elementObjects[self.start].start[0],
cathode=cathode,
)
self.Brho = self.global_parameters["beam"].Brho
[docs]
class gpt_element(frameworkElement):
"""
Generic class for generating headers for GPT.
"""
[docs]
def write_GPT(self, *args, **kwargs) -> str:
"""
Write the text for the GPT namelist based on its
:attr:`~objectdefaults`, :attr:`~objectname`.
Returns
-------
str
GPT-compatible string representing the namelist
"""
return self._write_GPT(*args, **kwargs)
def _write_GPT(self, *args, **kwargs):
output = str(self.objectname) + "("
for k in elementkeywords[self.objecttype]["keywords"]:
k = k.lower()
if getattr(self, k) is not None:
output += str(getattr(self, k)) + ", "
elif k in self.objectdefaults:
output += self.objectdefaults[k] + ", "
output = output[:-2]
output += ");\n"
return output
[docs]
class gpt_setfile(gpt_element):
"""
Class for setting filenames in GPT via `setfile`.
"""
objectname: str = "setfile"
"""Name of object"""
objecttype: str = "gpt_setfile"
"""Type of object"""
[docs]
class gpt_charge(gpt_element):
"""
Class for generating the `settotalcharge` namelist for GPT.
"""
set: str = '"beam"'
"""Name of beam for `settotalcharge`"""
charge: float = 0.0
"""Bunch charge"""
objectname: str = "settotalcharge"
"""Name of object"""
objecttype: str = "gpt_charge"
"""Type of object"""
def _write_GPT(self, *args, **kwargs):
output = str(self.objectname) + "("
output += str(self.set) + ","
output += str(-1 * abs(self.charge)) + ");\n"
return output
[docs]
class gpt_setreduce(gpt_element):
"""
Class for reducing the number of particles via `setreduce`.
"""
set: str = '"beam"'
"""Name of the beam for `setreduce`"""
setreduce: int = 1
"""Factor by which to reduce the number of particles"""
objectname: str = "setreduce"
"""Name of object"""
objecttype: str = "gpt_setreduce"
"""Type of object"""
def _write_GPT(self, *args, **kwargs):
output = str(self.objectname) + "("
output += str(self.set) + ","
output += str(self.setreduce) + ");\n"
return output
[docs]
class gpt_accuracy(gpt_element):
"""
Class for setting the accuracy of tracking via `accuracy` in GPT.
"""
objectname: str = "accuracy"
"""Name of object"""
objecttype: str = "gpt_accuracy"
"""Type of object"""
accuracy: int = 6
"""Accuracy for GPT tracking"""
def _write_GPT(self, *args, **kwargs):
output = (
"accuracy(" + str(self.accuracy) + ");\n"
) # 'setrmacrodist(\"beam\","u",1e-9,0) ;\n'
return output
[docs]
class gpt_spacecharge(gpt_element):
"""
Class for preparing space charge calculations in GPT via `spacecharge`.
"""
grids: getGrids = None
"""Class for calculating the required number of space charge grids"""
ngrids: int | None = None
"""Number of space charge grids"""
space_charge_mode: str | None = None
"""Space charge mode ['2D', '3D']"""
cathode: bool = False
"""Flag indicating whether the bunch was emitted from a cathode"""
objectname: str = "spacecharge"
"""Name of object"""
objecttype: str = "gpt_spacecharge"
"""Type of object"""
def model_post_init(self, __context):
super().model_post_init(__context)
self.grids = getGrids()
def _write_GPT(self, *args, **kwargs):
output = ""
if isinstance(self.space_charge_mode, str) and self.cathode:
if self.ngrids is None:
self.ngrids = self.grids.getGridSizes(
(self.npart / self.sample_interval)
)
output += 'spacecharge3Dmesh("Cathode","RestMaxGamma",1000);\n'
elif (
isinstance(self.space_charge_mode, str)
and self.space_charge_mode.lower() == "3d"
):
output += "Spacecharge3Dmesh();\n"
elif (
isinstance(self.space_charge_mode, str)
and self.space_charge_mode.lower() == "2d"
):
output += "Spacecharge3Dmesh();\n"
else:
output = ""
return output
[docs]
class gpt_tout(gpt_element):
"""
Class for setting up the beam dump rate via `tout`.
"""
startpos: float = 0.0
"""Starting position"""
endpos: float = 0.0
"""End position"""
starttime: float = 0.0
"""Start time for dumping"""
step: str = "0.1/c"
"""Dump step as a string [distance / c]"""
objectname: str = "tout"
"""Name of object"""
objecttype: str = "gpt_tout"
"""Type of object"""
def _write_GPT(self, *args, **kwargs):
self.starttime = 0 if self.starttime < 0 else self.starttime
output = str(self.objectname) + "("
if self.starttime is not None:
output += str(self.starttime) + ","
else:
output += str(self.startpos) + "/c,"
output += str(self.endpos) + ","
output += str(self.step) + ");\n"
return output
[docs]
class gpt_csr1d(gpt_element):
"""
Class for preparing CSR calculations via `csr1d`.
"""
objectname: str = "csr1d"
"""Name of object"""
objecttype: str = "gpt_csr1d"
"""Type of object"""
def _write_GPT(self, *args, **kwargs):
output = str(self.objectname) + "();\n"
return output
[docs]
class gpt_writefloorplan(gpt_element):
"""
Class for writing the lattice floor plan via `writefloorplan`.
"""
filename: str = ""
"""Floor plan filename"""
objectname: str = "writefloorplan"
"""Name of object"""
objecttype: str = "gpt_writefloorplan"
"""Type of object"""
def _write_GPT(self, *args, **kwargs):
output = str(self.objectname) + "(" + self.filename + ");\n"
return output
[docs]
class gpt_Zminmax(gpt_element):
"""
Class for setting the boundaries in z for discarding particles via `Zminmax`
"""
zmin: float = 0.0
"""Minimum longitudinal position"""
zmax: float = 0.0
"""Maximum longitudinal position"""
ECS: str = '"wcs", "I"'
"""Element coordinate system as a string"""
objectname: str = "Zminmax"
"""Name of object"""
objecttype: str = "gpt_Zminmax"
"""Type of object"""
def _write_GPT(self, *args, **kwargs):
output = (
str(self.objectname)
+ "("
+ self.ECS
+ ", "
+ str(self.zmin)
+ ", "
+ str(self.zmax)
+ ");\n"
)
return output
[docs]
class gpt_forwardscatter(gpt_element):
"""
Class for scattering particles via `forwardscatter`.
"""
zmin: float = 0.0
"""Minimum longitudinal position"""
zmax: float = 0.0
"""Maximum longitudinal position"""
ECS: str = '"wcs", "I"'
"""Element coordinate system"""
probability: float = 0.0
"""Scattering probability"""
objectname: str = "forwardscatter"
"""Name of object"""
objecttype: str = "gpt_forwardscatter"
"""Type of object"""
def _write_GPT(self, *args, **kwargs):
output = (
str(self.objectname)
+ "("
+ self.ECS
+ ', "'
+ str(self.name)
+ '", '
+ str(self.probability)
+ ");\n"
)
return output
[docs]
class gpt_scatterplate(gpt_element):
"""
Class for scattering particles off a plate via `scatterplate`.
"""
zmin: float = 0.0
"""Minimum longitudinal position"""
zmax: float = 0.0
"""Maximum longitudinal position"""
ECS: str = '"wcs", "I"'
"""Element coordinate system"""
a: float = 0.0
"""Plate length in x-direction"""
b: float = 0.0
"""Plate length in y-direction"""
model: str = "cathode"
"""Scattering model to be used ['cathode', 'remove']"""
objectname: str = "scatterplate"
"""Name of object"""
objecttype: str = "gpt_scatterplate"
"""Type of object"""
def _write_GPT(self, *args, **kwargs):
output = (
str(self.objectname)
+ "("
+ self.ECS
+ ", "
+ str(self.a)
+ ", "
+ str(self.b)
+ ') scatter="'
+ str(self.model)
+ '";\n'
)
return output
[docs]
class gpt_dtmaxt(gpt_element):
"""
Class for setting up minimum, maximmum temporal step sizes for tracking via `dtmaxt`.
"""
tend: float = 0.0
"""Final time value"""
tstart: float = 0.0
"""Initial time value"""
dtmax: float = 0.0
"""Maximum temporal step size"""
objectname: str = "dtmaxt"
"""Name of object"""
objecttype: str = "gpt_dtmaxt"
"""Type of object"""
def _write_GPT(self, *args, **kwargs):
output = (
str(self.objectname)
+ "("
+ str(self.tstart)
+ ", "
+ str(self.tend)
+ ", "
+ str(self.dtmax)
+ ");\n"
)
return output