This example differs from *A first example: fodo_simple1* only in the way
the lattice is defined. Instead of reading from a lattice file, we define
the lattice file directly in Synergia.

The simulation itself is defined by the Python script
`fodo_simple2.py`.

`fodo_simple2.py`:

```
# !/usr/bin/env synergia
import synergia
from synergia.foundation import Four_momentum, Reference_particle, pconstants
from synergia.lattice import Lattice_element, Lattice, Mad8_adaptor_map
from synergia.bunch import Bunch, Diagnostics_basic
from synergia.simulation import Independent_stepper_elements, Bunch_simulator, \
Propagator
# Set the lattice element parameters
focus_length = 7 # meters
quad_sep = 10 # meters
quad_length = 2.0 # meters
strength = 1.0 / (focus_length * quad_length) # 1/meters^2
# Define the lattice elements
o = Lattice_element("drift", "o")
o.set_double_attribute("l", quad_sep - quad_length)
f = Lattice_element("quadrupole", "f")
f.set_double_attribute("l", quad_length)
f.set_double_attribute("k1", strength)
d = Lattice_element("quadrupole", "d")
d.set_double_attribute("l", quad_length)
d.set_double_attribute("k1", -strength)
# Define the fodo lattice itself, interpreting elements
# by their Mad8 definitions
lattice = Lattice("fodo", Mad8_adaptor_map())
# Add copies of the lattice elements to the fodo lattice
lattice.append(f)
lattice.append(o)
lattice.append(d)
lattice.append(o)
# Define a reference particle
total_energy = 1.5 # GeV
four_momentum = Four_momentum(pconstants.proton_mass, total_energy)
reference_particle = Reference_particle(pconstants.proton_charge,
four_momentum)
lattice.set_reference_particle(reference_particle)
# Define a set of simulation steps
map_order = 1
steps_per_element = 2
stepper = Independent_stepper_elements(lattice, map_order, steps_per_element)
# Define a bunch
x_emit = 1.0e-6 # m-rad, RMS
y_emit = 1.0e-6 # m-rad, RMS
z_std = 0.01 # m
dpop = 1.0e-4 # unitless, RMS \frac{\delta p}{p_{tot}}
real_particles = 1.2e12 # unitless, meaningless in this simulation
# without collective effects
macro_particles = 50000
seed = 1415926 # random number seed; 0 for automatic calculation (GSL)
bunch = synergia.optics.generate_matched_bunch_transverse(
stepper.get_lattice_simulator(),
x_emit, y_emit, z_std, dpop,
real_particles, macro_particles,
seed=seed)
# Define a bunch simulator
bunch_simulator = Bunch_simulator(bunch)
# Define a set of bunch diagnostics
# Apply basic diagnostics every step
diagnostics = Diagnostics_basic("diagnostics.h5")
bunch_simulator.add_per_step(diagnostics)
# Perform the simulation
propagator = Propagator(stepper)
turns = 1 # a single pass through the line, since this isn't a ring
max_turns = 0 # Number of turns to run before writing checkpoint and stopping
# When max_turns is 0, the simulation continues until the end.
verbosity = 2 # Display information about each simulation step
propagator.propagate(bunch_simulator, turns, max_turns, verbosity)
```

The Synergia `Lattice_element` class contains a general set of
information about an element. Each element has a name, a type, a set of
attributes with numerical values and a set of attributes with string values.
Here we define focusing and defocusing quadrupoles (`f` and `d`,
respectively) and a drift (`o`) with length and strength parameters
as used in Mad8.

The lattice element parameters are only given meaning when they are converted
into an internal implementation representation through an `Element_adaptor`.
When we define the `Lattice`, we give a name for the lattice and
an object of type `Element_adaptor_map`, which contains a mapping from
element types to objects of type `Element_adaptor`. Here we tell the lattice
that the elements are to be interpreted as Mad8 elements by passing it an object of type
`Mad8_adaptor_map`. There are built-in
adaptor maps for Mad8 and MadX elements. The adaptor maps can be extended to accommodate
new element types, or modified to change the implementation behavior of elements.

The `Lattice` class contains an ordered list of elements. Each element is unique.
The uniqueness is enforced by storing a *copy* a copy of an element when it is appended
to the lattice. This means that modifying, say, the `f` object *after* it is appended
to the lattice will not affect the `f` in the lattice itself. It also means that
the lattice contains two different drifts named “o”. (In practice, it would have been better
to name the first drift “o1” and the second drift “o2”.

In `fodo.lat` the beam energy was defined by the `BEAM` statement.
Within Synergia, the `Lattice` class contains an object of type
`Reference_particle`. Dealing with the various relations between relativistic
velocity, momentum and energy is made simpler by the use of the `Four_momentum`
class, which does all the relevant conversions.

Now that the lattice object has been properly defined, the remainder of the example
is exactly the same as *A first example: fodo_simple1*.