Controlling Guide

NetLogo 4.1.3 User Manual   

NetLogo can be invoked and controlled by another program running on the Java Virtual Machine. For example, you might want to call NetLogo from a small program that does something simple like automate a series of model runs. Or, you might want to embed NetLogo models in a larger application.

This section of the User Manual introduces this facility for Java programmers. We'll assume that you know the Java language and related tools and practices. But note that our API's are also usable from other languages for the Java Virtual Machine, such as Scala, Clojure, Groovy, JRuby, Jython, etc.

Note: The controlling facility is considered "experimental". It is likely to continue to change and grow. Code you write now that uses it may need changes in order to continue to work in future NetLogo versions.

The NetLogo API Specification contains further details.

Starting a Java VM for NetLogo

NetLogo makes several assumptions about the Java VM that it is running in, and therefore there are arguments which should be given to the VM at startup.

Recommended options for both GUI and headless

-server
Use server VM for highest performance.
-Xmx1024m
Use up to 1 gigabyte of memory for Java VM heap. You may need to grow this number in order to run some models.

Additional recommended options for GUI only

-XX:MaxPermSize=128m
Prevent the VM from running out of memory when repeatedly compiling a model with very long code.
-Djava.ext.dir=
Ignore any existing native libraries on the system. This avoids conflicts with other versions of JOGL. You may need to leave this option out, or modify it to point to your native libraries if you are using Java VM extensions.
-Djava.library.path=./lib
Not needed on Mac or Windows; may be needed on other OS's such as Linux. Ensures NetLogo can find native libraries for JOGL and other extensions. If you are not starting the VM in the top-level NetLogo directory, then ./lib should be changed to point to the lib subdirectory of the NetLogo installation.

Current working directory

The NetLogo application assumes that the current working directory at startup time is the top level of the NetLogo installation.

Example (with GUI)

Here is a small but complete program that starts the full NetLogo application, opens a model, moves a slider, sets the random seed, runs the model for 50 ticks, and then prints a result:

import org.nlogo.app.App;
public class Example1 {
    public static void main(String[] argv) {
        App.main(argv);
        try {
            java.awt.EventQueue.invokeAndWait
                ( new Runnable()
                    { public void run() {
                        try {
                          App.app.open
                              ("models/Sample Models/Earth Science/"
                               + "Fire.nlogo");
                        }
                        catch( java.io.IOException ex ) {
                          ex.printStackTrace();
                        }
                    } } );
            App.app.command("set density 62");
            App.app.command("random-seed 0");
            App.app.command("setup");
            App.app.command("repeat 50 [ go ]");
            System.out.println
                (App.app.report("burned-trees"));
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}

The equivalent code in Scala:

import java.awt.EventQueue
import org.nlogo.app.App
object Example1 {
  def main(args:Array[String]) {
    App.main(args)
    wait {
      App.app.open("models/Sample Models/Earth Science/Fire.nlogo")
    }
    App.app.command("set density 62")
    App.app.command("random-seed 0")
    App.app.command("setup")
    App.app.command("repeat 50 [ go ]")
    println(App.app.report("burned-trees"))
  }
  def wait(block: =>Unit) {
    EventQueue.invokeAndWait(
      new Runnable() { def run() { block } } ) }
}

In order to compile and run this, NetLogo.jar (from the NetLogo distribution) must be in the classpath. In addition, the lib directory (also from the NetLogo distribution) must be in same location; it contains additional libraries used by NetLogo.jar.

If you are using Scala, you'll need to make sure you are using exactly the right version of Scala. In order to run NetLogo in the same class loader with your own Scala code, you must use the specific Scala build dated 2009-04-16, revision r17517, available here: scala-2.8.0.r17517.zip. We apologize for the inconvenience of requiring this particular Scala version; in a future version of NetLogo, we plan to require the final released version of Scala 2.8.0 instead, once it becomes available. (Note that if you are a JVM wizard, you may actually be able to run your own Scala code using a different Scala version than NetLogo requires, by using the two Scala versions in different class loaders. At least, we don't know of a reason why this wouldn't work.)

Note the use of EventQueue.invokeAndWait to ensure that a method is called from the right thread. This is because most of the methods on the App class may only be called some certain threads. Most of the methods may only be called from the AWT event queue thread; but a few methods, such as main() and commmand(), may only be called from threads other than the AWT event queue thread (such as, in this example, the main thread).

Rather than continuing to discuss this example in full detail, we refer you to the NetLogo API Specification, which documents all of the ins and outs of the classes and methods used above. Additional methods are available as well.

Example (headless)

The example code in this case is very similar to the previous example, but with methods on an instance of the HeadlessWorkspace class substituted for static methods on App.

import org.nlogo.headless.HeadlessWorkspace;
public class Example2 {
    public static void main(String[] argv) {
        HeadlessWorkspace workspace =
            HeadlessWorkspace.newInstance() ;
        try {
            workspace.open
                ("models/Sample Models/Earth Science/"
                 + "Fire.nlogo");
            workspace.command("set density 62");
            workspace.command("random-seed 0");
            workspace.command("setup");
            workspace.command("repeat 50 [ go ]") ;
            System.out.println
                (workspace.report("burned-trees"));
            workspace.dispose();
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}

The equivalent code in Scala:

import org.nlogo.headless.HeadlessWorkspace
object Example2 {
  def main(args:Array[String]) {
    val workspace = HeadlessWorkspace.newInstance
    workspace.open(
      "models/Sample Models/Earth Science/Fire.nlogo")
    workspace.command("set density 62")
    workspace.command("random-seed 0")
    workspace.command("setup")
    workspace.command("repeat 50 [ go ]")
    println(workspace.report("burned-trees"))
    workspace.dispose()
  }
}

In order to compile and run this, NetLogo.jar must be in your classpath. The lib directory, containing additional required libraries, must also be present. When running in a context that does not support a graphical display, the system property java.awt.headless must be true, to force the VM to run in headless mode.

Since there is no GUI, NetLogo primitives which send output to the command center or output area now go to standard output instead. export-world can still be used to save the model's state. export-view works for writing an image file with a snapshot of the (otherwise invisible) 2D view. The report() method is useful for getting results out of the model and into your extension code.

The files generated by export-world include the contents of all plots. You can also export the contents of plots individually using export-plot.

You can make multiple instances of HeadlessWorkspace and they will operate independently on separate threads without interfering with each other.

When running headless, there are some restrictions:

The NetLogo API Specification contains further details.

In order to run 3D headless you must make sure that the org.nlogo.is3D property is set, you can either do this by starting Java with the -Dorg.nlogo.is3d=true option, or you can set it from within Java by using System.setProperty as follows:

public static void main( String [] args )
{
  org.nlogo.awt.Utils.invokeLater
    ( new Runnable() {
      public void run() {
            System.setProperty( "org.nlogo.is3d" , "true" ) ;
                HeadlessWorkspace workspace =
                  HeadlessWorkspace.newInstance() ;
                try {
                  workspace.open
            ("models/3D/Sample Models/"
             + "DLA 3D.nlogo");
          workspace.command("set wiggle-angle 70");
          workspace.command("random-seed 0");
          workspace.command("setup");            
          workspace.command("repeat 50 [ go ]") ;
          System.out.println(workspace.report("count patches with [pcolor = green]"));            
          workspace.dispose();        
        }        
        catch(Exception ex) {
          ex.printStackTrace();        
        }
    } } ) ;
}

Note that org.nlogo.is3D must be set before creating the workspace.

Example (embedding)

When your program controls NetLogo using the App class, the entire NetLogo application is present, including tabs, menubar, and so forth. This arrangement is suitable for controlling or "scripting" NetLogo, but not ideal for embedding a NetLogo model in a larger application.

We also have a distinct but similar API which allows embedding only the interface tab, not the whole window, in another application. To access this functionality use the org.nlogo.lite.InterfaceComponent class, which extends javax.swing.JPanel. You can use the embedded component much the same way that you use App's static methods. Here is the App example converted to use InterfaceComponent:

import org.nlogo.lite.InterfaceComponent;
public class Example3 {
    public static void main(String[] argv) {
        try 
        {
            final javax.swing.JFrame frame = new javax.swing.JFrame();
            final InterfaceComponent comp = new InterfaceComponent(frame);
            java.awt.EventQueue.invokeAndWait
                ( new Runnable()
                    { public void run() {
                        frame.setSize(1000,700);
                        frame.add(comp);
                        frame.setVisible(true);
                        try {
                          comp.open
                              ("models/Sample Models/Earth Science/"
                               + "Fire.nlogo");
                        }
                        catch(Exception ex) {
                          ex.printStackTrace();
                        }
                    } } ) ;
            comp.command("set density 62");
            comp.command("random-seed 0");
            comp.command("setup");
            comp.command("repeat 50 [ go ]");
            System.out.println(comp.report("burned-trees"));
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}

The equivalent code in Scala:

import org.nlogo.lite.InterfaceComponent
object Example3 {
  def main(args:Array[String]) {
    val frame = new javax.swing.JFrame
    val comp = new InterfaceComponent(frame)
    wait {
      frame.setSize(1000,700)
      frame.add(comp)
      frame.setVisible(true)
      comp.open(
        "models/Sample Models/Earth Science/Fire.nlogo")
    }
    comp.command("set density 62")
    comp.command("random-seed 0")
    comp.command("setup")
    comp.command("repeat 50 [ go ]")
    println(comp.report("burned-trees"))
  }
  def wait(block: =>Unit) {
    java.awt.EventQueue.invokeAndWait(
      new Runnable() { def run() { block } } ) }
}

The embedding API gives you a variety of model control features in addition to those provided in the App class. You can simulate button presses, enable logging, create and hide widgets, and so on. See the NetLogo API Specification for details.

To use the embedded component you must have NetLogo.jar in your classpath. If you want to use logging you must also have the log4j jar from the lib directory in your classpath.

Conclusion

Don't forget to consult the NetLogo API Specification for full details on these classes and methods.

Some API facilities exist, but are not yet documented. So if you don't see the capability you want, contact us; we may be able to help you do you what you want. Please do not hesitate to contact us at feedback@ccl.northwestern.edu with questions, as we may be able to find a workaround or provide additional guidance where our documentation is thin.