Alfi Documentation


Contents

  1. Introduction

  2. Primitive and Boxes (Nodes)
    1. Primitives
    2. Boxes
    3. Short Definition of Node Terms

  3. Containers and Inheritance in Alfi
    1. Inheritance of Base Elements
    2. Inherited Elements vs. Local Additions
    3. Indicators and Functionality of Local vs Inherited Elements
    4. Contained Elements vs Inherited Elements

  4. Alfi Mechanics
    1. Starting Alfi
    2. Creating New Boxes and Loading Boxes
    3. Viewing Box Nodes
    4. Editting Box Nodes
      1. Primitive Members
      2. Box Members
      3. Ports
      4. Connections
      5. Junctions
      6. Bundles and Bundlers
      7. Macros
      8. Comments
      9. The Default Edit Menu
    5. Misc
      1. Opaque Box Nodes


Introduction

The End to End model simulation program must read a file which describes the configuration of the system to simulate. This description file is called a box file. A box file may be as simple as the description of one digital filter representing an electronic module, or may describe a system as complex as the full LIGO system. Originally, box files were managed manually by using a simple text editor, but soon it became prohibitively complicated to develop and maintain complex systems without a program which provides the user a graphical interface and other related functionality.

Alfi then, was designed to offer such an interface for the construction and maintenance of box files.

For now, the main focus of Alfi is the preparation of box files for the Adlib engine. But in the future, it will offer other tools for the End to End model, such as including the display of the output data and the interactive manipulation of settings.


Primitives and Boxes (Nodes)

Primitives

Primitives are the "atoms" from which an E2E model is made. Alfi does not concern itself with the internal definition of a primitive, but only allows for a data I/O interface (via I/O ports)and access to the setting of parameters (via primitive settings) for the particular instance of the primitive at hand. The definition of primitives is outside the context of Alfi, and the files which define them must be found at startup by Alfi. Alfi searches an E2E_PATH at startup in order to find these files.

The set of root base primitives found at startup are the templates used to create instance primitives which are placed in the boxes you create and edit in Alfi. Every primitive you see in Alfi is an instance primitive (i.e., an instance of a root base primitive.) Again, the base primitives are edited outside the context of Alfi.

Boxes

Boxes are containers which are used to set up more complex systems by the interconnection of primitives and/or other boxes which have been previously created. Every box has an external I/O interface (right) just as primitives do, but the internal components of boxes (left), which ultimately define the meaning of a box's external I/O, may be edited.

A root base box is a template in the same way a that a root base primitive is. Whenever you load a box into Alfi from disk (or when you create a new box), you are always loading a root base box (which will itself likely contain many instances of other boxes and primitives.) Whenever you add a member box into a container box's edit window, you are adding an instance box, which is a copy of some root base box you have chosen. This instance box inherits all the elements contained in this root base box. You may then decide to change some things in the new instance box, but this does not affect the contents of the base box. On the other hand, subsequent changes in a particular base box DOES affect all instances of boxes derived from that base.

Both boxes and primitives are collectively refered to in Alfi as nodes.

Short Definition of Node Terms


Containers and Inheritance in Alfi

Inheritance of Base Elements

All instance nodes are derived from some base node. The figures to the right are the internal views of three boxes. The first two boxes are root base boxes. One easy way to tell this is that their names (Detector and PSL respectively) have no "." in them. The "." is a container hierarchy delimiter. The name of the third box, Detector.PSL, indicates that there is a member box PSL contained in box Detector, and its full name is Detector.PSL. And in fact, if we look at the contents of Detector, we see box PSL inside it. This is the external view of member element PSL in Detector. At some point, the builder of the Detector box decided to add an instance of a PSL box into Detector. Beware that the names of member nodes (such as PSL) may be set to whatever the Detector box builder decides. But it is good form to use names which can be associated easily with the root node it is based upon.

Now let's compare root base box PSL with Detector.PSL, which is contained in Detector, and is an instance of PSL. When it was newly added to Detector, Detector.PSL's internal elements were as shown in the "Unmodified" figure. Compare this to the contents of PSL itself. The contents are exactly the same, thought there are some alterations in the look of many of the elements. These details will be described later. For now, the main point is that when the user adds an instance of PSL to Detector (Detector.PSL in this case), the new instance is a copy of PSL.

