Fork me on GitHub

NetLogo 进阶:导入 .nls 文件、Headless 模式、传递 Java 虚拟机参数

__includes 导入 .nls 文件

当模型包含多个例程时,全部写在一个 .nlogo 文件会显得臃肿、结构不清晰、可读性差,这就产生了拆分文件的需求。NetLogo 提供了一个实验性的 keyword __includes,根据官方文档,用法如下

#! model.nlogo
__includes [ "directory/lib.nls" "directory/package.nls" ]

其中 lib.nlspackage.nls 是两个 NetLogo 源文件(source file),directory 是存放该文件的目录。为更好展示其用法,本博客设计了以下例子,源代码放在 github 上。

单个文件

下面例子将一个打印 "Hello World!" 的程序复杂化,拆分成声明全局变量、初始化全局变量和应用全局变量、主例程 4 个步骤。这个例子虽然很愚蠢,但抽象自复杂 Model。

#! model.nlogo
; 声明全局变量
globals [
  greeting
]

; 初始化全局变量
to setup
  set greeting "Hello World!"
end

; 应用全局变量
to say-hi
  print greeting
end

; 主例程
to go
  say-hi
end

多个文件

当各个步骤变得复杂时,拆分文件可以让逻辑变得更清晰。下面把前 3 个步骤放到 3 个不同的 .nls 文件中

#! libs/global.nls
globals [
  greeting
]

#! libs/setup.nls
to setup
  set greeting "Hello World!"
end

#! libs/package.nls
to say-hi
  print greeting
end

然后在主例程中导入 3 个源文件(假设均放在目录 libs 下)

#! split_model.nlogo
__includes ["libs/global.nls" "libs/setup.nls" "libs/package.nls" ]

to go
  say-hi
end

下面对上述拆分做简单的分析。可以看到,.nlogo 文件仅有一个,只有该文件能够导入源文件,一个源文件可以直接使用另一个文件声明的变量(但不能重复声明,其实也可以调用另一个源文件定义的例程)。猜测源文件依附于模型文件存在,只有在其导入模型文件后才会生效,__includes 似乎只起到了“合并”文件的作用。这和 Python 中 .py 文件的地位有很大差别。

Headless 模式最佳实践

有时我们不关心交互式界面,希望在后台运行 model.nlogo,比如希望重复试验,希望在没有桌面的服务器上运行。通过 NetLogo 的 Headless 模式(无头模式)可以实现。推荐使用 Java 调用 NetLogo,好处是使用简单,可以结合 Java 实现更复杂的功能的。参考官方文档,写了 Test.java 文件进行测试,代码放在 github 上。

package com.test;
import org.nlogo.headless.HeadlessWorkspace;

public class Test {
    public static void main (String[] args) {
        System.out.println("Testing the headless mode...");
        // path for model.nlogo
	String model = args[0];
	try {
            // initialization
	    HeadlessWorkspace workspace = HeadlessWorkspace.newInstance();
            // open the model file
	    workspace.open(model);
            // execute NetLogo commands
	    workspace.command("setup");
            workspace.command("repeat 1 [ go ]");
            // close the model file
            workspace.dispose();
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }
}

该方法需要通过 -classpath 正确指定 NetLogo 的 jar 包的位置。此外可能遇到的坑是,模型文件无法正确导入 Java 扩展,需要将相应的 jar 包放到模型文件所在的目录下。

传递 Java 虚拟机参数

BehaviorSpace 固然灵活,但有时需要在 GUI 里面查看可视化结果,那如何在 NetLogo GUI 中设置环境变量、命令行传参呢?其实跟 BehaviorSpace 的道理是一样的。注意到 NetLogo 可以通过 netlogo.shnetlogo-gui.sh 运行,那么我们只需在 .sh 脚本中指定环境变量和传参就可以了。因为 NetLogo 5.1.0 的 shell 脚本比较简单,仅以该版本为例。在笔者的项目中,有一个第三方扩展需要调用 R 语言做统计分析,需要指定 JRI 的路径。原始脚本为

#!/bin/sh
cd "`dirname "$0"`"             # the copious quoting is for handling paths with spaces
# -Djava.library.path=./lib     ensure JOGL can find native libraries
# -Djava.ext.dirs=              ignore any existing JOGL installation
# -XX:MaxPermSize=128m          avoid OutOfMemory errors for large models
# -Xmx1024m                     use up to 1GB RAM (edit to increase)
# -Dfile.encoding=UTF-8         ensure Unicode characters in model files are compatible cross-platform
# -jar NetLogo.jar              specify main jar
# "$@"                          pass along any command line arguments
java -Djava.library.path=./lib -Djava.ext.dirs= -XX:MaxPermSize=128m -Xmx1024m -Dfile.encoding=UTF-8 -jar NetLogo.jar "$@"

指定 JRI 目录后的脚本为

#!/bin/sh
cd "`dirname "$0"`"             # the copious quoting is for handling paths with spaces
# -Djava.library.path=./lib     ensure JOGL can find native libraries
# -Djava.ext.dirs=              ignore any existing JOGL installation
# -XX:MaxPermSize=128m          avoid OutOfMemory errors for large models
# -Xmx1024m                     use up to 1GB RAM (edit to increase)
# -Dfile.encoding=UTF-8         ensure Unicode characters in model files are compatible cross-platform
# -jar NetLogo.jar              specify main jar
# "$@"                          pass along any command line arguments
java -Djava.library.path=./lib -Djava.ext.dirs= -XX:MaxPermSize=128m -Xmx1024m -Dfile.encoding=UTF-8 -Djava.library.path=/home/rotopia/R/x86_64-pc-linux-gnu-library/4.1/rJava/jri -jar NetLogo.jar "$@"

经过上述的改动,笔者项目中需要调用 JRI 的第三方扩展可以正常工作。

posted @ 2022-04-12 11:02  Rotopia  阅读(487)  评论(0编辑  收藏  举报