next up previous contents
Next: C.4 The Minimos-NT Test Up: C. Miscellaneous Projects Previous: C.2 The Minimos-NT Interactive

Subsections



C.3 SEILIB - The Simulation Environment Interaction Library

SEILIB - the Simulation Environment Interaction Library -- is designed for handling parameterized input-decks and commands. In addition, several tools can be integrated to complete simulation flows based on these parameterized input files and optimization setups can be configured.

Values in text input-decks can be replaced by so-called arguments, which are generally encoded with preceding and succeeding brackets: {SEIARG}. The parameterized input-decks are regarded as templates or meta input-decks. They are used to create the actual input files for the simulators. The library provides several additional features, such as process administration, multi-threading, and host management.

C.3.1 Motivation

The library is dedicated to all users of an arbitrary number of arbitrary programs, scripts, or functions. Many similar simulations depending on an arbitrary number of input parameters shall be processed and (partly) repeated. It is therefore essential to keep the inputs and outputs consistent.

Furthermore, all available computational resources should be employed to reduce the execution time. The setup is extensible and several tools can be integrated to complete tool flows. In context of the GMRES(M) evaluation discussed in Section 5.5.3, the library successfully processed 23,000 processes on four IBM cluster nodes. It is important to note, that the use of arguments is not a must. So the host management can also be used for simulations based on conventional (non-parameterized) input-decks.

The library is written in PYTHON [167]. The implementation of the library itself can be found in the file seilib.py, which has to be accessible for the interpreter.

C.3.2 Getting Started

This section addresses the most important steps how to set up a new SEILIB application. Note that the optimization system is not covered here, see Appendix C.3.9 for this information.

A new PYTHON script normally starts with an execution directive for the operating system. As PYTHON is an interpreted language, the following line tells the loader which interpreter to use:

#! /usr/bin/env python

After importing the SEILIB library file, a new instance of the main class can be created, the name for it chosen here is p:

import seilib
p = seilib.seiclass()

At this point, the proper processing can be already tested. After setting execute permissions for the script (chmod u+x name_of_script.py), it can be directly started like other programs by typing on the command line: ./name_of_script.py. Alternatively, the name of the script can be passed to the interpreter: python name_of_script.py.

Note that the seilib.py file must be accessible for the interpreter. If the library is stored in another directory, the search path for libraries can be extended in order to avoid import errors:

import sys
sys.path.append(``name_of_directory'')

If the script is properly executed, only the welcome message of the SEILIB is printed. Of course, the newly created instance can now be set up for the actual SEILIB application. The setup can be divided in two steps:

  1. Define the tool or the tool flow.
  2. Define the arguments and prepare the templates.

C.3.3 Basic Nomenclature and Definitions

In order to understand the functionality of the script, the definitions of tool, command, host, argument, template, and auxiliary have to be given:

Tool: A tool represents either an arbitrary program or PYTHON function. The command line for a call and its template input-decks must be specified. In order to be executed, a specific command is assigned. Since one program might be represented by several tools, each tool should be given a unique name, which is called prefix. It can be used to derive unique file names for all auxiliaries and output files. However, the uniqueness is neither checked nor forced.
Command: A command defines the way how a tool is executed. Depending on which of five methods is chosen, the program is either executed on the local host or on a remote host. PYTHON functions are always executed on the local host. In addition, a command can be restricted to a distinct set of hosts. This might be necessary for taking the memory consumption of the simulator or license restrictions of commercial products into account.
Argument: An argument is the general expression for either a variable or constant. The name of the argument, preceded and succeeded by brackets {NAME}, can be arbitrarily used in templates and all kind of command line definitions. During processing, this name is replaced by the current value of the argument. Thus, an argument stands for a set of values, which are subsequently processed. These values are defined either in arrays or by PYTHON functions. In addition to user-defined arguments, the library provides so-called special-purpose arguments (see Table C.1).
Template: A template looks almost like the input-deck of the employed simulator. However, it contains some arguments instead of some values. For example, if the base voltage is normally defined by VB = 1.0 V;, it is defined as VB = <{VB}> V;. The templates are used to generate actual input-decks for the simulators according to the values of its arguments.
Auxiliary: An auxiliary is an actual input-deck which was created based on a template. Its number and name can be addressed by special-purpose arguments provided by the library.
Host: A host generally represents a computer. The user specifies, how many processes may be simultaneously running and which nice level is set. An additional parameter controls the number of subsequently started processes on that host. A host can have an arbitrary number of host-specific arguments.