But after the instance is added, the user may decide to modify one or more parts of the instance. For example, we see in the "After Local Modifications" figure that a second primitive data_viewer (data_viewer_0) node has been added, as well an accompanying connection. One other change has occured as well. The gray shade of Laser indicates that something internal to that node has been modified from its base configuration (in this case, an internal setting was modified.) Lastly, because of these "local" changes to Detector.PSL, the PSL node in the Detector window will appear as shown to the left. Such changes are documented in the Indicators and Functionality of Local vs Inherited Elements section below.

Inherited Elements vs. Local Additions

There can be two types of elements which can be seen in any box window. There are elements which a user has explicitly added, most often by using the mouse or keyboard to place a node, port, connection, etc into a particular place in the window. These are known as local elements. The other type of element is an inherited element. These elements are copies of the elements contained in the base of the box at hand.

For example, in box Detector.PSL at bottom right, the data_viewer node  is an inherited node, because it exists in Detector.PSL's base box PSL. But the data_viewer_b node  is a locally added node. Note that this node does not appear in the base node PSL.

It is often important to be able to spot local changes because these elements signify how a system has been changed from its default operation. Also, only local elements may be repositioned. Inherited elements always keep the same positions as they have inside the base box they originated from. In other words, in the Detector.PSL "After Local Modifications" figure to the right, only the local data_viewer_b node  and the connection to it may be moved.  All the other elements' positions are static (unless their position were changed from within the base PSL node itself.)

Root Base Box Detector


Root Base Box PSL


Detector.PSL (Unmodified)

Detector.PSL (After Local Modifications)

Note that all elements in the top level of a box are always local elements (see the windows for the Detector and PSL boxes.) This is because all boxes start empty when created, and anything appearing in the top level must then have been added locally. But when boxes are placed inside boxes, lower level nodes are created which will initially be occupied only by inherited elements (as in the unmodified Detector.PSL node.)


Indicators and Functionality of Local vs Inherited Elements


Contained Elements vs Inherited Elements

 These are two fairly simple concepts, but they can be easily confused because they correspond to two different but interconnected hierarchies in Alfi. When a node (box or primitive) is added to the internals of another box, it is considered to be a member node of that box, and the box including it is called the container node. E.g., box Detector is the container node for box member nodes Detector.Env, Detector.EnvMon, Detector.PSL, Detector.IOO, Detector.COC, and Detector.DigitalISC, as well as primitive member nodes Detector.data_in and Detector.data_out (at upper right.) Member nodes are often just refered to as "members".

At the same time, node Detector.Env is a derived node of Env, and Detector.data_in is a derived node of data_in. Conversely, Env is the base node of  Detector.Env, and data_in is the base node of Detector.data_in.

Avoid the use of the generic terms parent node and child node, as it is easy to confuse which hierarchy is being refered to in that relationship.


The full name of any node is always a reflection of the container hierarchy of the node. If we open up the Detector.PSL box, we see nodes such as Detector.PSL.Laser for example. Note that node Detector.PSL is both a member node (in Detector), and a container node as well. Most box nodes are both member and container. Only root base boxes have no container node. They are always top level nodes like Detector and PSL (note their names never have a "." in them.)

The container hierarchy of the entire set of nodes which has been loaded into Alfi is shown in Alfi's main window. The "Box Directories" folder contains a list of all the directories from which boxes were loaded into Alfi (in this case just the "." or current directory.) The figure to the left shows the root base box (in blue) Detector was loaded into the current session of Alfi from the directory ./SimLIGO/Boxes/. Under each root base box folder is the contents of all nodes contained down to any depth of containment. Note that the color schemes for all contained nodes is the same as in the windows.

Primitive nodes are denoted by bullets, and box nodes by folders, as they can contain other nodes.

  The inheritance hierarchy is a little more subtle. The names of nodes in these examples are indicative of their base nodes, but they can be changed by the user who builds the boxes, so later users inspecting a system may have to investigate a little to understand what the base nodes are in some cases. Another subtlety is that, in general, any particular node's direct base node is not the root base node.

