Cookbook of SIMION User Programming Examples¶
This is a collection of common SIMION user programming techniques. See also Lua Cookbook.
Get number of particles¶
A user program may want to know the number of particles in the simulation, such as for computing a percent ion transmission:
simion.workbench_program()
simion.early_access(8.2)
local nhit = 0
function segment.terminate()
if ion_px_mm > -5 and ion_py_mm < 5 then -- acceptance region
nhit = nhit + 1
end
end
function segment.terminate_run()
print('% transmission:', nhit / sim_ions_count * 100)
end
In Early Access Mode (8.2) you can obtain this via the sim_ions_count reserved variables.
In SIMION 8.0/8.1 (a similarly in 7.0), a workaround is this:
local nions = 0
function segment.initialize()
nions = ion_number
end
The initialize segment gets called for each particle that is created inside a PA instance. Assuming all particles are created inside a PA instance, after all initialize segments are run, nions will contain the last ion number, which also is the number of particles. You will need to ensure particles are created inside PA instances to make this work (or add an empty PA instances there).
Get/set kinetic energy of current particle¶
In Early Access Mode (8.2) you can obtain the current particle’s kinetic energy via the ion_ke reserved variables.
In SIMION 8.0/8.1 (a similarly in 7.0), you can use this:
local function get_ke()
local speed = math.sqrt(ion_vx_mm^2 + ion_vy_mm^2 + ion_vz_mm^2)
return speed_to_ke(speed, ion_mass)
end
local function set_ke(ke)
local speed = math.sqrt(ion_vx_mm^2 + ion_vy_mm^2 + ion_vz_mm^2)
assert(speed ~= 0)
local new_speed = ke_to_speed(ke, ion_mass)
local scale = new_speed/speed
ion_vx_mm = ion_vx_mm * scale
ion_vy_mm = ion_vy_mm * scale
ion_vz_mm = ion_vz_mm * scale
end
Ensure time steps terminate on multiples of “step”¶
This code ensures that time step boundaries fall on multiples of time step_size. For example, 0.001, 0.002, 0.0003, etc. microseconds. SIMION may still insert additional time-steps unless you set the trajectory quality (TQual) to zero. Another way to do this, without programming, is to enable “time markers” every 0.001 microseconds (Particles tab on SIMION 8.0 or ion definition screen in SIMION 7.0).
-- Lua example (SIMION 8.0)
simion.workbench_program()
adjustable step_size = 0.001 -- microseconds
function segment.tstep_adjust()
ion_time_step = math.min(ion_time_step, step_size)
end
Older SIMION 7.0 examples:
; PRG example
DEFA step_size 0.001 ; microseconds
SEG tstep_adjust
; max_step = (1 - frac(ion_time_of_flight / step_size)) * step_size
RCL ion_time_of_flight RCL step / FRAC CHS 1 + RCL step * STO max_step
; if ion_time_step > max_step
RCL ion_time_step RCL max_step X>=Y GTO skip1
; max_step = ion_time_step
RCL max_step STO ion_time_step
LBL skip1
# SL example
adjustable step_size = 0.001 # microseconds
sub tstep_adjust
max_step = (1 - frac(ion_time_of_flight / step_size)) * step_size
if ion_time_step > max_step
max_step = ion_time_step
endif
endsub
Kill particle if time of flight reaches some maximum¶
The following code will cause particles to terminate precisely upon time of flight (TOF) reaching some maximum value specified. You may find this code useful to limit the duration in which particles fly, especially if particles would normally fly on essentially forever such as in an ion-trap/ICR.
We use a tstep_adjust segment here to ensure that a time-step ends exactly at time max_time given (this may alternately be done by enabling a time marker to occur at max_time). This is not essential but does improve the preciseness of the termination time. Then we have an other_actions segment to cause the particle to terminate if the time of flight is reached in the current time step. Some care is taken to ensure we terminate precisely on the correct time step. Use data recording to record TOF on ion splat to verify this works.
The following is a Lua version of the program (requires SIMION 8):
simion.workbench_program()
adjustable max_time = 2 -- maximum time of flight in microseconds
-- Overrides time-step size.
function segment.tstep_adjust()
-- Ensure that a time-step boundary exactly coincids
-- with max_time (for increased precision).
if ion_time_of_flight < max_time then
ion_time_step = min(ion_time_step, max_time - ion_time_of_flight)
end
end
-- This segment is called on each time-step.
local is_splat_next = false -- flag indicating splat scheduled on next time-step
function segment.other_actions()
-- Splat particle if previously scheduled.
if is_splat_next then ion_splat = -1 end
-- If the end of current time-step exceeds max_time,
-- then finish current time-step but schedule a splat
-- on start of next time-step.
if ion_time_of_flight >= max_time then
is_splat_next = true
end
-- Reset for next particle.
if ion_splat ~= 0 then is_splat_next = false end
end
The following is an equivalent PRG version of the program (compatible with SIMION 7):
defa max_time 2 ; maximum time of flight in microseconds
defs is_splat_next 0 ; flag indicating splat scheduled
; on next time-step (1=YES)
-- Overrides time-step size.
SEG tstep_adjust
; Ensure that a time-step boundary exactly coincides
; with max_time (for increased precision).
RCL max_time RCL ion_time_of_flight
X>=Y GOTO skip1
RCL ion_time_step +
X<=Y GOTO skip1
RCL max_time RCL ion_time_of_flight - STO ion_time_step
LBL skip1
; This segment is called on each time-step.
SEG other_actions
; Splat particle if previously scheduled.
RCL is_splat_next X=0 GOTO skip2
-1 STO ion_splat
LBL skip2
; If the end of current time-step exceeds max_time,
; then finish current time-step but schedule a splat
; on start of next time-step.
RCL max_time RCL ion_time_of_flight X<Y GOTO skip1
1 sto is_splat_next
LBL skip1
Periodically update potential energy display¶
This code causes the potential energy (PE) view to update every update_pe_period microseconds. This is useful in conjunction with a fast_adjust segment that changes electrode potentials during the fly’m.
SIMION 8.0 example (Lua):
simion.workbench_program()
local pe_update_period_usec = 0.010 -- microseconds
local last_pe_update = 0
function segment.other_actions()
if abs(ion_time_of_flight - last_pe_update) >= pe_update_period_usec then
last_pe_update = ion_time_of_flight
sim_update_pe_surface = 1 -- require PE update
end
end
SIMION 7.0 example (not exactly equivalent):
; PRG example
DEFA update_pe_period 0.010 ; microseconds
DEFS next_pe_update_time 0
SEG other_actions
; if ion_time_of_flight >= next_pe_update_time
RCL ion_time_of_flight RCL next_pe_update_time X>Y GTO skip1
; next_pe_update_time = ion_time_of_flight + update_pe_period
RCL ion_time_of_flight RCL update_pe_period + STO next_pe_update_time
; update_pe_surface = 1
1 STO update_pe_surface
LBL skip1
# SL example
adjustable update_pe_period = 0.010 # microseconds
static next_pe_update_time = 0
sub other_actions
if ion_time_of_flight >= next_pe_update_time
next_pe_update_time = ion_time_of_flight + update_pe_period
update_pe_surface = 1
endif
endsub
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
; 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
See also the SIMION “waveform” example (added 2009-11-07).
