Workbench Program Extensions in SIMION 8.1

This page describes SIMION workbench user program capabilities added recently in SIMION 8.1 These features are not yet described in the printed manual. Some capabilities added to latter 8.0.x versions are also described here.

Note

This page is abridged from the full SIMION "Supplemental Documentation" (Help file). The following additional sections can be found in the full version of this page accessible via the "Help > Supplemental Documentation" menu in SIMION 8.1.1 or above:
  • Segment Flow Chart

Segments

The following new segments are available.

segment.flym()

The segment.flym segment is the main routine executed when you do a Fly’m. It is typically used to implement a loop that performs a series of runs (invoked by the run() command). For example:

simion.workbench_program()
local V1
function segment.flym()
  for i=1,10 do  -- Do 10 runs, stepping through voltages.
    V1 = 100*i
    run()
  end
end
function segment.initialize_run()
  print('running with V1=', V1)
end
function segment.fast_adjust()
  adj_elect01 = V1
end

You can place nearly whatever you want inside the flym segment. For example, function segment.flym() print 'Hello.' end will print “Hello.” and not fly any trajectories. However, typically, the flym segment will contain at least one run() command, which does an entire particle tracing run, including invoking all necessary segments from segment.initialize_run() to segment.terminate_run() and everything in-between. If the particle tracing run fails, then run() will raise an error. run() can only be called inside a flym segment (and will raise an error otherwise). You may also wish to do things like invoke Refine (simion.pas pa:refine()) between runs.

If the flym segment is not specified, then the Fly’m behavior is roughly equivalent to the following code:

function segment.flym()
  repeat
    run()  -- This may set sim_rerun_flym to 1 or 0.
  until sim_rerun_flym == 0
end

Note also that this:

function segment.flym()
  for i =1, 3 do
    f()
    run()
    g()
  end
end

is similar to this:

function segment.flym()
  for i = 1, 3 do
    run()
  end
end
function segment.initialize_run()
  f()
end
function segment.terminate_run()
  g()
end

The following reserved variables are accessible from the flym segment:

  • sim_trajectory_image_control (read/write) – affects next run()

  • sim_rerun_flym (read/write) – affects next run()

  • sim_trajectory_quality (read/write)

  • sim_repulsion (read/write)

  • sim_repulsion_amount (read/write)

  • ion_run (read) – number of previous run(). 0 if run() not yet called

  • sim_retain_changed_potentials (write) – retains from previous run()

Note: no PA instance or ion specific variables are accessible from the flym segment.

It can be useful to disable retaining trajectories across runs (to avoid filling the disk and screen):

function segment.flym()
  sim_trajectory_image_control = 1  -- view but not retain trajectories
  for i=1,10 do
    run()
  end
  sim_trajectory_image_control = 0  -- view and retain trajectories for next run
  run()
end

A similar but not identical code is

function segment.flym()
  sim_rerun_flym = 1  -- enable "rerun" mode
  for i=1,10 do
    run()
  end
  sim_rerun_flym = 0  -- disable "rerun" mode
  run()
end

Note: in “rerun” mode, trajectories are not retained either (regardless what sim_trajectory_image_control is set to) and data recording is not saved to disk. Furthermore, enabling rerun mode will clear any trajectores that may have previously been retained. The SIMION Example: geometry_optimization and SIMION Example: tune: take these points into account.

In previous versions of SIMION (e.g. 8.0), the control logic for the for i=1,10 do in examples above had to instead be incorporated into the initialize and terminate segments like this:

-- initialize_run/terminate_run example
-- (which can be written more simply just using `flym`)
simion.workbench_program()
local V1
local nrun = 0
function segment.initialize_run()
  nrun = nrun + 1
  V1 = 100*nrun
  print('running with V1=', V1)
end
function segment.fast_adjust()
  adj_elect01 = V1
end
function segment.terminate_run()
  sim_rerun_flym = (nrun < 10) and 1 or 0
end
-- Note: In 8.0, initialize_run/terminate_run also had to be
-- rewritten in terms of initialize/terminate segments.
-- Note: In 8.1, ion_run is incremented automatically.