For example, the direct base node of Detector.PSL.Laser is PSL.Laser. The inheritance hierarchy in this case is Detector.PSL.Laser is derived from PSL.Laser which is in turn derived from the root base primitive field_gen. (Note that PSL.Laser could have been named  PSL.field_gen, but the builder of the box decided that "Laser" was a more appropriate name for this element.) Only locally added nodes are directly derived from root base nodes. Inherited nodes always have an inheritance hierarchy back to such a root base node, and any changes in the intermediate base nodes are propogated forward to all nodes derived from them.  Therefore, if a change is made in field_gen, the change appears in PSL.Laser, and in Detector.PSL.Laser. But if a change is made in PSL.Laser, it also appears in Detector.PSL.Laser (though not backwards to field_gen.)


Alfi  Mechanics

Starting Alfi

If you have not yet installed the required software to run Alfi, here are the installation notes. If you are a Unix user, you may also be interested in the alfi_ws script which allows for command line arguments among other things.

The above resources explain how to start an Alfi session.

Startup Issues

On startup, you may be queried for some information Alfi needs at initialization time.  This information will usually be saved in a preference file after the first time you use Alfi.



Creating New Boxes and Loading Boxes

To start work on a new box, select New Box... from the file menu and type in a name for the box. For Alfi to recognize the file, it needs to have a ".box" extension.

Or, if you have an already existing box you'd like to edit further, select Load... from the File menu and select the root base box you wish to work on. The root base boxes of any member boxes inside the selected box will be automatically loaded as well. They will all appear in the container tree in the main window.
 
Occasionally you may wish to reload a box from file which is currently already loaded into Alfi (in order to return to the last saved state of the box.) To do this, select the root base box in the main window tree, and then select Reload Selected Node from the File menu. Beware that current changes to the box will be lost.

You may unload already loaded boxes in a similar manner using the Unload Selected Node menu item.

Only root base boxes maybe reloaded/unloaded in such a manner. I.e., the member node Detector.Env cannot be reloaded by itself. The whole of the root box Detector must be reloaded to accomplish this.


Viewing Box Nodes


When a new box is created, or when a box is directly loaded by the user, an edit window for the internal view of this box is automatically displayed. The internal view of the box which shows all the contents of the box.

To open any box node in the container hierarchy (which shows all root base boxes and all box nodes contained therein), left-double-click on the icon representing the box you wish to view. In the case shown, the edit window to the left would appear if the icon next to Detector.PSL in the container hierarchy shown were double-clicked on.

 If an edit window is already open for a particular box node, then double-clicking on it in the container hierarchy will bring that window to the top. A list of open edit windows can also be found in the Window menu of the main Alfi window. Selecting windows there also will bring the selected window to the fore.


Editing Box Nodes

Primitive Members

Adding Local Primitive Nodes
There are two methods which can be used to add an instance of a primitive into a box. You may left-click in any blank (white) space in a box edit window to pop up a Primitives window to select from (see left.) Or you may use the Primitives toolbar (see right.) To use the toolbar, left-click in the toolbar on the image of the primitive you wish to add. Then click on any blank spot(s) in an edit window and an instance of the selected primitive will be added. To turn this behavior off, click on the primitive you just added to the edit window. The Primitives tool bar will deselect the current selection. The Primitives toolbar can be shown or hidden at the user's option by selecting Primitive Toolbar in the View menu of the main Alfi window.

Removing Primitive Nodes

To remove a primitive node from a box, right-click on the primitive node and select  from the menu. You may remove both local and inherited primitives in this manner.

Repositioning  Primitive Nodes
Inherited primitives may not be repositioned. Local primitives may be moved within the box window by left-button-dragging the node or by selecting the node (left click) and using the arrow keys.

Miscellaneous Primitive Node Operations and Information

Box Members

Adding Local Box Nodes
To add a local member box node (always an instance of some root base box) into the box you are working on, use control-left-click to get a menu of box files from your working directory. The list will also contain the directories in your E2E_PATH, and you may select boxes from these locations as well.

