# Manipulating SIMION Potential Arrays Programmatically¶

Contents

## Abstract¶

SIMION Potential arrays files (PA/PA#) can be defined using the SIMION
Modify GUI or, for more complicated tasks, GEM files or the CAD import
features of *SL Tools*. However, sometimes even these methods
are too limited, such as when you wish to create or modify a geometry
according to a mathemetical equation or arbitrary logic. In such
cases, it it useful to create the PA files directly using a general
purpose programming language. This article explains how to do so.
Specifically, we show how to generate a swirl-like shape using the
C++, Python, Perl, and Lua programming languages. To simplify this
task, the examples make use of the SL Libraries from
SIMION 8 (or the SL Toolkit) or the simion.pas Lua
library in SIMION 8.1 (or 8.0.4-earlyaccess).

## Problem¶

Our task is to generate a potential array of the following shape:

Although the SIMION GUI and GEM files provide means to generate
surfaces of revolution, this shape is not a simple surface of
revolution but rather can be described a rectangular prism revolved
around an axis while *simultaneously* moving the prism uniformly
along the axis of revolution. As such, this shape cannot be easily
generated using the various GEM file primitives (one could define each
3D pixel individually, but that is not easy to do by hand). You might
alternately describe this shape as a sinusoidal projected onto the
lateral side of the cylinder. However, GEM files don’t provide
intrinsic support for sinusoidal shapes either (rather, ellipses,
lines, hyperbolas, etc.). You might be able to generate the above
geometry in a CAD program, but we will not examime that possibility in
this article.

After exhausting the above possibilities, the solution is to generate the PA file directly during using a general programming language to provide full flexibility. The easy way to do this is to use the C++, Python, or Perl classes in the SL Libraries, which come bundled with SIMION 8 (previously with the SL Toolkit). The SL Libraries already implement for you the functions necessary to read and write PA files and easilly perform a variety of other manipulations on PA files as well. If you wish to use another programming language not supported, and you are familiar with binary file I/O, you can alternately write the PA files yourself and skip the SL Libraries. PA files are in a binary file format described in Appendix F.5 of the SIMION 8.0 manual (or page D-5 of the SIMION 7.0 manual). This article takes the former approach, but you can still get some useful ideas out of this article if taking the latter approach.

The following sections describe generating a PA file using the C++ language, then doing the same thing using the Python, and finally doing the same using the Perl language. Although the code is not described in this article, Eric Saltzman has contributed a code sample (http:resources/sllibrariestut/spiral-vb5.zip) of creating a somewhat similiarly shaped potential array in the Visual Basic 5.0 language (manually, not using the SL Libraries).

**Compiler/interpreter availability:** To write potential arrays
programatically, you will first need to acquire a compiler for the
particular language you want to program in. Python interpreters and
Perl interpreters are freely available, while both free and commercial C++ compilers are
available (including Microsoft Visual C++ Express Edition and g++
(also included in Cygwin).
(The SL Toolkit, though not SIMION 8.0, bundled a copy of Perl.)

There is also an experimental Lua version of doing this. The Lua
version has some advantages because it runs inside the SIMION process
and requires no additional compiler. The Lua interface to potential
arrays is a new feature scheduled for SIMION 8.1, but it can also be
used now in SIMION 8.0.4 when run in “early access mode”. For details
on the Lua version see *simion.pas*.

## The C++ solution¶

The below code is the solution is C++.

```
/**
* swirl_test.cpp
* This generates a potential array file describing a swirl-like
* shape (e.g. a ribbon wrapped around a cylinder).
*
* David Manura, Scientific Instrument Services, Inc. 2003-11
*/
#include <iostream>
#include <cmath>
#include <simion/pa.h> // SL potential array class
//#include <simion/pa.cpp>
using namespace std;
using namespace simion;
const double pi = 3.141592;
int main()
{
cout << "Generating swirl.pa# file....\n";
// create array in memory
PA pa(PAArgs().nx(100).ny(100).nz(100));
// iterate over all points
for(int x = 0; x < pa.nx(); x++) {
for(int y = 0; y < pa.ny(); y++) {
for(int z = 0; z < pa.nz(); z++) {
//-- compute point info
// compute polar coordinates
int dx = x - 50;
int dy = y - 50;
double radius = sqrt((double)(dx * dx + dy * dy));
double theta = atan2((double)dy, (double)dx); // -PI..PI
double omega = pi + theta + (double)z/5;
// wrap omega to range 0..2*PI
while(omega >= 2*pi)
omega -= 2*pi;
bool is_electrode = (radius > 30 && radius < 35 && omega < 2);
double voltage = 1;
// set point value
if(is_electrode) {
pa.point(x, y, z, true, voltage);
}
}}} // end loops
// save to file
pa.save("swirl.pa#");
cout << "done\n";
return 0;
}
```

A brief description of the code follows.

First we deal with the specifics of C++. We include standard C++ libraries (“iostream” for printing to the display console and “math” for trigonometric and other math operations). We then include the “pa.h” header file. This provides PA class from the SL Libraries. When you compile, you will also need to link in the pa.cpp file’s object code, which implements this class described in pa.h, but if you have difficulty doing this, you can simply include it into your main program by uncommenting the #include “pa.cpp” line. As in most modern C++ programs, we use the standard (std) namespace, but we also use the simion namespace. (No need to ask why–it just works–but be careful if you have a very old C++ compiler, which will likely choke on this namespace construct. Modern compilers such as Microsoft Visual C++ 6.0 (and above) and g++ work fine with the PA class, and recent version of Borland C++ should work too.) We also define the constant PI.

Inside of the main loop, we display a message to the screen (“Generating swirl.pa# file...”). Then we construct a new potential array object in memory (pa) and initialize its array to a size of 100 x 100 x 100 grid units. By default, the array is assumed to be a electrostatic (not magnetic) array of planar symmetry and no mirroring. If this were not the case, you would have to specify these parameters as well, such as

```
pa.field(MAGNETIC);
pa.symmetry(CYLINDRICAL);
pa.mirror_y(true);
```

to change pa to be a magnetic array with MIRROR-Y cylindrical symmetry. These can also be placed in the constructor (where y mirroring is implicit from cylindrical symmetry):

```
PA pa(PAArgs().nx(100).ny(100).nz(100).
field_type(MAGNETIC).symmetry(CYLINDRICAL));
```

Next, we traverse over all points in the array, and at each point,
we appropriately set its potential and electrode type (electrode
v.s. non-electrode). The series of for loops traverses each point in
the volume such that inside the loop, (x, y, z) represents the grid
coordinates of the current point. So, the question now is, given
point (x, y, z), how do we mathematically describe whether that point
is an electrode or not an electrode? In words, we want to include in
our electrode all points that are within a certain radius of a line
through the center of the potential array. This would generate a
cylinder. However, we further want to restrict the inclusion volume
to only *some* of the points on that cylinder (i.e. those points
touching a ribbon wrapped around the cylinder).

In order to achieve the first part, of finding points on the cylinder, we first transform (x, y, z) into a cylindrical coordinates (radius, theta, z), where the origin is taken at the potential array center x=50, y=50. That’s what the dx, dy, sqrt, and atan2 statements above do. (As a side note, atan2 is a special version of the arctangent. It takes two parameters, x and y, and returns the angle that (x,y) makes with respect to the origin. If we instead used the usual “arctan” function, i.e. +-atan(y/x), we would get two angles, and if would not be immediately clear which one to use. Many programming languages besides C++ also provide this atan2 function.)

Now, to only keep the points touching the ribbon on the cylinder, we use the statments involving omega above. omega is a value that increases both as theta increases and as z increases. So, the set of points corresponding to a certain value of omega, will resemble a string (not a ribbon) wrapped around a cylinder. To get a ribbon, we want limit omega to not just a single value but to certain ranges. To do so, we “wrap” omega (almost like performing a modulus but using real numbers) onto the interval (0, 2*PI] and exclude points above a certain value of omega. We now have determined whether the point (x, y, z) is an electrode.

If we determine that (x, y, z) is on an electrode, we mark it as an electrode (true) with a certain voltage (we use 1V here since this will be a fast adjustable array). This is done using the “point” function, which simultaneously sets the electrode status and potential at the given point (x, y, z). For additional methods you can call on the PA object, see the <SL Libraries for C++ API>`http:cpp/index.html <http:cpp/index.html>`_.

Finally, after setting all points, we save the PA. You can now import the PA file into SIMION. The generated <swirl.pa#>`http:resources/sllibrariestut/swirl.pa%23.zip <http:resources/sllibrariestut/swirl.pa%23.zip>`_ file (zipped) is provided here for reference.

For additional details see the
*simion::PA - PA Library in C++* reference.

## The Python Solution¶

The Python solution is in many ways similar (in concept) to the C++ solution. The code is given below.

```
# swirl.py
# Generates a swirl-like SIMION potential array
# (like a ribbon wrapped around a cylinder)
# David Manura, Scientific Instrument Services, Inc. 2003-11.
from SIMION.PA import *
from math import *
print "Generating swirl.pa# file....\n";
# create new potential array
pa = PA(nx=100, ny=100, nz=100)
# iterate over all points
for x in range(0, pa.nx()):
for y in range(0, pa.ny()):
for z in range(0, pa.nz()):
# compute polar coordinates
dx = x - 50
dy = y - 50
radius = sqrt(dx * dx + dy * dy)
if dx == 0 and dy == 0: # atan2 would fail on this
theta = 0
else:
theta = atan2(dy, dx); # -PI..PI
# this is what generates the rotation along the axis.
omega = pi + theta + z/5.0
# wrap around omega to range 0..2*PI
while omega >= 2*pi:
omega -= 2*pi
# compute point value
is_electrode = (radius > 30 and radius < 35 and omega < 2)
voltage = 1
# set point value
if is_electrode:
pa.point(x, y, z, 1, voltage)
#-- end loop
# write file
pa.save("swirl.pa#")
print "done"
```

For additional details, see the
*SIMION.PA - PA Library in Python* reference.

## The Perl Solution¶

The Perl solution is also in many ways similar (in concept) to the C++ solution as well. The code is given below.

```
# swirl.pl
# Generates a swirl-like SIMION potential array
# (like a ribbon wrapped around a cylinder)
# David Manura, Scientific Instrument Services, Inc. 2003-11.
use strict;
use POSIX;
use SIMION::PA; # potential array library
my $pi = 3.141592;
print "Generating swirl.pa# file....\n";
# create new potential array
my $pa = new SIMION::PA(nx => 100, ny => 100, nz => 100);
# iterate over all points
for my $x (0..$pa->nx()-1) {
for my $y (0..$pa->ny()-1) {
for my $z (0..$pa->nz()-1) {
# compute polar coordinates
my $dx = $x - 50;
my $dy = $y - 50;
my $radius = sqrt($dx * $dx + $dy * $dy);
my $theta = atan2($dy, $dx); # -PI..PI
# this is what generates the rotation along the axis.
my $omega = $pi + $theta + $z/5;
# wrap around omega to range 0..2*PI
$omega -= 2*$pi while $omega >= 2*$pi;
# compute point value
my $is_electrode = ($radius > 30 && $radius < 35 && $omega < 2);
my $voltage = 1;
# set point value
if($is_electrode) {
$pa->point($x, $y, $z, 1, $voltage);
}
}}} # end loop
# write file
$pa->save("swirl.pa#");
```

For additional details, see the *SIMION::PA - PA Library in Perl*
reference.

## The Lua Solution¶

The following solution in Lua will require SIMION 8.1. Create the below swirl.lua file, click the “Run Lua Program” button on the SIMION main screen and select the swirl.lua file. The potential array will be created in memory, and you can view it in Modify or View.

```
-- swirl.lua
-- Generates a swirl-like SIMION potential array
-- (like a ribbon wrapped around a cylinder)
-- David Manura, Scientific Instrument Services, Inc. 2007-10.
print "Generating swirl.pa-- file....\n";
-- create new potential array
local pa = simion.pas:open()
pa.symmetry = 'planar'
pa:size(100,100,100)
-- iterate over all points
for x = 0, pa.nx-1 do
for y = 0, pa.ny-1 do
for z = 0, pa.nz-1 do
-- compute polar coordinates
local dx = x - 50
local dy = y - 50
local radius = sqrt(dx * dx + dy * dy)
local theta
if dx == 0 and dy == 0 then -- atan2 would fail on this
theta = 0
else
theta = atan2(dy, dx); -- -PI..PI
end
-- this is what generates the rotation along the axis.
omega = math.pi + theta + z/5.0
-- wrap around omega to range 0..2*PI
while omega >= 2*math.pi do
omega = omega - 2*math.pi
end
-- compute point value
local is_electrode = (radius > 30 and radius < 35 and omega < 2)
local voltage = 1
-- set point value
if is_electrode then
pa:point(x, y, z, voltage,true)
end
end
end
end
-- write file
pa:save("swirl.pa#")
print "done"
```

For additional details, see the simion.pas reference.

## Conclusion¶

Various ways to create SIMION potential arrays using the SL Libraries have been presented. Which language you use largely depends on which language you are most familiar with. At the cost of some CPU and memory efficiency, Lua and Python generally have the simplest and most beginner-friendly syntax, and the Lua version (in SIMION 8.1/8.0.4-earlyaccess) is the simplest because there is no compiler setup and it runs inside of SIMION, but all presented languages work adequately for this purpose, and there are many reasons you may have to use any one of the above languages.

–D.Manura, Scientific Instrument Services, Inc. November 2003-2010.