The following examples give a first glance on how the script is configured:

p = seilib.seiclass()
p.hostman.newHost("tcad01", 0, 8, 1)
ecmd = seilib.seicmdexec("<{CMD}> > <{LOG}>")

p.newTool("mmnt",  # name of the tool
            1, 1,  # mode and number of sub-modes
            ecmd,  # command
         "short",  # prefix
      "<{AUX1}>",  # command line options
[ "seimos.ipd" ])  # array with template files

p.newTool(merge, 12, 1, 0, "test", "",  [ ])

The instance of the main class seiclass is p. A new host, tcad01, is registered at the host management system of p. The nice level for all processes on that host will be zero, eight processes may run simultaneously, and the once parameter is set to one (see below).

Next, a command is created, which is here an instance of the simple execution class seicmdexec. One purpose of this class is to compose the command line of the actual tool execution depending on the execution method. The user is absolutely free in defining these commands. In the example, the command line consists of {CMD} and the pipe of standard output to {LOG}. The special-purpose argument {CMD} contains the name of the tool and its command line options as specified in the parameter of the newTool call.

Finally, the tools are specified which is here a call of MINIMOS-NT (mode one, one sub-mode, prefix ``short'') with one input-deck only. In the command line, this input file is represented by the special-purpose argument {AUX1}. The template is stored in array, that could hold an arbitrary number of templates which are consecutively numbered. Note that all automatically generated numbers start with one, whereas PYTHON counting starts with zero.

More than one template is particularly useful if the include statement of the input-deck is used in order to construct a hierarchy of input-decks (as an alternative to conditional inheritance). For example, the physical definition is stored in a base input-deck, which is then included in various application input-decks like for simulating Gummel plots and extracting S-parameters. As all of these input-decks may contain SEILIB arguments, the library therefore supports an arbitrary number of templates. Note that template names may be arguments themselves.

The second tool is a PYTHON function (not shown here) and does therefore not require a command. It is supposed to combine all MINIMOS-NT output curve files to one. It belongs to mode twelve.

There are five methods for executing processes:

The library always waits for all spawned processes before it terminates.

C.3.4 The Argument System

What happens to the template and its arguments? This brings the argument system into play which is going to be discussed in this section. The template input-deck seimos.ipd contains the following four arguments:

aux infile   = "<{CWDIR}>/" + if(getenv("HOSTTYPE") == "i386", "mos", 
                                                               "mosibm");
aux outfile  = "<{DEVOUT}>";
aux crvfile  = "<{CRV}>";
aux numSteps =  <{SN}>;

As stated above, arguments are addressed by their names extended by preceding and succeeding brackets. The constant {CWDIR} is always provided by the library and stands for the current working directory. The names of the input device is added to this directory, but depends on the host type due to the binary ordering. The name of the device output and curve files ({DEVOUT} and {CRV}) depend on the actual state of the script processing, otherwise they would be overwritten all the time. The only input parameter is the number of steps {SN}, which is referenced by a stepping function. This example is for demonstration purposes only - the number of scattering events in a Monte Carlo simulator might be a better one.

Note that in the parameterized input-deck the double quotes must still be used for all represented strings such as the file names. But there are no double quotes for {SN}, which actually stands for an integer. Basically, the script processes strings only, and all non-string values specified as arguments are automatically converted to strings. However, this is no restriction for system handling text-based input-decks.

The last point is how the arguments and their values are registered. This should be demonstrated for the example:

p.newVariable("SN",     getSN)

p.newConstant("NAME",   "Test")