Removing Box Nodes
The procedure for removing a box node is the same as that for a primitive node. Right click on the member you wish to delete and select from the menu. Both local and inherited box nodes may be deleted from a container node.

Repositioning  Box Nodes
Inherited box nodes may not be repositioned. Local box nodes may be moved by left-button-dragging the box node or by selecting the node (left click) and using the arrow keys to increment the node position inside the container node's window.

Miscellaneous Box Node Operations and Information


Ports

Adding Ports

To add an external port to a container node, merely left click anywhere in the black port border.  A default port will appear at that location and a modification dialog will appear to implement any changes from this default state. (External ports are those ports in the black border on the edges of a container node. Do not confuse external ports with a node's external view. A node's external ports appear on the edges of the node in both the internal and external views of the node.)

The Modify Port Dialog will appear where you will be asked to provide a port name, an I/O Type, and the initial edge(s) the port will appear on in the internal and external views of the node. The internal view edge is the edge you will see the port added to immediately in the container you are working in. The external view edge concerns itself with the look of the I/O interface for the node you are working in when it itself is added to another node.
 
For example, I am working on node PSL,



and there is an instance of PSL in Detector (Detector.PSL). I decide to add
an output Integer data port to the root PSL box, and specify east for the internal view and west for the external view positions. The change in the internal view of PSL would appear as:



And the external view of Detector.PSL in Detector would appear as:


Removing Ports
Only local external ports may be removed from a node. To remove a local external port, right-click on the port in question and select from the port popup menu. If this option is disabled, it is because you are attempting to delete an inherited port.

Repositioning Ports
Local external ports may also be repositioned in both the internal and external view of a box node. Inherited ports will never move. (See the note below on selecting a port in the internal node view vs the external node view.). Once a local port is selected, there are 4 modes of port movement:
  1. Left-click-dragging on a local external port will drag the port along the same edge, exchanging positions with other local ports it is dragged over. Such movement will not displace inherited ports though. (This mode does not require pre-Selecting the port. Just click, hold, and drag.)
  2. Using the arrow keys on your keyboard, you can move the selected port up/down or left/right (depending on the side the port sits on, of course.) The moving port will not displace or exchange positions with other ports as in method 1, but will simply jump to the next available open position on the side.
  3. While pressing the <SHIFT> key, use the arrow keys on your keyboard to move the selected port along the same edge, pushing or pulling the following ports on the edge with it.
  4. To move the port to a different edge, press the <CTRL> key, and press the arrow key which points to the side you wish to move the port to. The port will move to the first available spot on the desired edge. You may then move the port along the new edge by using one of the above methods.
The same mechanics are used to move ports in both the internal and external views of a node except in how to select a port for movement. In the external view of a node, either a left or middle mouse click on the port will select the port for movement via the arrow keys. In the internal view, a left click on a port is used to start a new connection to that port, so a middle click is required to select a port for movement in the internal view. After selection, though, all the mechanics of  moving the port are identical in both internal and external views.

Miscellaneous Port Operations and Information

Connections

Adding Connections
One way to add a connection to a container is to left-click on a port (an external port or a port on a member node, either one.) This will start the connection building process. As the mouse is moved, a partial connection will extend to follow the cursor. To build connections with more than a single corner, left-click at points you wish the connection to be anchored to. Before clicking, use the spacebar to toggle between the two possible routes the connection can take to the point.

To complete the connection, finally, left-click on the other terminal port. The terminal ports must be of the same data type (or of Unknown type), and one port must be a sink port, and the other a source port. Source ports may be used to start an unlimited number of connections, but sink ports will accept only a single connection. The new connection, when completed, will change to the data type color of the source port. See Miscellaneous Port Operations and Information for information about data type colors
Connections may also be started/terminated on junctions and bundlers .

Removing Connections
Any connection in a container node may be removed, local or inherited. To remove a connection, right-click on the connection and select from the connection popup menu.

