.. _getting-started:
Getting started with SIMBA
==========================
.. _creating-the-lattice-elements:
Lattice Definition
------------------
Accelerator lattices in :mod:`SIMBA` are derived from the `LAURA `_
standard lattice format. This is a schema for providing generic descriptions of accelerator elements and
layouts; see the `LAURA documentation `_ for more details.
Given that this format is designed to capture all relevant information about accelerator elements,
and that it includes a built-in translator module for exporting lattice files to various simulation codes,
it can be used within :mod:`SIMBA` for loading, modifying, writing and exporting input and lattice
files for simulation codes.
We would be remiss not to begin with a simple FODO:
.. code-block:: python
from laura.models.element import Quadrupole, Marker # noqa E402
from laura.models.elementList import MachineModel # noqa E402
from laura.Exporters.YAML import export_machine
from laura import LAURA # noqa E402
outdir = "/path/to/lattice/directory"
m1 = Marker(
name="M1",
machine_area="FODO",
hardware_class="Marker",
physical={
"middle": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
}
}
)
# Make some quads and add them to the machine
q1f = Quadrupole(
name="QUAD1F",
machine_area="FODO",
magnetic={
"length": 1.0,
"k1l": -1,
},
physical={
"length": 1.0,
"middle": {
"x": 0.0,
"y": 0.0,
"z": 0.75,
},
},
)
q1d = Quadrupole(
name="QUAD1D",
machine_area="FODO",
magnetic={
"length": 1.0,
"k1l": 1.0,
},
physical={
"length": 1.0,
"middle": {
"x": 0.0,
"y": 0.0,
"z": 3.25,
}
},
)
m3 = Marker(
name="M3",
machine_area="FODO",
hardware_class="Marker",
physical={
"middle": {
"x": 0.0,
"y": 0.0,
"z": 4.0,
}
}
)
sections = {
"sections": {
"FODO": ["M1", "QUAD1F", "QUAD1D", "M3"],
}
}
layouts = {
"default_layout": "line1",
"layouts": {
"line1": ["FODO"],
}
}
machine = LAURA(element_list=[m1, q1f, q1d, m3], layout=layouts, section=sections)
export_machine(path=f"{outdir}/Lattice", machine=machine, overwrite=True)
Generating an input beam
------------------------
The simulation requires a macroparticle distribution to run. This can be generated using the
:ref:`frameworkGenerator ` as follows:
.. code-block:: python
from simba.Codes.Generators imoutdirport frameworkGenerator
import simba.Modules.Beams as rbf
gen = frameworkGenerator(
global_parameters={"master_subdir": outdir},
filename="M1.openpmd.hdf5",
initial_momentum=5e6,
sigma_x=1e-4,
sigma_px=1e3,
sigma_y=1e-4,
sigma_py=1e3,
sigma_z=1e-3,
sigma_pz=1e3,
gaussian_cutoff_x=3,
gaussian_cutoff_y=3,
gaussian_cutoff_z=3,
gaussian_cutoff_px=3,
gaussian_cutoff_py=3,
gaussian_cutoff_pz=3,
charge=100e-12,
)
gen.write()
beam = rbf.beam(filename=f"{outdir}/M1.openpmd.hdf5")
Defining the Lattice Simulation
-------------------------------
With the initial beam distribution and lattice now defined, the simulation can be prepared.
This involves passing a group of settings to :mod:`SIMBA`, including:
* The ``files`` to run; this defines the start and end element (or longitudinal position) in the lattice and creates the sections;
* The simulation code used to run each section;
* Additional settings, such as the enabling of collective effects, or transverse matching parameters for the initial beam;
* Settings for beam generation;
* Locations for :mod:`LAURA` files.
These are all read in when :mod:`SIMBA` is instantiated. The example below shows how to create these settings in :mod:`python`;
alternatively, one can create a settings file in YAML and pass that in; see :ref:`Loading a lattice `:
.. code-block:: python
from simba.Framework_Settings import FrameworkSettings
settings = FrameworkSettings()
files = {}
for sec, elems in machine.sections.items():
files.update(
{
sec: {
"code": "ocelot",
"charge": {
"space_charge_mode": "False",
},
"input": {
"twiss": {
"beta_x": 3.2844606,
"alpha_x": 2.48956886,
"nemit_x": 1e-6,
"beta_y": 3.2846606,
"alpha_y": -2.48956886,
"nemit_y": 1e-6,
},
},
"output": {
"start_element": elems[0].name,
"end_element": elems[-1].name,
},
}
}
)
settings.files = files
settings.layout = machine.layout
settings.section = {"sections": {name: e.names for name, e in machine.sections.items()}}
settings.element_list = f"{outdir}/Lattice"
This lattice definition would produce a lattice file (called ``FODO.py``) for running in the **Ocelot** beam tracking code.
If the :mod:`MachineModel` defined above consisted of sequential sections rather than just a FODO, these would then
be added automatically to the tracking.
Running SIMBA
-------------
With everything now prepared, :mod:`SIMBA` can be used to track through the simple FODO lattice.
Note that, in order to run executables (i.e. **ASTRA**, **ELEGANT**) rather than simulation codes based only on
python (i.e. **Ocelot**, **Cheetah** etc.), the :mod:`SimCodes` directory must be set up; see
:ref:`SimCodes `.
.. code-block:: python
import simba.Framework as fw
from simba.Framework import load_directory
framework = fw.Framework(
machine=machine,
simcodes='/path/to/simcodes/',
directory=f"{outdir}/ocelot",
clean=True,
verbose=True
)
framework.loadSettings(settings=settings)
framework.global_parameters["beam"] = beam
framework["FODO"].lsc_enable = False
framework["FODO"].csr_enable = False
framework.set_lattice_prefix("FODO", "../")
framework.track()
fwdir = load_directory(f"{outdir}/ocelot")
fwdir.plot(xkey="z", ykeys=['sigma_x', 'sigma_y'], ykeys2=["ecnx", "ecny"])
Which produces the plot in :numref:`fig-ocelot-fodo`:
.. _fig-ocelot-fodo:
.. figure:: assets/ocelot-fodo.png
Output of simple FODO tracking in **Ocelot**
The same lattice can also be tracked using a different code as follows:
.. code-block: python
framework.global_parameters["beam"] = beam
framework.change_Lattice_Code("FODO", "elegant")
framework.setSubDirectory(f"{outdir}/elegant")
framework.track()
fwdir = load_directory(f"{outdir}/elegant")
fwdir.plot(xkey="z", ykeys=['sigma_x', 'sigma_y'], ykeys2=["ecnx", "ecny"])
Producing the output in :numref:`fig-elegant-fodo`:
.. _fig-elegant-fodo:
.. figure:: assets/elegant-fodo.png
Output of simple FODO tracking in **ELEGANT**