Previous Next Table of Contents

11. Extended Objects

GENESIS provides an extensive set of objects for building simulations. The extended object facilities may be used to create additional objects at the script level. New objects may also be added to GENESIS by compiling new object libraries written in C. (See Customizing GENESIS.)

A GENESIS object is characterized by its object name, fields, message definitions, actions and classes. An extended object is created by adding new fields, message definitions, actions or classes to an element of a preexisting GENESIS object. The new object defined through extending the element may be added as a named GENESIS object using the addobject command, or the element (and copies of it) may be used directly without using addobject.

Although extended objects may be used for any purpose they are usually used for one of three reasons: specialization, composition or creating completely new objects. Specialization customizes a given GENESIS object to describe a specific component in the model; the sodium channel of a squid giant axon for example. Composition takes a potentially complex combination of elements using them together to implement a new function and then presents the composition as a single object. A new object with a completely new function may be implemented by extending a neutral element.

An extended object is said to be derived from or based on the object from which it was extended. This derivation gives rise to an isa relationship between the extended object and its base object (or objects). The isa command and ISA wildcard path option allow testing and selection of elements based on generic object names. For example, if there were a number of specialized channels derived from the hh_channel object, the wildcard path

        /##[ISA=hh_channel]
would select all elements of all the specialized objects while
        /##[OBJECT=hh_channel]
would select only those element created directly from hh_channel.

If the class of the extended object needs to belong to an additional class besides that of the base object, another one may be added with the addclass command. This will be necessary when the simulation schedule for the new extended object needs to be different from that of the base object, as when the base object is a neutral object. (See Simulation Schedules.)

11.1 Extending Fields

New fields are added to an element using the addfield command. A new field may be used in every respect just as any field built in to the element. The -indirect option of addfield adds fields which refer to other fields of the element (field aliases) or to fields of other elements in the element hierarchy (useful for making fields of elements within a composition available). Fields added with addfield may later be removed with the deletefield command.

Fields may also be extended by changing the accessibility of the field with setfieldprot . A field may be one of readwrite, readonly or hidden. The accessibility of a field determines what a script executing outside the scope of the element may do with the field. A readwrite field is completely accessible, readonly fields may not be set and hidden fields may not be examined or set, though their presence in the element may be determined by the exists and showobject commands. A field's accessibility may never be made more permisive than the accessibility defined by the base object being extended (i.e. the computed field of an element which is readonly may not be made readwrite by extending the element), unless the -debug option of setfieldprotect is used.

Examples:

    create compartment mycompt
    addfield mycompt RM -description "Specific membrane resistance"
    setfieldprot mycompt -readonly Rm // Rm is now computed
    setfieldprot mycompt -readwrite Vm // Error Vm was readonly

    create xcell acell
    addfield acell fatmin -indirect . diamin \
        -description "XODUS 1 name for diamin"

11.2 Extending Actions

New actions may be added to an element by defining a script function implementing the action and using the addaction command to add this function as an action function for the element. Once added, all calls on the added action will result in a call to the script function. The first argument to the action function is always the name of the action followed by additional action-specific arguments.

An extended object may also override an action already defined for the base object. A few of the built in actions provide a chaining of calls to action functions in which all action functions defined for the object and its base objects are called in a specific order.

Current chained actions include CREATE, COPY, SET and DELETE. CREATE and COPY actions are called in base object to subobject order while SET and DELETE are called in subobject to base object order. The chaining of SET actions and subsequent direct setting of the element field are aborted if any SET action function returns a non-zero value. Manual chaining of other actions can be implemented using the call command's ``-parent'' option within an action function. For example, to perform the PROCESS action of the base object, as well as some new ones which are defined for the extended object, add

        call . PROCESS -parent
to the beginning of the script function which defines the PROCESS action for the extended object.

When executing an action function, the working element is set to the element the action is called on and the effective accessibility of the element fields change. Within an action function any fields added with addfield are effectively readwrite and fields of the base object revert to the accessibility defined by the base object.

Example:

    // Here's a SET action function to calculate the actual membrane
    // resistance when the specific resistance, length or diameter
    // changes

    function __mycomptSET__(action, field, value)
        float   len, dia, RM

        // don't have to name the element since mycompt is cwe

        len = {getfield len}
        dia = {getfield dia}
        RM = {getfield RM}

        if (field == "RM")
            RM = value
        elif (field == "len")
            len = value
        elif (field == "dia")
            dia = value
        else
            return 0
        end

        // Rm is readwrite within the action function

        setfield Rm {RM * PI * dia * len}

        return 0
        end

    addaction mycompt SET __mycomptSET__

11.3 Extending Message Definitions

New messages types can be defined for an element with the addmsgdef command. A new message type can then be used by the addmsg command to add messages of the new type. Adding a new message definition typically means overriding the PROCESS action to obtain and use the new message data.