Repositioning Connections
Only local connections may be repositioned. Connections are altered one segment at a time. Left-click-drag on any local connection segment to reposition that segment. Adjacent segments will shrink/grow as needed to accomodate the new position of the segment. All segment movement is automatically perpendicular to the data flow direction of the segment. All connection segments are always kept orthogonal to each other.

Miscellaneous Connection Operations and Information




Before Trim


Trimming Connection(s)


After Trim
If you wish to trim all the connections in the window, right-click in an empty spot in the edit window and select Trim Connections instead. Finally, if you wish connections to be automatically trimmed whenever you add or modify them, turn on the Auto Trim Connections option in the main window's Options menu.


   Junctions

Adding Junctions

Junctions ( ) are used to avoid the clutter of having many connections from the same source overlapping each other. All connections coming out of a connection can be traced back to having the same origin port (even if that is through other junctions along the way.)

As you can see, the use of junctions can help clarify the paths of connections, and also make clear the difference between connection paths which merely cross each other as opposed to connections which have the same origin port.

To add a junction to any local connection, right-click on the connection at the location you wish to place the junction and select Create Junction from the connection popup menu.

You may also merely left-click anywhere on a local connection to add a junction at that point and also automatically start a new connection from that junction at the same time.

Junctions are also automatically added at appropriate locations when Trim Connections is used.

Removing Junctions
Inherited junctions cannot be removed. To delete a local junction, right-click on the junction and select from the very short junction popup menu. When you remove a junction, any connections using it as a source will automatically be extended back to the previous source before the junction. If no such source exists, then any such connections are removed along with the junction.

Repositioning Junctions
To move a junction, merely left-click-drag it to another location. Connections attached to the junction will be changed to follow the junction to its new location.

Miscellaneous Junction Operations and Information

Bundles and Bundlers

All information in this section refers only to Alfi Bundle data type and not to the deprecated Bundle data type.

Bundle connections (aka "bundles")are used to decrease the clutter of having many different connections running in parallel. Instead of several different connections entering, exiting, and/or crossing through a node, each of these connections may be fed into a bundle and extracted downstream at the point in which they are needed.

Connections are placed into and extracted from bundles via bundlers (  ). Bundlers should not be confused with junctions (  ). Junctions merely branch an input data stream into identical output data streams, whereas bundlers either join a connection into a collection of connections (a bundle) or extract a connection from same.

A bundler may have a maximum of 3 connections. There may be a primary bundle input, a primary bundle output (essentially this primary bundle flows through the bundler in this manner (  ).) The last connection a bundler may have is a secondary input or a secondary output.
Creating Bundlers

One method to create a new bundler is to via the background popup menu.

Another method, if a bundle already exists in the window, is to middle-click on the bundle at the location you wish to place a bundler. A bundler will be placed at that point of the bundle connection, and can then be used to either add a connection to the bundle, or to extract a connection from the bundle.

Finally, the same effect can be acheived by selecting Create Bundler from the popup menu which appears by right-clicking on a bundle.

Removing Bundlers

Inherited bundlers may not be removed. A local bundler may be removed by right-clicking on the bundler and selecting from the bundler popup menu.


Bundlers: Primary Bundles, Secondary Input/Output Channels

Each bundler sits on a Primary Bundle. The  purpose of every bundler is to either add a data channel (or channels) or extract a data channel (or channels) from this primary bundle. Think of the primary bundle as a data freeway, with bundlers being the on and off-ramps to and from the freeway. The path of the primary bundle through a bundler is given by the line passing through the middle of the bundler, e.g.,  or . Note in the bottom bundler in the first case that the primary bundle does not pass straight through the bundler. And in the second case, the primary bundle does not pass through the bundler at all. The primary bundle terminates at this bundle. The connection which is not linked by the line inside the bundler is the secondary input or output.

Adding Data Channels to a Bundle (Secondary Bundler Input)

To attach a secondary input to a bundler (thus adding this data channel into the bundle), click on the data source object (e.g., a port) and then terminate this connection on the bundler. You will be queried for an input name for this addition to the bundle. This is the name which will be used in order to extract the data stream from the bundle at some point further downstream.