which can be more awkward, especially for more complicated, nested loops. In SIMION 8.0 it was actually possible to rewrite that with Lua coroutines to invert the control logic to make it more similar to the SIMION 8.1 flym segment, but this was rarely done and still is not as simple nor as general (e.g. doesn’t support zero iterations of the loop):

-- coroutine example
-- (which can be written more simply just using `flym`)
simion.workbench_program()
local V1
local mycoroutine = coroutine.wrap(function()
  for i=1,10 do
    V1 = i
    coroutine.yield()
  end
end)
function segment.initialize_run()
  mycoroutine()
end
function segment.fast_adjust()
  adj_elect01 = V1
end
-- Note: In 8.0, initialize_run/terminate_run also had to be
-- rewritten in terms of initialize/terminate segments.

Compatibility: The flym segment was added in 8.1.0.40. Previous versions would raise an error on attempt to define this segment. Supported in old PRG in 8.1.1.0 (with new RUN command).

segment.initialize_run()

The initialize_run segment is called exactly once prior to the start of each run (or rerun). It is called before any calls to the initialize segment are made. In contrast, the initialize segment is called each time a particle is initialized inside a PA instance and (unlike the initialize_run segment) has access to reserved variables pertaining to those particles and PA instances. The initialize_run segment is suitable for initialization code that must be called exactly once, whereas the initialize segment is suitable for code that reads/writes particle data via reserved variables.

For example:

simion.workbench_program()
function segment.initialize_run()
  print 'begin1'
end
function segment.initialize()
  print('begin2', ion_instance, ion_number)
end

The 'begin1' is printed exactly once on the beginning of the run. However, the number of times 'begin2' is printed is less clear. If you have 10 particles all created inside an electrostatic PA instance, 'begin2' will be printed 10 times (i.e. once per particle). If all particles are inside a magnetic PA instance as well, 'begin2' will be printed 20 times. If, however, all particles are started outside of PA instances or if you have zero particles, then 'begin2' is never printed.

Note also that the initialize segment has access to reserved variables pertaining to the current particle (e.g. ion_number) and current PA instance (e.g. ion_instance). The initialize_run segment does not have the concept of current particle and current PA instance because it is always called exactly once regardless of the number of particles and which (if any) PA instances those particles are located in. The intialize_run segment does, however, have access to these reserved variables:

The main advantage of initialize_run is that you can be sure it is called exactly once. This can make certain types of initialization code simpler and more robust. In previous (8.0) versions of SIMION, a workaround was to do things like this:

simion.workbench_program()
function segment.initialize()
    if ion_instance == 1 and ion_number == 1 then
        print 'begin1'
    end
    -- warning: This SIMION 8.0 workaround fails if particle #1 is
    --          not created inside PA instance #1.
end

Compatibility: The initialize_run segment was added in 8.1.0.40. Previous versions would raise an error on attempt to define this segment.

segment.terminate_run()

The terminate_run segment is called exactly once after the end of each run (or rerun). It is called after any calls to the terminate segment are made. The relationship between terminate_run and terminate segments is analogous to the relationship between initialize_run and initialize, so see the segment.initialize_run() documentation for further details.

The terminate_run segment has access to these reserved variables:

Compatibility: The terminate_run segment was added in 8.1.0.40. Previous versions would raise an error on attempt to define this segment.

segment.load()

The load segment is only called when the workbench IOB file is first loaded, which occurs prior to any number of Fly’m(s). In particular, unlike most other segments, the load segment is never called during the Fly’m. Note that at the end of a Fly’m, some variables like sim_rerun_flym and adjustable variables are reset to their values prior to the Fly’m, which can include values set in a load segment.

A load segment is useful for performing on the workbench some initial configuation that does not need to be redone each time the Fly’m button is pressed. Perhaps you are setting up a default option that the user should still be able to change prior each Fly’m. For example, you might set sim_trajectory_quality (T.Qual) in a load segment rather than in an initialize_run segment since the former will set T.Qual to a recommended initial value upon loading the IOB, whereas the latter will force the value regardless regardless what the user later selects for T.Qual on the Particles tab on the View screen. The load segment may also be useful for performing computationally intensive things like refining a PA in a special way (simion.pas pa:refine()) that should not be performed on each Fly’m. Establishing a connection to Excel may be another case. Beware, however, that unless you have good reason otherwise, you should probably prefer to place code inside an initialize_run segment rather than a load segment since the former tends to ensure that clicking the Fly’m button more than once will tend to give reproducible results because execution will depend less on past history.

