1. 程式人生 > 其它 >NetLogo 踩坑系列(二):奇技淫巧

NetLogo 踩坑系列(二):奇技淫巧

NetLogo 踩坑系列(二):奇技淫巧

__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...");
        // model.nlogo 的路徑
		String model = args[0];
		try {
            // 初始化
			HeadlessWorkspace workspace = HeadlessWorkspace.newInstance();
            // 開啟模型檔案
			workspace.open(model);
            // 執行 NetLogo 命令
			workspace.command("setup");
            workspace.command("repeat 1 [ go ]");
            // 關閉模型檔案
            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 的第三方擴充套件可以正常工作。