p.newConstant("AUXDIR", "<{CWDIR}>/aux/",                              "dir")
p.newConstant("LOGDIR", "<{CWDIR}>/log/",                              "dir")
p.newConstant("OUTDIR", "<{CWDIR}>/out/",                              "dir")

p.newConstant("ALLDAT", "<{NAME}>_<{FLATB}>_<{PREFIX}>")

p.newConstant("AUX",    "<{AUXDIR}>/<{ALLDAT}>_<{AUXNR}>_<{AUXNAME}>")

p.newConstant("LOG",    "<{LOGDIR}>/<{ALLDAT}>.log",                   "log")
p.newConstant("CRV",    "<{OUTDIR}>/<{NAME}>_<{FLATB}>.crv")
p.newConstant("DEVOUT", "<{OUTDIR}>/<{ALLDAT}>_out.pbf")

Although this does not look like the ultimate selling point at the first glance, there are quite many arguments for such a small parameterized simulation. Obviously it would have been possible to skip some of them. But this example is also intended to be a template for further applications where the underlying concepts might be useful.

Starting with the variable SN, its values are coming from a PYTHON function, namely getSN. The function itself is shown below. Then, nine constants are defined: a name, three directories, one constant being used as intermediary, the name convention for auxiliary files, and finally the three constants which were used in the template input-deck. The following statements characterize the argument system:

Note the different definition of {CRV}. In contrast to the other constants, it does not include the unique tool prefix. While setting up tool flows, the output of one tool is normally used as input for another tool. In the example, the MINIMOS-NT output curve files are the inputs for the merge function which is supposed to combine them. This can be easily achieved if the definition does not depend on the tool prefix.

The auxiliary files are created after all arguments have got their respective values. The stream editor is used to transform the templates to auxiliary files. The user can define an argument {AUX}, which contains the template for a name of an auxiliary file (see the example above). If such a variable is missing, a unique name is generated by combining {FLATB}, {PREFIX}, the number of the auxiliary and its name. The special-purpose arguments {FLAT} and {FLATB} contain the current meta-level and combination (for example M1_C1), and can be extended by a certain number of variable values (cf. maxVarDepth). A list of all special-purpose arguments is given in Table C.1.


Table C.1: List of all special-purpose arguments, which are automatically generated by the library.
Name Value
AUX Template for the name of an auxiliary file (not generated if existing)
AUXNAME Name of the currently processed template file (not generally available)
AUXNR Number of the currently processed template file (not generally available)
AUXx Name of the x-th auxiliary file
CMD The command line of the tool and its options
COMBNUM The current combination number
CWDIR The current working directory during initialization
FLAT Current meta-level and combination (extendible)
FLATB Like {FLAT}, but all points are replaced by underscores
HOST The name of the selected host (is used for the ssh command)
HUNAME The uname string of the selected host
HVARx The x-th host-dependent variable
METANUM The current meta-level number
MODE The current mode
NICE The nice level of the selected host
PREFIX The prefix of the current tool
PROGRAM The name of the current tool
SUBMODE The current sub-mode


C.3.5 Process Management System

The library is basically process-oriented and so the complete implementation is based on this idea. Therefore, it is useful if the user keeps the idea of creating processes in mind, which are finally executed on one host. The underlying simulations differ from each other by an arbitrary number of argument values. How many processes are potentially started?

This depends on the user configuration, which can be completed now:

def getSN(name, currProcess): return currProcess.procComb * 10

p.numCombinations = 7

The function getSN was already referenced by argument {SN} and should return the number of steps. This number should obviously depend on the processing state. In the example, the number of steps is the tenfold value of the current combination number. Note that the integer value is automatically converted to a string. Furthermore, it should be again emphasized that functions like getSN are native PYTHON code without any restrictions coming from the library. The library provides the following information in the function parameters: as first parameter the name of the argument the library is requesting a value, since one function could be used for several variables. The second parameter is the current process (the name of the parameter is up to the user: here currProcess was chosen) and an instance of the class seiprocess, which stores all information on this specific process.

But there is also a second way for defining values of variables: the array. The library automatically uses the combination number as index for the array. If the array is too small, the combination number modulo the array length is taken. The alternative configuration for {SN} could simply look as follows:

p.newVariable("SN", [ 10, 20, 30, 40, 50, 60, 70 ])

Four members of the process class are essentially defining the state of processing:

Combination: A combination is a set of values of all variables. Each variable gets one particular value in each combination. Thus, all combinations create one axis of a two-dimensional parameter space. The class member for the number of combinations is numCombinations, which is seven for the example above. Thus, there are seven states on this axis.
Meta-Level: This level creates the second axis of the two-dimensional parameter space. All combinations are repeated a specific number of times. The default value for the corresponding class member metaMultiplier is one, which is not modified in the example above. Note that further dimensions are of course possible, but it was decided to provide them on a multiplicative basis. The user is so responsible to derive the dimension by analyzing the meta-level.
Mode: As stated above, each tool is assigned to a mode. Thus, all tools create a set of active modes. Note that a tool can be disabled by specifying a negative mode.
Sub-Mode: In addition, each tool can have several sub-modes. This means, that each tool can be subsequently processed more than one time.

Since this looks a bit complicated, it is inevitable to note that the library can only be as powerful as its state system. Since the user already specified a set of tools which have to be additionally taken into account, the hierarchy is given by:

  1. Loop over all active modes.
  2. Loop over all meta-levels.
  3. Loop over all combinations.
  4. Loop over all tools of the current mode.
  5. Loop over all sub-modes.

Inside the fifth loop, an instance of the process class is allocated, a host is selected, all arguments get their values, the auxiliary files are created, and the tool is finally started.

The data stored in the process class is not always complete. There are three levels:

  1. Value requesting level: the state information and the real constants are available only.
  2. Pre-execution control: besides the state information, the values of all arguments are retrievable by using getValue.
  3. Post-execution control: all data is available

The library offers a flexible execution control. Before a tool is really executed, the function assigned to the doExecute class member is called. It takes the current process as the only parameter. If this function returns zero, the process is not executed. Note that the values of all arguments and the complete state information is available. The user can of course use the complete PYTHON syntax to write this function. The post-execution function afterExecute works in a similar way. It takes the complete process information (including the exit code) as only parameter. If it returns zero, the library processing will be terminated as soon as possible. Examples for these two functions are given now:

def doExecute(currProcess):
    if currProcess.procMode != 12: return 0
    return 1

def afterExecute(currProcess):
    if currProcess.procExit != 0 and not currProcess.procIsCF:
        print("log file: " + currProcess.procLog)
    return 1

The function doExecute allows only tools of mode twelve to be executed. The function afterExecute prints the log file of the process in the case of a non-zero exit code, but only if the process represents a program and not a callable function.

C.3.6 The Host Management System

The host management system offers the possibility to distribute the load over several CPUs and/or over a network (see Section 5.3.1). The user can specify an arbitrary number of hosts which are subsequently used to execute processes. The system retrieves information about the load average of each host, which can be used to select or reject hosts. By using the once parameter of the host, the user can influence the way how hosts are selected. This should be clarified by an example. It is supposed that more than eight processes should be executed, and four hosts (tcad01 - tcad04) are registered allowing two processes at the same time. Depending on the parameter once, the following host selection sequences are possible:

once = 1: tcad01 => tcad02 => tcad03 => tcad04 => 
          tcad01 => tcad02 => tcad03 => tcad04 => waiting...

once = 2: tcad01 => tcad01 => tcad02 => tcad02 =>
          tcad03 => tcad03 => tcad04 => tcad04 => waiting...

Hence the parameter once can be used to priorize some hosts, for example to keep the first processes local or to fully employ the local resources before remote ones are allocated.

Two classes are responsible for the host management: whereas the seihost class represents one particular host and stores all information about it, the hostManagement class administers the list of all hosts and organizes the communication to them.

As already mentioned above, some commands can be restricted to particular hosts. The following example should demonstrate this:

tcad01 = p.hostman.newHost("tcad01", 0, 1, 1, [ "powerpc" ])
tcad02 = p.hostman.newHost("tcad02", 0, 2, 2, [ "powerpc" ])
tcad03 = p.hostman.newHost("tcad03", 0, 2, 1, [ "powerpc" ])
tcad04 = p.hostman.newHost("tcad04", 0, 2, 2, [ "powerpc" ])

scmd1 = seilib.seicmdssh("source <{CWDIR}>/seisetup; 
                          nice -n <{NICE}> <{CMD}> > <{LOG}>", 
                        [ tcad01, tcad04 ])

Four hosts are registered, but the command scmd1 is restricted to tcad01 and tcad04. Note that in the restriction array the return values of newHost are used. If there is no host for such a restricted process available, it is simply postponed. Thus, while the process is waiting for an appropriate host, other unrestricted processes can still be executed on hosts tcad02 and tcad03. However, postponed processes have priority over new processes for the hosts they are waiting for.

Since a new shell is opened for all spawned processes, the user has to ensure the setting of the environment. Therefore, a setup script can be sourced as shown in the example above. In addition, the user is responsible for taking the nice level into account. The library offers a wide variety of possibilities in the parameter space, but the user is totally free in applying them.

C.3.7 Class Diagram

The diagram of the major SEILIB classes is depicted in Figure C.1. In the center, the main class seiclass is shown. This class combines own functionalities and those of other classes in order to provide the complete set of features. Therefore, it is the base class for all SEILIB applications.

Figure C.1: SEILIB class diagram.
\includegraphics[width=10.8cm]{figures/seihierarchy2.eps}

In addition, an auxiliary class provides basic functions such as the administration of command line arguments, exit codes, output functions etc. The name of this class is Auxiliary and a global instance seilib.auxiliary is publicly available.

C.3.8 Example Application: The Minimos-NT Test

The formerly single-threaded MINIMOS-NT test system was parallelized mainly for two reasons:

  1. New tests have been added to the test suite so that the execution time on a single CPU computer exceeds user-friendly limits. Such a situation could result in skipped tests and therefore less quality and effectiveness of the test.
  2. With the IBM cluster, new hardware resources are available which can be employed for spawning multiple processes.

So the platform of the complete test system was changed from Linux to AIX, which did not have any consequences for the PYTHON code since the interpreter is also available for AIX. Furthermore, the new platform has additional advantages regarding the numerical representation (see Appendix C.4.1).

Instead of parallelizing the existing test system, a new test system has been developed as a SEILIB application. Since both systems are based on the same ideas, the adaptation of the old tests was very simple. Due to extended features of SEILIB, new tests can be written in a much more flexible and easier way.

In Figure C.2, the class diagram of the new MINIMOS-NT test system is shown. The main class Example is derived from seiclass and is responsible for processing one test. The process and host management systems are directly available, the argument system is processing the settings found in the test script.

Figure C.2: Class diagram of the MINIMOS-NT test system based on the SEILIB library.
\includegraphics[width=8.0cm]{figures/seimmnttest.eps}

There is also one test which does not fit entirely in the concept. This test is intended to evaluate all MINIMOS-NT examples as found in a exa directory. So the specialized class ExaExample was written in order to process all input-decks found in this directory and to provide all features known from the other tests.

The new test system is not only able to parallelize the execution of one test, but also to parallelize the execution of all test scripts found in the test directory. This functionality is regarded as a test itself, and therefore a specialized class CompleteTest derived from Example was implemented. At this point of the test system a cascaded application of the SEILIB can be seen, which is an important aspect regarding the flexibility and stability of the library.


C.3.9 The Optimization System

Another proof of concept can be conducted based on the next considerations. If the set of the implemented SEILIB subsystems is analyzed, interesting similarities to the SIESTA framework [232] can be found. Basically, SEILIB is able to process template input-decks, employs a process and host management system, and allows to define tool flows consisting of an arbitrary number of arbitrary tools.

In contrast to the SIESTA system, SEILIB was designed to process a fixed number of combinations in a pre-defined and deterministic way. However, this should be no obstacle for providing an optimization feature by implementing a new optimizer class derived from seiclass. The optimization system is a highly-specialized SEILIB application which can be employed as a full alternative for SIESTA optimization models.