If the IOB is loaded during a fly batch mode command (e.g. from simion.command or the command line), any adjustable variable values passed to it via the --adjustable argument are applied prior to the execution of segment.load().

The load segment has access to these reserved variables:

Compatibility: The load segment was added in 8.1.1.0. Previous versions would raise an error on attempt to define this segment.

segment.instance_adjust()

The instance_adjust segment always you to reorder the PA instance priorities during a run.

Background: Normally when PA instances overlap, the particle only sees the highest priority electric PA instance and highest priority magnetic PA instance (if any) the particle is located within. PA instances priority is controlled via the L-/L+ buttons on the View screen PAs tab.

Why might you want to reorder PA instances? One possibility is that you represent a certain region of your electrodes within two PA instances that overlap, but the far ends of the overlap region (where the PA’s are cut) the fields might not be accurate. But you can use an instance_adjust segment to suppress those inaccurate regions.

For example, if you have two partially overlapping PA instances (PA instance #2 partly overlaps #1), you might do something like this:

simion.workbench_program()

function segment.instance_adjust()
  if ion_instance == 2 then
    if ion_px_mm > 10 then
      ion_instance = 0  -- suppress
    end
  end
end

What that means is that if the current particle finds itself inside PA instance #2 and the particle position is in the region x > 10 mm, then the PA instance #2 will be suppressed at that location. SIMION will then try then next available PA instance (#1) containing the particle and since the instance_adjust segment does not suppress instance #1 in that region, SIMION is free to show that to the particle. SIMION will proceed to use instance #1 to calculate the field and can consult segments (e.g. fast_adjust, efield_adjust, mfield_adjust) to help compute that field for the particle.

The ion_instance variable is initially set to the currently inferred PA instance number (positive integer). Setting it to 0 will suppress the current PA instance. It is invalid to set ion_instance to any other value. If you want a certain PA instance to be active, suppress all PA instances called by ion_instance having higher higher priority numbers. There is never a need to suppress PA instance #1 unless you want a particle to see no PA instances (empty space).

The following reserved variables are accessible from this segment:

Simulation (read):
  sim_grouped, sim_relativity, sim_repulsion_amount, sim_trajectory_quality,
  sim_segment_global, sim_repulsion
Flym/Run properties (read):
  ion_run, sim_ions_count
PA instance variables (read/write):
  ion_instance
Particle properties (read):
  ion_color
  ion_charge
  ion_cwf
  ion_mass
  ion_number
  ion_px_mm, ion_py_mm, ion_pz_mm
  ion_time_of_flight

Compatibility: Added in 8.2EA-20170214.

See also Programmatically Controlling PA Instance Priority.

Reserved Variables

The following new reserved variables are now supported.

ion_cwf

The ion_cwf reserved variables represents the charge weighting factor (CWF) for the current particle. CWF values, which are normally used for the charge repulsion feature and default to 1, are originally defined in the particle definitions.

Permissions: ion_cwf has the same segment access permissions as ion_charge.

Tip: As of SIMION 8.0.4-TEST19, the CWF can also be selected for data recording. The REC format has been extended to support this selection. This capability also allows the following useful trick. Any CWF specified in the particle definitions is readable/writable via the user program ion_cwf reserved variable and also carries over to the CWF field in the Data Recording output. This is true even if charge repulsion effects are disabled, in which case SIMION otherwise ignores the CWF field, but you may use the CWF field for your own purpose to carrying over user-defined data concerning individual particles into the user program and/or data recording output.

Compatibility: Added in 8.0.4-TEST19. Added in PRG in 8.0.5-TEST8. Issue-I 452. Documented in the 8.0.4 manual.

ion_effective_charge

The ion_effective_charge variable represents the real (physical) charge represented by the particle in elementary charge units. It only differs from ion_charge only when using the charge repulsion features.

In comparison, the ion_charge variable, which is the same as the charge specified in the particle definitions, contains the per-particle charge in elementary charge units (e.g. Carbon is 12 e). However, when charge repulsion features are used (Beam, Coulombic, or Factor repulsion), each traced particle can represent more than one real particle (possibly millions). The precise number is controlled by the global charge repulsion amount on the Particles tab (or sim_repulsion_amount) and the charge-weighting-factors on the individual particles (or ion_cwf). The ion_effective_charge variable takes these into account.

When Coulombic or Factor repulsion is used, each particle traced represents a point (or cloud) of charges, and ion_effective_charge represents the total charge, in elementary charge units, of that point.

When Beam repulsion is used, each particle trajectory instead represents a beam of current (in A). Current is a charge per time (where time=distance/velocity), so it’s not as clear how best to define ion_effective_charge. We choose ion_effective_charge to represent the charge, in elementary charge units, in the current time-step. Current (in A) can be obtained by 1.6021773349E-19 C * ion_effective_charge / (ion_time_step * 1E-6 s). (In the initialize segment, ion_time_step is not defined; but we arbitrary treat it as 1 microsecond in the above formula.)

Permissions: ion_effective_charge can be read anywhere ion_charge can be read. It is not writable.

Compatibility: Added in 8.1.0.0. Issue-I 452.3

ion_ke

This contains the kinetic energy (in eV) of the current particle. The particle kinetic energy can be read from or written to this variable. If it is written to, the ion_vx_mm, ion_vy_mm, and ion_vz_mm are updated accordingly.

Permissions: ion_ke has the same permissions as ion_vx_mm. Writing a non-zero value to ion_ke when ion_ke is zero will raise an error because a zero KE does not define a velocity direction.

Reading ion_ke is a short-hand for this:

speed_to_ke(math.sqrt(ion_vx_mm^2 + ion_vy_mm^2 + ion_vz_mm^2), ion_mass)

See also Get/set kinetic energy of current particle.

Compatibility: Added in 8.2.

ion_run

The ion_run reserved variable represents the current run number (integer >= 1). It is incremented automatically on each rerun.

Permissions: It is readable in all segments and writable in none.

ion_run should not be used at the top-level (which is prior to the first run), although presently it equals 0 in that case.

Example:

simion.workbench_program()
function segment.initialize_run()
  print('run number=', ion_run)
  sim_rerun_flym = 1
end

The ion_run variable can also be used in the data recording settings to splitting data recording output into multiple files (see Issue-I 231).

Earlier (8.0) versions of SIMION without this variable may use the following workaround:

simion.workbench_program()
local nrun = 0
function segment.initialize()
  if ion_number == 1 and ion_instance == 1 then
    nrun = nrun + 1
    print('run number=', nrun)
  end
end
-- warning: This SIMION 8.0 workaround fails if particle #1 is
--          not created inside PA instance #1.

Compatibility: Added in 8.1.0.0. Issue-I 529.

sim_grouped

The sim_grouped reserved variable is 1 if Grouped particle flying is enabled (i.e. all particles flown at the same time) or 0 if Grouped flying is disabled (i.e. particles flown one after another).

Permissions: Reading is allowed in all segments. Writing is allowed in the top-level, load, flym, and initialize_run segments (i.e. before particles are created).

Setting this variable to 0 will automatically change sim_repulsion to 'none' (i.e. disable repulsion effects).

Note: The grouped mode can also be set via the fly --grouped batch mode parameter (see Appendix M of the manual).

Compatibility: Added in SIMION 8.1.0.43.

sim_relativity

The sim_relativity defines whether relativistic effects are on (1=yes, 0=no) for the Fly’m and functions like simion.speed_to_ke() and simion.ke_to_speed(). This is also tied to the “omit relativistic effects” option on the Particles Tab “more” panel.

Permissions: Reading is allowed in all segments. Writing is allowed in the top-level, load, flym, and initialize_run segments (i.e. before particles are created). Inaccessible from PRG.

Note: The relativity can also be set via the fly --omit-relativistic-effects batch mode parameter (see Appendix M of the manual).

Compatibility: Added in 8.2. Not accessible in PRG.

sim_repulsion

The sim_repulsion reserved variable contains the charge repulsion method currently selected: 'none', 'beam', 'coulomb', or 'factor'.

Permissions: Reading is allowed in all segments. Writing is allowed in the top-level, load, flym, and initialize_run segments (i.e. before particles are created). Inaccessible from PRG.

Setting this variable to anything other than 'none', will automatically change sim_grouped to 1.

Note: The repulsion type can also be set via the fly --repulsion batch mode parameter (see Appendix M of the manual).

Compatibility: Added in SIMION 8.1.0.43. Not accessible in PRG.

sim_repulsion_amount

The sim_repulsion_amount reserved variable represents the charge repulsion amount in the charge repulsion options. It’s meaning depends on the current value of sim_repulsion and you should set this variable only after after setting sim_repulsion. The behavior of this variable is unspecified if sim_repulsion is 'none'.

Permissions: Reading is allowed in all segments. Writing is allowed in the top-level, load, flym, initialize/initialize_run, other_actions, and terminate/terminate_run segments.

Note: The repulsion amount can also be set via the fly --repulsion-amount batch mode parameter (see Appendix M of the manual).

Compatibility: Added in SIMION 8.1.0.0. Issue-I 452.2

sim_trajectory_quality

The sim_trajectory_quality represents the trajectory quality factor (“T.Qual”). It has a range -500 to +500.

Permissions: Reading is allowed in all segments. Writing is allowed in the top-level, load, initialize/initialize_run, other_actions, and terminate/terminate_run segments.

Compatibility: Added in 8.0.3. Access from PRG added in 8.0.5-TEST8. Access from top-level added in 8.0.5-TEST10. Issue-I 370.

sim_ions_count

The sim_ions_count contains the current number of particles in the Fly’m. Particles splatted or not yet born are included in this count. See also Counting number of particles.

Permissions: Reading is allowed in all segments except load, flym, and the top-level. Writing is not allowed, but its value can be affected by simion.experimental.add_particles() (but such changes are not seen until the next time-step).

Compatibility: Added in 8.2.

A typical use is this:

simion.workbench_program()
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
sim_segment_global

This allows program segments to be called outside even when particles are outside PA instance volumes by setting this variable to 1. Normally this is 0, which uses traditional SIMION behavior of only calling segments for particles inside PA instance volumes.

A typical example is this is to ensure that the terminate segment is called for all particles, regardless whether they terminate inside a PA instance volume:

simion.workbench_program()
sim_segment_global = 1
function segment.terminate()
  print(ion_number, ion_px_mm)
end
function segment.terminate_run()
  print('all done')
end

Note: segment.terminate is called for each particle and will be called zero times if there are no particles. That differs from segment.terminate_run, which is always called exactly once and is not affected by this variable. terminate is appropriate when you need to access information (e.g. ion_px_mm) for each particle terminating, and terminate_run is appropriate when you want to do something exactly once at the end of a run.

Another use is to define a magnetic field without using a “dummy” magnetic PA instance:

simion.workbench_program()
sim_segment_global = 1
function segment.mfield_adjust()
  ion_bfieldx_gu = 100
end

Outside of PA instances, ion_instance is defined as 0, ion_mm_per_grid_unit is defined as 1, and ion_px_mm is identical to ion_px_gu and ion_pz_gu_abs (and so forth).

Permissions: Reading is allowed in all segments. Writing is allowed in the top-level, load, flym, and initialize_run segments (i.e. before particles are created).

This variable also can affect simion.wb:efield() and simion.wb:bfield().

Compatibility: Added in 8.2EA-20170214.

Note

This page is abridged from the full SIMION "Supplemental Documentation" (Help file). The following additional sections can be found in the full version of this page accessible via the "Help > Supplemental Documentation" menu in SIMION 8.1.1 or above:
  • Reserved Variables Full List

  • Particle Creation API

  • Load Functions

PRG (Old Interface)

New segments and variables mentioned above generally are available in (old SIMION 7.0 style) PRG code as well:

seg load
  mess ; loading
seg initialize_run
  rcl ion_run
  mess ; run=#
seg flym
  run
  run