SIMION®
The field and particle trajectory simulator
Industry standard charged particle optics software
Refine your design: model > simulate > view > analyze > program > optimize it.
SIMION v8.1.3.0-TEST posted.
About | Documentation | Community/Support | Downloads | Ordering

Cookbook of SIMION User Programming Examples

This is a collection of common SIMION user programming techniques. See also Lua Cookbook.

Trajectory programming techniques

See Particle Trajectory Calculations for many programming examples dealing with particle trajectories and data recording.

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

My changes to adjustable variables are ignored.

Changes the user makes to adjustable variables on the Variables tab are only effective within segments.

Consider this example:

1:  simion.workbench_program()
2:  adjustable x = 1
3:  adjustable y = x
4:  print(x,y)
5:  function segment.initialize_run() print(x,y) end
6:

SIMION goes through these stages in processing the Lua code:

  • The Lua program is compiled and checked for syntax errors. Any adjustable variables (e.g. x) detected in compilation are added to the Variables tab, if they aren’t already. adjustable variables otherwise behave like Lua local variables.
  • The Lua file is executed from top to bottom. That Lua code is expected to do things like define Lua segments and the default values of adjustable variables.
    • Executing Line #1 tells SIMION it is a user program.
    • Executing Line #2 sets the default value of “x” to 1.
    • Executing Line #3 sets the default value of “y” to the then current value of x, which is 1.
    • Executing Line #4 prints “1 1”.
    • Executing Line #5 defines a segment, the content of which is not executed immediately.
  • After completing execution (Line #6), the then current values of the adjustable variables are used as the default values shown on the Variables tab, which the user may change.
  • SIMION re-assigns adjustable variables to values defined by the user on the Variables tab. For example, the user may set x to 10 and y to 11 from the Variables tab.
  • Finally, the Fly’m runs and calls the previously defined segments at appropriate stages of the Fly’m, which now print “10 11”.

If you want Line #4 to reflect user adjusted values on the Variables tab, you must move it inside a segment like in Line #5.

Testing the following workbench user program may make things clearer:

simion.workbench_program()
adjustable x = 1
print('A', x)  -- prints 1
x = 2
print('B', x)  -- prints 2
function segment.initialize()
  print('C', x)  -- prints 3 (or 4 on later executions)
  x = 4
  print('D', x)  -- prints 4
end
x = 3
print('E', x)  -- prints 3

The following example loads the HS1 collision model which defines an adjustable variable with pressure in terms of Pascal and instead defines it in terms of our own adjustable variable in units of Torr. Done properly, the original adjustable variable is set in the initialize_run segment.

simion.workbench_program()
local HS1 = simion.import 'collision_hs1.lua'
adjustable pressure_torr = 500
function segment.initialize_run()
  adjustable _pressure_pa = pressure_torr * (101325 / 760)
  HS1.segment.initialize_run()
end

Merging Segments

A common scenario is if you define the same segment twice, such as when copying and pasting someone else’s code into your own program:

-- main.lua
function segment.initialize()
  print('one')
end
function segment.initialize()
  print('two')
end

That does not do what you might intend. The second segment definition overwrites the former one. So, the code is equivalent to this:

-- main.lua
function segment.initialize() print('two') end

You could merge the segments yourself:

-- main.lua
function segment.initialize()
  print('one')
  print('two')
end

Another option is to create two different functions that are themselves called by the single initialize segment:

-- main.lua
function initialize1() print('one') end
function initialize2() print('two') end
function segment.initialize()
  initialize1()
  initialize2()
end

or even save a copy of the old initialize segment before redefining it and calling the old one from the new one:

-- main.lua
function segment.initialize() print('one') end
local old_initialize = segment.initialize
function segment.initialize()
  old_initialize()
  print('two')
end

This need more typically occurs if you reuse Lua libraries that define segments in separate files and don’t want to touch the original files.

-- one.lua
function segment.initialize() print('two') end
-- two.lua
function segment.initialize() print('two') end
simion.workbench_program()
simion.import 'one.lua'
local one_initialize = segment.initialize
simion.import 'two.lua'
local two_initialize = segment.initialize
function segment.initialize()
  one_initialize()
  two_initialize()
end

Another approach used is

-- one.lua
local M = {segment = {}}
function segment.initialize() print('two') end
return M
-- two.lua
local M = {segment = {}}
function segment.initialize() print('two') end
return M
simion.workbench_program()
local ONE = simion.import 'one.lua'
local TWO = simion.import 'two.lua'
function segment.initialize()
  ONE.segment.initialize()
  TWO.segment.initialize()
end

You may even define a utility function to merge segment tables from libraries into the main segments:

local function merge_segments(t)
  for name,newseg in pairs(t) do
    local oldseg = segment[name]
    segment[name] =
      oldseg and function() oldseg(); newseg() end
             or  newseg
  end
end
simion.workbench_program()
local ONE = simion.import 'one.lua'
local TWO = simion.import 'two.lua'
merge_segments(ONE.segment)
merge_segments(TWO.segment)

efield_adjust

Q: Within ``segment.efield_adjust``, both the potential (``ion_volts``) and potential gradient (``ion_dvoltsx_mm``, i.e. negative E field) are available to change. Should both always be updated? What happens if only the gradient is changed?

True, ion_volts is not used for the particle trajectory calculation. You can omit it. Someone might want to set it though if they are, for example, recording voltage in Data Recording and want to ensure the voltages are recorded are correctly.

Suppress fringe fields or compare to ideal field

Q: I am trying to understand how much fringe fields are affecting my beam. Is there a way to turn it off just to compare?

One way this is sometimes done is with an efield_adjust segment in a user program. The efield_adjust segment can override the field within the simulation (or some region within the simulation) with a user defined equation.

An efield_adjust segment like that is included (but commented out) in SIMION Example: quad to allow one to compare results of the real quadrupole field with an idealized quadrupole field. That may be more complex than what you need. Your code might be as simple as this:

simion.workbench_program()
function segment.efield_adjust()
  if ion_px_mm < 50 and ion_px_mm < 100 then
    -- define grad V = - E
    ion_dvoltsx_gu = 200
    ion_dvoltsy_gu = 0
    ion_dvoltsz_gu = 0
  end
end

(Click User Program on the Particles table, paste that into the text file, and Save the text file.)

That applies a uniform E field (-200 V/mm) in the X direction with a certain X range in mm.

Any comments on this web page? (will be sent to SIS SIMION Support)
[Optional] Your name: email: phone/fax: