-
-
Notifications
You must be signed in to change notification settings - Fork 247
Controlling API
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 page introduces this facility for Java and Scala programmers. We'll assume that you know Java or Scala and associated tools and practices. But note that our API's are also usable from other languages for the Java Virtual Machine, such as 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.
For discussion of NetLogo API's and NetLogo development in general, visit http://groups.google.com/group/netlogo-devel.
The NetLogo API Specification contains further details. Details on transitioning from the NetLogo 5 to NetLogo 6 API can be found in the 6.0 extension and controlling API transition guide.
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.
-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.
-Dfile.encoding=UTF-8
Force all file I/O to use UTF-8 encoding. Ensures that NetLogo can load and save all models consistently, and that file-* primitives work consistently on all platforms, including models containing Unicode characters.
-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.
The NetLogo application assumes that the current working directory at startup time is the installation directory in which NetLogo.jar
resides. On Mac OS X this is NetLogo.app/Contents/Java/
, on Windows this is C:\<Path to NetLogo>\app
, and on linux this is <path to NetLogo>/app
. The lib
subdirectory (relative to the working directory) includes native libraries for Mac (is not needed on other platforms) and the natives
subdirectory (relative to the working directory) includes native libraries for all platforms.
NetLogo further assumes that there will be models
, docs
, and extensions
subdirectories of the working directory, but will look for them at a specific path if following VM options are set:
- Docs -
-Dnetlogo.docs.dir=<path-to-docs>
- Extensions -
-Dnetlogo.extensions.dir=<path-to-extensions>
- Models -
-Dnetlogo.models.dir=<path-to-models>
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 Scala 2.9. (2.9.1, 2.9.2, and 2.9.3 are all acceptable.) Other versions such as 2.10 will not 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.
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()
}
}
And in Clojure:
(import org.nlogo.headless.HeadlessWorkspace)
(def workspace
(doto (HeadlessWorkspace/newInstance)
(.open "models/Sample Models/Earth Science/Fire.nlogo")))
(doto workspace
(.command "set density 62")
(.command "random-seed 0")
(.command "setup")
(.command "repeat 50 [ go ]"))
(println (.report workspace "burned-trees"))
(.dispose workspace)
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
movie-*
primitives are not available; trying to use them will cause an exception. -
user-*
primitives which query the user for input, such asuser-yes-or-no
will cause an exception.
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.EventQueue.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.
This example code shows how to run HubNet headlessly. It opens the Template model, runs both the hubnet-reset
and setup
commands, and then runs the go
command in a loop, forever.
import org.nlogo.headless.HeadlessWorkspace;
class HubNetHeadlessServerExample {
static public void main(String[] args) {
HeadlessWorkspace workspace = HeadlessWorkspace.newInstance();
try {
workspace.open("models/HubNet Activities/Code Examples/Template.nlogo");
workspace.command("hubnet-reset");
workspace.command("setup");
while(true) {
workspace.command("go");
}
}
catch(Exception ex) {
ex.printStackTrace();
}
}
}
The next example shows not only how to run HubNet headlessly, but allows for commands to be entered while the model is running. Any commands entered by the while the go
command is executing will be run immediately after it finishes.
class HubNetHeadlessServerExample {
static public void main(String[] args) {
HeadlessWorkspace workspace = HeadlessWorkspace.newInstance();
CommandLineThread commandLine = new CommandLineThread(workspace);
commandLine.start();
try {
workspace.open(
"models/HubNet Activities/Code Examples/Template.nlogo");
workspace.command("hubnet-reset");
workspace.command("setup");
while (true) {
workspace.command("go");
commandLine.runCommands();
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
static class CommandLineThread extends Thread {
private final HeadlessWorkspace workspace;
private ArrayBlockingQueue<String> queuedCommands =
new ArrayBlockingQueue<String>(10);
public CommandLineThread(HeadlessWorkspace workspace) {
super("Command Line Thread");
this.workspace = workspace;
}
public void run() {
System.out.println("enter command> ");
while (true) {
try {
queuedCommands.put(readLine());
}
catch (Exception e) {
e.printStackTrace();
}
}
}
private void runCommands() {
for (String command : queuedCommands) {
System.out.println("executing command: " + command);
try {
workspace.command(command);
}
catch (Exception e) {
e.printStackTrace();
}
System.out.print("enter command> ");
}
queuedCommands.clear();
}
private BufferedReader reader =
new BufferedReader(new InputStreamReader(System.in));
private String readLine() throws IOException {
return reader.readLine();
}
}
}
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 from above 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.
Don't forget to consult the NetLogo API Specification for full details on these classes and methods.
You may also find it useful to consult the NetLogo source code on GitHub.
Some API facilities exist, but are not yet documented. Please do not hesitate to ask questions on netlogo-devel, as the NetLogo developers can often provide information about undocumented calls and other kinds of guidance.
- Extensions
- NetLogo Libraries
- Controlling API
- Extensions API
- 6.0 Extension and Controlling API Transition-Guide
- Optimizing NetLogo Runs
- Setting English as the Default Language when Running NetLogo
- Unofficial Features
- Advanced Installation
- Optimization List
- Java System Properties and how to use them
- NetLogo on ARM Devices
- Multiple Views via HubNet
- Branches
- Building
- Tests
- Windows Setup
- Continuous Integration
- Draft: How to Help
- NetLogo Project Ideas
- Syntax Highlighting
- Building with IntelliJ
- Code Formatting
- Localization
- File (.nlogo) and Widget Format
- Benchmarking
- Releasing
- Preparing the Models Library for Release
- Documentation
- NetLogo Bundled Java Versions
- JOGL
- Plugins API
- Architecture
- LazyAgentset
- Model Runs and Review Tab
- Model Runs: To Do and Code Overview
- Notes on in Radius
- Archived Branches
- The nlogox format
- Touch API Proposal
- Why isn't NetLogo "Parallel"?
- Potential Speedups
- Tortoise
- SimServer, WebStart, and NetLogo in Classrooms