Here is a simple example which creates an extended object that uses its PROCESS action to receive an INPUT message and compute the sine of its input. As there is no similar pre-defined object available to use as the base object, it is based on the most primitive object, the neutral object. As neutral elements are not scheduled for processing, we will want to use the addclass command to assign the new object to a class which is scheduled for the PROCESS action.

    create neutral /sine

    // Create fields called input and output
    addfield /sine input
    addfield /sine output

    // Create a message definition for the INPUT message, define a PROCESS
    // action to process the INPUT message and make the sine object a device
    // class object
    addmsgdef /sine INPUT value
    addaction /sine PROCESS __sinePROCESS__
    addclass /sine device

    // PROCESS action script function
    function __sinePROCESS__(action)

        // check for an INPUT message: since its the only message type
        // sine will accept, just make sure at least one message is there
        // and use the first message's value

        if ({getmsg . -incoming -count} > 0)
            // Have an INPUT message: compute output and set input field
            // based on INPUT message value
            float input = {getmsg . -incoming -slot 0 0}
            setfield input {input} output {sin {input}}
        else
            // no INPUT message: compute output based on input field value
            setfield output {sin {getfield input}}
        end

        return 1
    end

    // create the sine object
    addobject sine /sine -author "J. R. Hacker" \
        -description "Computes sine of input"
    resched

Note the use of resched after the sine element is turned into the sine object. This command (or a reset) is needed so that the new object will be included in the current simulation schedule.

The situation is a little more complicated when we want to construct a composite element, and the message is intended to be forwarded to a child element. In a composition, child elements are not directly accessible to the user once the addobject command is used. In order to allow message data to be passed to a child element, a message added on the parent may be forwarded to the child from the parent's ADDMSGIN action using the addforwmsg command. In this case the message data is handled directly by the child element. The forwarded message must be deleted explicitly by calling the deleteforwmsg command in the parent's DELETEMSGIN action. Addforwmsg requires that the destination element accept messages with the same name and number of data slots as the message being forwarded.

Example:

    // This is part of a compositition which would normally include
    // an axis scaling menu and perhaps some additional buttons or
    // labels

    create xform /form
    create xgraph /form/graph

    addmsgdef /form PLOT data plotname color
    addaction /form ADDMSGIN __compgraphMSGIN__
    addaction /form DELETEMSGIN __compgraphMSGIN__

    // __compgraphMSGIN__ is used for both the ADDMSGIN and
    // DELETEMSGIN actions, the action argument is checked to
    // determine which action is being called

    function __compgraphMSGIN__(action, msgnum)
        str msgtype

        msgtype = {getmsg . -incoming -type {msgnum}}

        if (msgtype == "PLOT")
            if (action == "ADDMSGIN")
                addforwmsg . {msgnum} graph
            else  // DELETEMSGIN
                deleteforwmsg . {msgnum} graph
            end
        end
    end

11.4 Extending Classes

Additional class names can be associated with an element using the addclass command. Class names added with addclass can be used in wildcard path specifications for selecting groups of elements.

Example:

        addclass mycompt specificmemb

        setfield /##[CLASS=specificmemb] RM 1234

11.5 Using Extended Objects for Specialization

Specialization entails setting certain fields of an existing element to represent a more specialized component of a model. The fields which define the specialized element should not be changed once set or the element would no longer represent the specialized component. This can be enforced by setting the field accessibility to readonly or hidden.

In the following example we define a Hodgkin-Huxley squid giant axon potassium channel as a specialization of the hh_channel object. This is a scaled down version of Scripts/tutorials/hhchan_K.g which provides the same specialization, but also adds a gdens field to model channel density within a compartment and automates message setup between the channel and its compartment.

    // constants for form of rate constant equations

    int EXPONENTIAL = 1
    int SIGMOID     = 2
    int LINOID      = 3

    // H-H squid parameters

    float EREST_ACT = -0.070
    float EK        = -0.082

    // Now create the hh_channel and set fields for our channel.

    create hh_channel K_squid_hh
    setfield K_squid_hh \
            Ek              {EK} \                          // V
            Gbar            0.0  \
            Xpower          4.0 \
            Ypower          0.0 \
            X_alpha_FORM    {LINOID} \
            X_alpha_A       -10.0e3 \                       // 1/V-sec
            X_alpha_B       -10.0e-3 \                      // V
            X_alpha_V0      {10.0e-3+EREST_ACT} \           // V
            X_beta_FORM     {EXPONENTIAL} \
            X_beta_A        125.0 \                         // 1/sec
            X_beta_B        -80.0e-3 \                      // V
            X_beta_V0       {0.0+EREST_ACT}                 // V

    // Hide the hh_channel fields which define the rate equations.
    // We could have just made them readonly, but hiding them reduces
    // the number of fields the user must consider.

    setfieldprot K_squid_hh -hidden  Xpower Ypower X_alpha_FORM \
        X_alpha_A X_alpha_B X_alpha_V0 X_beta_FORM X_beta_A \
        X_beta_B X_beta_V0 Y_alpha_FORM Y_alpha_A Y_alpha_B \
        Y_alpha_V0 Y_beta_FORM Y_beta_A Y_beta_B Y_beta_V0 
                
    // Now add this specialization as a new object.

    addobject K_squid_hh K_squid_hh -author "J. R. Hacker" \
        -description "Hodgkin-Huxley Active K Squid Channel - SI units"