The description of the optimizer system starts with some details on the technical implementation: each supported optimizer provides an interface program to the actual optimizer system. The optimizers themselves and their interfaces are not strictly speaking part of the SIESTA framework. SIESTA starts the interface program as a separate process and communicates with this process by writing to standard input and reading from standard output. This part can be easily reproduced in PYTHON.

Each optimization model includes one or more so-called free parameters which are varied by the optimizer in order to find a minimum of a given scalar (cost or score function) or vector (cf. Levenberg-Marquart optimizer) target. The free parameters are defined by specifying a default, minimum, and maximum value. The variation of the free parameters depends on the results of the respective simulations. So during the optimization process, the interface class of the chosen optimizer requests one or more variable combinations per step. For each of these combinations, the defined tool flow must be processed after the values of the free parameters have been replaced in the template input-decks. The respective results are collected and written to standard output.

In the SEILIB context, the free parameters are arguments with boundaries. Furthermore, in the core results were not of interest at all up to now. For that reason, also slight extensions of the core modules were necessary and implemented. However, each request of the interface class represents a new setup of a pre-defined number of combinations. Thus, the concept fits perfectly in the already existing system and very few adaptations were necessary for providing the optimization setups with all features known from SEILIB and SIESTA. In fact, the largest part of the optimization system deals with preparing the settings for the optimizers.

SEILIB-based optimizations are based on one of four optimizer classes, which are derived from a base optimizer class (see the class diagram in Figure C.3).

Figure C.3: Class diagram of the SEILIB optimization system.
\includegraphics[width=8.0cm]{figures/seiopthierarchy.eps}

The following four systems are currently supported:

  1. Levenberg-Marquardt Algorithm (LMMIN) by Moré et al., 1980
  2. Donlp2 (DONOPT) by Peter Spellucci, 1995
  3. Adaptive Simulated Annealing (ASA, SIMAN) by Lester Ingber, 1993-2000 [103]
  4. A C++ Library of Genetic Algorithm Components (GALIB) by Matthew Wall, MIT [234], 1995-1999

Note that these optimizer systems are not part of the SEILIB code, but merely coupled to SEILIB. Thus, although their code is redistributed and provided, the license agreements of the respective code or package (see more information in [232]) have to be considered.

In the following, a short example should be discussed, which employs the DONOPT optimizer:

p = seiopt.seidonopt()

Besides of normal arguments, free parameters are defined, for example:

p.newVariable("a", p.getVariable, [ 1.0, -10.0, 10.0 ] )
p.newVariable("b", p.getVariable, [ 0.0, -10.0, 10.0 ] )
p.newVariable("c", p.getVariable, [ 0.0, -10.0, 10.0 ] )

Free parameters are variables and get their values from the optimizer. The name of the method is getVariable (getValue is already used in the argument system). The optional third argument was used for dir and log before, now it is used to specify the default, minimum, and maximum value of the variable in an array.

The most critical point of the optimizer model is the feedback path of the result. The tool flow is processed depending on the input values requested by the optimizer. In order for the results to be written back to the optimizer, the user is responsible for

  1. collecting the results from the correct output files,
  2. passing the either scalar or vector-valued result to the setResult method of the current process, and
  3. finally calling the optimizer method writeResult.

A frequently used place for doing this is the afterExecute method. If more than one tool is involved, the user must furthermore ensure that this is done for the last tool in the tool flow only.

For example:

currProcess.setResult(scalarResult)
p.writeResult(currProcess)

Now the definition of the optimization model is done - the only missing part is the configuration of the optimizer. Whereas DONOPT and LMMIN provide configuration methods, the settings of the GENOPT and SIMAN optimizers have to be done directly by accessing the public members of the classes.

The advantages of the SEILIB over the SIESTA system are:

The disadvantages are:


next up previous contents
Next: C.4 The Minimos-NT Test Up: C. Miscellaneous Projects Previous: C.2 The Minimos-NT Interactive

S. Wagner: Small-Signal Device and Circuit Simulation