# Time-Dependent Field¶

SIMION can handle low-frequency, time-dependent electric and magnetic fields.

The term “low-frequency” refers to frequencies that are low enough such that
radiation effects are not significant (the quasi-static approximation to
Maxwell’s equations, in which time derivatives are omitted). Generally, for
this to hold, the lengths of the electromagnetic waves (lambda = c /
frequency, where c is the speed of light) should be much larger than the
maximum length of your system. A number of the SIMION examples such as the
quadrupole and ion trap run at frequencies on in the **megahertz** range.
Simulating higher-frequency (radiation) effects requires different software
using different methods.

Usually, fields in SIMION are generated by applying voltages to electrodes. A user program may change electrode voltages during the simulation so that the field becomes time-dependent. A user program can apply any waveform desired, including sinusoidal or stepped. Regular PA files allow all voltages to be proportionately scaled by the same factor. To independently (non-proportionately) adjust potentials on individual electrodes, you must use a fast-adjustable potential array (PA# file), which makes use of the principle of superposition in an efficient way.

A number of the SIMION examples make use of time-dependent fields in this way:

- buncher lens - SIMION Example: buncher (stepped voltage)
- SIMION Example: rfdemo (oscillating) - simple RF examples
- quadrupole - SIMION Example: quad and octupole SIMION Example: octupole (oscillating)
- ion-trap - SIMION Example: trap (oscillating). Also see Paul Trap.
- ICR - SIMION Example: icrcell (oscillating)

The following simple SIMION user programs applies a sinusoidal voltage to electrode #1 in a fast-adjust potential array.

```
-- Lua example (SIMION 8 required)
simion.workbench_program()
adjustable ac_voltage = 500 -- AC voltage
adjustable dc_voltage = 10 -- DC voltage
adjustable omega = 100 -- angular frequency (radians per microsecond)
function segment.fast_adjust()
adj_elect01 = ac_voltage * sin(omega * ion_time_of_flight) + dc_voltage
end
```

Note: The `sin`

function expects an angle in radians, not degrees,
and `ion_time_of_flight`

has units of microseconds.
So

`sin(2 * math.pi * ion_time_of_flight)`

would have a period of 1 microsecond.`sin(2 * math.pi * 1E-6 * ion_time_of_flight)`

would have a period of 1 second.`sin(2 * math.pi * 1E-6 * f * ion_time_of_flight)`

would have a frequency of f Hz.

Some example code for generating an arbitrary stepped or segmented voltage profile is given in Applying a non-repeating segmented waveform potential.

Another method of generating fields is by setting the field vectors directly
via a user program (`efield_adjust`

and `mfield_adjust`

segments). These
fields can be static or time-dependent. This method is typically used if an
analytical expression is known for the field or if the field is defined by an
external data file.

When oscillating fields, make sure that your trajectory time-steps are small enough so that your particles “see” the oscillations. There should be at least a few time-steps per field oscillation.

For full details on time-dependent fields, see the SIMION manual, specifically
the sections on fast-adjustable PA# files and user programming appendix
concerning `fast_adjust`

, `efield_adjust`

, and `mfield_adjust`

segments. See also the examples mentioned above.

## Square waveforms¶

Here are two different quick approaches to generating square or rectangular waveforms.

The first approach utilizes the sinusoidal function. If the sin value is above some threshold (some number between -1 and 1), then one voltage is used; otherwise, another voltage is used. Here is a 50% duty cycle:

```
simion.workbench_program()
adjustable hz = 1000000 -- frequency in Hz
function segment.fast_adjust()
local f = math.sin((2*math.pi*hz*1E-6) * ion_time_of_flight)
adj_elect01 = (f >= 0) and 200 or -300
end
```

Here is for an arbitrary duty cycle:

```
simion.workbench_program()
adjustable hz = 1000000 -- frequency in Hz
adjustable duty = 0.8
function segment.fast_adjust()
local f = math.sin((2*math.pi*hz*1E-6) * ion_time_of_flight)
adj_elect01 = (f >= math.cos(math.pi * duty)) and 200 or -300
end
```

The second approach uses the remainder (`%`

) of the time divided by the period.
If this value is above a certain value, then one voltage is used;
otherwise, another voltage is used.
This approach probably is more straightforward, at least as the pulse
duration is defined directly.

```
simion.workbench_program()
adjustable hz = 1000000 -- frequency in Hz
function segment.fast_adjust()
local period = 1E6 / hz -- waveform period in microseconds
local f = ion_time_of_flight % period -- microseconds within current period
adj_elect01 = (f >= 0.3*period) and 200 or -300
end
```

Please note that the above two approaches do not adjust time-steps to accurately end on voltage transitions. Rather, time-steps may straddle voltage transitions. To improve this, you would need a tstep_adjust segment, or code like SIMION Example: waveform which does it for you with care. Alternately, just use small time-steps (large T.Qual) or even dynamically reducing time-step sizes (positive T.Qual) to mitigate the error somewhat without additional code (but experiment to see to what extent varying the T.Qual value affects results to see if error is significant).

A square wave can also be approximated by combinations of sine waves, and this may be done also in practice in the electronics, like approximating a square wave with a bisinuoidal waveform (see SIMION Example: faims (bisinusoidalwavelib.lua)).

## Triangular waveforms¶

A sawtooth waveform based on right triangles:

```
adj_elect01 = (ion_time_of_flight / (1E6/_frequency_hz)) % 1
```

The (1E6/_frequency_hz) is the period in microseconds. “% 1” is the fractional remainder when divided by one, thereby generating the sawtooth pattern.

A sawtooth waveform based on isosceles triangles:

```
adj_elect01 = math.abs(4 * ((ion_time_of_flight / (1E6/_frequency_hz)) % 1) - 2) - 1
```

## Applying a non-repeating segmented waveform potential¶

These user programs apply a non-repeating wave-form voltage to an electrode such that the waveform consists of a series of line segments. Both SIMION 8 (Lua) and SIMION 8 (PRG) versions of the program are included.

```
-- SIMION 8.0 user program (Lua) to create non-repeating
-- waveform of line segments.
--
-- Note: for better accuracy, also ensure that your time-steps are
-- sufficiently small relative to the waveform. For example, impose a
-- sufficiently large negative (or positive) trajectory quality control
-- value, enable time step markers, or add a tstep_adjust segment here.
--
-- D.Manura-2006-11--Rev.2
simion.workbench_program()
-- Here's where the waveform is defined as a list
-- of ordered pairs (t, v) of times (t) and voltages (v).
local wave = {
{0, 20}, -- initial
{10, 20},
{20, 60},
{30, 60},
{30, 30},
{math.huge, 30} -- infinity
}
function segment.fast_adjust()
-- For improved accuracy, define time t at the mid-point of the
-- time-step. The computed potential at this time will then
-- be the accumulated average potential over the entire time-step.
local t = ion_time_of_flight + ion_time_step * 0.5
-- Locate current line segment [n-1, n] of the waveform.
local n
for m = 1, #wave do
n = m; if wave[m][1] > t then break end
end
-- Obtain points (t1,v1) and (t2,v2) of that line segment.
local t1, t2, v1, v2 = wave[n-1][1], wave[n][1], wave[n-1][2], wave[n][2]
-- Linearly interpolate potential over the line segment.
local v = v1 + (t - t1) * ((v2-v1)/(t2-t1))
-- Store voltage.
adj_elect01 = v
end
```

See also SIMION Example: waveform.

## Square Waveform with Gradual Rise/Decay (Sigmoid)¶

```
simion.workbench_program()
adjustable period = 2
adjustable duty = 0.5
adjustable V2 = 100
adjustable scale = 50
function voltage(time)
local f = time % period
local f1 = duty * 0.5
local f2 = (duty + 1)*0.5
if f < duty then
return V2 * (1/(1 + exp(-(f - f1)*scale)))
else
return V2 * (1 - 1/(1 + exp(-(f - f2)*scale)))
end
end
function segment.fast_adjust()
adj_elect[1] = voltage(ion_time_of_flight)
if ion_number == 1 then
end
end
function segment.initialize_run()
fh = assert(io.open("wave.csv", "w"))
for i=0,1000 do
local t = i/10
fh:write(t, ",", voltage(t), "\n")
end
fh:close()
end
```

This uses a sigmoid function (Wikipedia:Sigmoid_function) in each half-period. One is centered around duty * 0.5. The other is centered around (duty + 1)*0.5 and inverted. It is all defined in terms of the variable “f” which cycles between 0 and 1, using the modulus operator (%).

## Older Code Examples¶

SIMION 7.0 version of sinusoidal field:

```
; PRG example (SIMION 7 compatible)
DEFA ac_voltage 500 ; AC voltage
DEFA dc_voltage 10 ; DC voltage
DEFA omega 100 ; angular frequency (radians per microsecond)
SEG fast_adjust
RCL omega RCL ion_time_of_flight * SIN RCL ac_voltage * RCL dc_voltage +
STO adj_elect01
# SL example (SIMION 7+SL compatible)
adjustable ac_voltage = 500 # AC voltage
adjustable dc_voltage = 10 # DC voltage
adjustable omega = 100 # angular frequency (radians per microsecond)
sub fast_adjust
adj_elect01 = ac_voltage * sin(omega * ion_time_of_flight) + dc_voltage
endsub
```

SIMION 7.0 version of waveform defined by line segments.

```
; SIMION 7.0 user program (PRG) to create non-repeating
; waveform of line segments.
;
; Note: for better accuracy, also ensure that your time-steps are
; sufficiently small relative to the waveform. For example, impose a
; sufficiently large negative (or positive) trajectory quality control
; value, enable time step markers, or add a tstep_adjust segment here.
;
; D.Manura-2006-09--Rev.1
DEFS wave_num_points 6 ; size of array
ADEFS wave_times 6 ; note: array values in microseconds
ADEFS wave_voltages 6 ; note: array values in volts
DEFS is_initialized 0
SEG fast_adjust
; Here's where the waveform is defined as a list
; of ordered pairs (t, v) of times (t) and voltages (v).
; if not is_initialized then
RCL is_initialized X!=0 GTO skip1
1 STO is_initialized
0 1 ASTO wave_times 20 1 ASTO wave_voltages
10 2 ASTO wave_times 20 2 ASTO wave_voltages
20 3 ASTO wave_times 60 3 ASTO wave_voltages
30 4 ASTO wave_times 60 4 ASTO wave_voltages
30 5 ASTO wave_times 30 5 ASTO wave_voltages
1E99 6 ASTO wave_times 30 6 ASTO wave_voltages ; infinity
LBL skip1
; end
; For improved accuracy, define time t at the mid-point of the
; time-step. The computed potential at this time will then
; be the accumulated average potential over the entire time-step.
; t = ion_time_of_flight + ion_time_step * 0.5
RCL ion_time_of_flight RCL ion_time_step 0.5 * + STO t
; Locate current line segment [n-1, n] of the waveform.
; for n = 1, wave_num_points do
1 STO n LBL loop1
RCL wave_num_points RCL n X>Y GTO skip2
; if wave_times[n] > t then break
RCL t RCL n ARCL wave_times
X>Y GTO skip2
; n = n + 1
RCL n 1 + STO n
GTO loop1
LBL skip2
; end
; Obtain points (t1,v1) and (t2,v2) of that line segment.
RCL n 1 - ARCL wave_times STO t1 ; t1 = wave_times[n-1]
RCL n ARCL wave_times STO t2 ; t2 = wave_times[n]
RCL n 1 - ARCL wave_voltages STO v1 ; v1 = wave_times[n-1]
RCL n ARCL wave_voltages STO v2 ; v2 = wave_times[n]
; Linearly interpolate potential over the line segment.
; v = v1 + (t - t1) * ((v2-v1)/(t2-t1))
RCL v1
RCL t RCL t1 -
RCL v2 RCL v1 - RCL t2 RCL t1 - /
* +
STO v
; Store voltage.
RCL v STO adj_elect01 ; v = adj_elect01
```