If the secondary input is itself a bundle, it may be inserted into the primary bundle as either an individual entity (wrapped), or it may expose (unwrap) its top level contents for them to each be added into the primary bundle seperately.  For example, a secondary input bundle which you name "environment" may have the data streams "temperature_1", "temperature_2", and "seismic_activity" in it. When adding the environment bundle to the primary bundle, if you add it without unwrapping, the data streams will be referred to (in the primary bundle) as "environment.temperature_1",  "environment.temperature_2", etc. If, instead, you click the unwrap checkbox in the Input Name Dialog, then the resulting streams in the primary bundle will be referred to merely as "temperature_1", "temperature_2", and "seismic_activity." It is up to the developer of the box to be sure to avoid naming conflicts.

Extracting Data Channels from a Bundle (Secondary Bundler Output)

To attach a secondary output connection to a bundler, click on the bundler, and then click on the data sink object (e.g., a port.) The Output Channel Selection Dialog will appear.

You may select the channel(s) you wish to extract from the bundler's primary bundle by clicking on the channel in the list of Currently Available Output Channels. If the secondary output channel you are creating is itself a bundle, you may select multiple channels to extract by Control-clicking.

If you wish to add a channel name to extract by hand, you may type in a channel name which you expect to later be available in the Manual Output Channel field and then click on the Add Channel Name button.

Finally, after the channel or channels you wish to extract appear in the Selected Output Channel(s) list, then click OK to finalize the selection.




Macros

Macros are entities which define straight text substitutions in a box and in any member nodes included in the box. An example of a macro might be MAX_FREQ = 1000. Then any primitive within this box node or its contained nodes which set a parameter to MAX_FREQ would have this value controlled at this one point. Macros do not appear in any visible form in an Alfi edit window. They are accessed only in the Edit Macros dialog.

Adding and Removing Macros
To add a macro to a box node, right-click in an empty spot in the box edit window, and select Edit Macros... And in the Edit Macros dialog, add a line which defines the macro in the format "NAME = VALUE", e.g., "MAX_FREQ = 1000". To remove a macro, merely remove the line which defines the macro you wish to delete.


Comments

Every node, box or primitive, can have a comment attached to it.

Adding and Removing Comments

To add a comment to a box node, right-click in an empty spot in the box edit window, and select Edit Comment... And in the Edit Comment dialog, add to or replace the current comment. Delete comments in the same manner, by just deleting the lines in the Edit Comment dialog.

Miscellaneous Comment Operations and Information



Commands in the Edit Window Background Popup Menu

A right-click in an edit window will pop up the default popup menu. Here is a list of the commands which can be given via this menu:
An esoteric note on inheritance in copied/pasted nodes.

  Opaque Box Nodes

Parameter Link Declarations (link declarations)are a special element which may only be added to root boxes. When link declarations are added to a root box, all instances of this box which are added to other containers are considered opaque boxes. An opaque box node behaves much in the same manner as a primitive node. Link declarations define names, default values, and a set of link targets (to be explained below.) When an opaque box member is double clicked on, instead of openning a window showing the contents of the box as usual, a settings dialog appears, just as if the opaque box were a primitive node. The parameter names in this dialog are those defined in the link declarations. These are the parameters the builder of this box wishes to be manipulated in instances of this box.

For example, if I were the author of the PSL box, I may decide that when it is placed in a container node like Detector, only a couple of the parameters in the primitive PSL.Laser should be changed by the user, perhaps waist_size_X, waist_size_Y, and polarization. Normally, the user would need to go into PSL and then open the settings dialog on Laser, but if I added link declarations to the root box PSL which linked these three Laser parameters to names in my declarations in the following manner:

In the PSL root window default popup, I select , and then add parameter links waist_size (which I've decided will set both Laser:waist_size_X and Laser:waist_size_Y) and polarization:




I have set the default values to be polarization = -1, and waist_size = 100 (which will set both waist_size_X and waist_size_Y in Laser to 100.)

After adding such parameter links to the PSL root box, now all instances of PSL will be considered opaque boxes. So, when a user double clicks on Detector.PSL, they will see this parameter dialog:



and may set the values of polarization and waist_size just as if Detector.PSL were a primitive node.