11.6 Using Extended Objects for Composition

Composition uses a number of cooperating elements to create a reusable new component performing a complex task. The new component presents itself as a single element even though it is composed of a combination of other elements. In the following example a reusable graph scaling button is created from the basic built-in graphical elements. The component includes a popup dialog for setting graph axis ranges. The new object appears as an xbutton with a single additional field which is set to the name of the graph to control.

NOTE: XODUS callback scripts don't work particularly well for widgets inside a composite object, since you can't use the full widget path to refer to the widget. Here we use actions rather than script callbacks, since an action sets the affected widget as the current working element.

    // First we create the button which when clicked upon brings up
    // the scaling dialog.  All widgets must be created within a form

    create xform /form
    create xbutton /form/scale

    // Add a field which names the graph that scaling changes are to
    // apply to.  We set the default value of the graphpath to the
    // parent of the scale button.  This works nicely if we normally
    // create graphscales under the corresponding graph.

    addfield /form/scale graphpath -description "pathname to graph element"
    setfield /form/scale graphpath ..

    // Add actions for popup and hiding of graphscale dialog

    // __gs_B1DOWN: on a button press, copy the cuurent graph axis
    // settings from the graph named in the graphpath field to the
    // popup dialogs and show the dialog

    function __gs_B1DOWN(action)
        str graph = {getfield graphpath}
        if (!{isa xgraph {graph}})
            echo xgraphscale {el .} graphpath {graph} is not an xgraph
            return
        end

        // Initialize the value of each dialog from the corresponding
        // graph field.
        str field
        foreach field (xmin xmax ymin ymax)
            setfield popup/{field} value {getfield {graph} {field}}
        end
            xshow popup
    end

    // __gs_HIDE: hide the popup graph scale form

    function __gs_HIDE(action)
        xhide popup
    end

    // __gs_APPLY: apply a change to a graph scale dialog to the
    // appropriate graph field.

    function __gs_APPLY(action, field, value)
        str graph = {getfield graphpath}
        setfield {graph} {field} {value}
    end

    addaction /form/scale B1DOWN __gs_B1DOWN
    addaction /form/scale HIDE __gs_HIDE
    addaction /form/scale APPLY __gs_APPLY

    // Now create a new form under the scale button.  This form will
    // hold the popup dialog for setting the graph values.  In the
    // popup form we create the dialogs for setting the graph axis
    // ranges and a done button.

    create xform /form/scale/popup [300,0,400,300]
    create xdialog /form/scale/popup/xmin
        addaction ^ B1DOWN __gs_apply_one
    create xdialog /form/scale/popup/xmax
        addaction ^ B1DOWN __gs_apply_one
    create xdialog /form/scale/popup/ymin
        addaction ^ B1DOWN __gs_apply_one
    create xdialog /form/scale/popup/ymax
        addaction ^ B1DOWN __gs_apply_one
    create xbutton /form/scale/popup/done
        addaction ^ B1DOWN __gs_call_hide

    // Call the APPLY action on the graphscale to update
    // the graph field

    function __gs_apply_one(action)
        call ../.. APPLY {getfield name} {getfield value}
    end

    // Call the HIDE action on the graphscale to hide the
    // graph scale dialog

    function __gs_call_hide(action)
        call ../.. HIDE
    end

    // Now that we have all the pieces in place, add the new object.
    // We can also delete the form we used to create the new button.

    addobject xgraphscale /form/scale
    delete /form

Now adding a graph scaling button to a graph is as easy as creating an element. The following works even without setting the graphpath field since graphpath defaults to the parent of the xgraphscale.

    create xform /form
    create xgraph /form/graph
    create xgraphscale /form/graph/scale

11.7 Related Commands

The following routines are used for working with extended objects:

Routine Description
addaction Add an action to an element and a script function to
implement the action
addclass Add a class to the class list of an element
addfield Add a field to an element
addforwmsg Forward a copy of an existing message to another element
addmsgdef Add a message definition to an element
addobject Add the object described by a given element as a named
GENESIS object
deleteaction Delete an action previously added to an element
deleteclass Delete a class previously added to an element
deletefield Delete a field previously added to an element
deleteforwmsg Delete a forwarded message to another element
deletemsgdef Delete a message previously added to an element
setfieldprot Set the accessibility for a field


Previous Next Table of Contents