Skip navigation.
Home

Synchronizing Two Model Using Ptolemy II

1. Introduction

We have two models written in different programming language, here we use the Ptolemy II to integrate the two models, and control the synchronous running between them.

2. Add model file

Copy the source code of the Repast Simphony model to the Ptolemy II root directory

Copy the source code of the python model to Ptolemy II root directory.(The python source code importing problem you can read the “Adding External JARS to Ptolemy II Environment” doc)

3. Create interaction model

1. According to content described above, in the Ptolemy root directory,click the .bat file you create. Then the Ptolemy graphic UI will show as below:


figure1

In menu bar, choose “File”>>“New”>>“Graphic Editor”,Then it will show the Ptolemy graphic editor UI.


figure2

3. In the environment , add the signal actor that send the messages to model actors, each signal will trigger one step running of each model.

Here,we creaete a SmartSender actor, that send signal discretely,and it is controlled by DE Driector,it can synchronize the signal to the real time.SmartSender actor is written in java,and in the package myactors.If you want to use this actor in Ptolemy runtime environmentm you just need to put the compiled “myactor” package including “SmartSender.java” and “SmartSender.class” into the Ptolemy root directory, and in the graphic environment of Ptolemy,choose “Graphic” >> “Initiate Entity”, input your class name, click “OK”.

Of course,if you want to add more SmartSend Actors, just need to do “ctrl +c” and “ctrl + v”.

Warning: You should add myactors package into the Ptolemy root first,and the start the Ptolemy from .bat,The next just do the Initiate Entity step of import SmartSender.


figure3


figure4

4. After add the signal actor, add two actors,

1. Open the MoreLibraries directory,
2. Choose the python directory.
3. Add two PythonActors to the right model editing environment


figure5

5. Why choose two PythonActors?
In the Ptolemy Python is implemented by jython, so you can in the PythonActor import java class or java lib, and its code is clear and efficient. Also you can write an java actor according to the introduction in the Ptolemy documents, and add it to this environment.

6. Rename the PythonActor and edit its apparence.

1.right click on the PythonActor, choose “Customize”>> “Rename”


figure6

2. change the Display name as you need.
3. right click on the PythonActor, choose “Appearence”>> “Edit custom icon”
here, we just change one of the PythonActor,and change its appearance to SimphonyActor.


figure7


figure8

7. Add the ports to the actors so that they can send messages and receive messages from each other.

Right click one of the actor,choose “Customize”>> “Ports”

Here we rename the “input” to “signal”, “output” to “outctl” ,and also add another port named “inctl”, set the “input property ” checked.In this model, the messages we sent is used string type ,so we have to set the “outctl” and “inctl” prot type to “String”.These names we will used in the code.Also in ports configuration you can change the port direction, you just have to change the “Direction” property as you want.


figure9


figure10

Then click the “Apply” >> “Commit”. And the model show like below.


figure11

8. Write code in the actors so that they can send messages to and receive messages from each other, also they can deal with the messages to decide what to do.

1.right click on the PythonActor, choose “Open Actor”. In the editor environment, write the python code to invoke your python model script and deal with the messages, according the messages to invoke function or method in your python model script, also get messages from your python model script, and use the actors function to send it out.


figure12

PythonActor code is as below:

# This is a simple actor that copies the input to the output.
# You can remove the ports, add new ports, and modify the script.
#import the python model script, in the before,we has described how to add your .py file
#directory which is in the Ptolemy root directory to the jython classpath
from model import Model
# import message type from Ptolemy lib
import ptolemy.data.StringToken as StringToken
class Main :
"Copy the inputs to the output."
def __init__(self):
self.model=Model() #initialize the model
def fire(self) : # each signal will trigger the fire method of the actor.
for i in range(self.signal.getWidth()):
if self.signal.hasToken(i):
token = self.signal.get(i) #retrieve the signal
self.model.step() #model does step action
msg=self.model.getMsg() #get msg from the model inner,
if msg != "no_msg": #get msg from the model inner,
self.outctl.send(0,StringToken(msg))
for i in range(self.inctl.getWidth()):
if self.inctl.hasToken(i):
token=self.inctl.get(i) # retrieve the msg from other actor,
msg=token.stringValue()
self.model.doAct(msg) # invoke the functiono or method in model by sending msg to it

2. Right click on the SimphonyActor, choose “Open Actor”,It also show a dialog as opening PythonActor.
In the editor environment, write the python code to invoke your simphnoy model, but here, in order to make code clear, we don’t directly write python code to invoke the simphony model, we write java code as an adapter to invoke the simphony model.The java code you can refer to the document “Invoking Repast S Model”. We just need to put the package of that java application(That include two java class “TestRunner.java” and “TestMain.java”) to the Ptolemy root directory. We rewrite the TestMain.java, add some methods to initialize the model, run the step of model, also invoke agents’s method by sending messages to them, and return messages(It will be used by actor to send it to another actor) from model. So in the simphony actor,we just have to invoke the TestMain class, use it to control the action of the simphony model.
TestRunner.java:

package test;

import java.io.*;
import java.util.Iterator;
import repast.simphony.batch.BatchScenarioLoader;
import repast.simphony.context.Context;
import repast.simphony.engine.controller.Controller;
import repast.simphony.engine.controller.DefaultController;
import repast.simphony.engine.environment.AbstractRunner;
import repast.simphony.engine.environment.ControllerRegistry;
import repast.simphony.engine.environment.DefaultRunEnvironmentBuilder;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.environment.RunEnvironmentBuilder;
import repast.simphony.engine.environment.RunState;
import repast.simphony.engine.schedule.ISchedule;
import repast.simphony.engine.schedule.Schedule;
import repast.simphony.parameter.SweeperProducer;
import simphony.util.messages.MessageCenter;
import sampletest.SampleAgent;

public class TestRunner extends AbstractRunner {

private static MessageCenter msgCenter = MessageCenter.getMessageCenter(TestRunner.class);

private RunEnvironmentBuilder runEnvironmentBuilder;
protected Controller controller;
protected boolean pause = false;
protected Object monitor = new Object();
protected SweeperProducer producer;
private ISchedule schedule;
private Context context;

public TestRunner() {
runEnvironmentBuilder = new DefaultRunEnvironmentBuilder(this, true);
controller = new DefaultController(runEnvironmentBuilder);
controller.setScheduleRunner(this);
}

public void load(File scenarioDir) throws Exception{
if (scenarioDir.exists()) {
BatchScenarioLoader loader = new BatchScenarioLoader(scenarioDir);
ControllerRegistry registry = loader.load(runEnvironmentBuilder);
controller.setControllerRegistry(registry);

} else {
msgCenter.error("Scenario not found", new IllegalArgumentException(
"Invalid scenario " + scenarioDir.getAbsolutePath()));
return;
}

controller.batchInitialize();
controller.runParameterSetters(null);
}

public void runInitialize(){
controller.runInitialize(null);
RunState rs=RunState.getInstance();
schedule = rs.getScheduleRegistry().getModelSchedule(); //get the schedule
context = (Context)rs.getMasterContext(); //get the main context of the model
}

public void cleanUpRun(){
controller.runCleanup();
}
public void cleanUpBatch(){
controller.batchCleanup();
}

// returns the tick count of the next scheduled item
public double getNextScheduledTime(){
return ((Schedule)RunEnvironment.getInstance().getCurrentSchedule()).peekNextAction().getNextTime();
}

// returns the number of model actions on the schedule
public int getModelActionCount(){
return schedule.getModelActionCount();
}

// returns the number of non-model actions on the schedule
public int getActionCount(){
return schedule.getActionCount();
}

// Step the schedule
public void step(){
schedule.execute();
}

// stop the schedule
public void stop(){
if ( schedule != null )
schedule.executeEndActions();
}

public void setFinishing(boolean fin){
schedule.setFinishing(fin);
}

public void execute(RunState toExecuteOn) {
// required AbstractRunner stub. We will control the
// schedule directly.
}
public Iterator getAgentIterator(){ //get the collection of the agents
Iterator it=context.getObjects(SampleAgent.class).iterator();
return it;
}
public static void main(String[] args){
System.err.println(new TestRunner().getModelActionCount());
}
}

TestMain.java:

public class TestMain {
private TestRunner runner;
private int count=1;
private FileTest ft;
public TestMain(){
runner=new TestRunner();
ft=new FileTest("simphony.txt");
}
public void initialize(){ //initialize method
File file = new File("Sample_Test\\sampletest.rs");// repast scenario file directory
try {
runner.load(file); // load the repast scenario
} catch (Exception e) {
e.printStackTrace();
}
runner.runInitialize();
}

public void pushMsgToAgent(String msg){ //send messages to agents
Iterator it=runner.getAgentIterator();
while(it.hasNext()){
SampleAgent sa=(SampleAgent)it.next();
sa.doAction(msg);
}
}

public String getMsgFromAgents(){ //get the messages from the simphony model
String msg="no_msg";
if(count%5==0){
RandNum randNum=new RandNum();
Date date=new Date();
int action=randNum.getRandNumber();
if(action==1){
msg = "add";
}
else if(action==2){
msg = "minus";
}
else{
msg = "nothing";
}
ft.writeStr(date+" "+String.format("%16s","send msg "+msg));
}

return msg;
}
public void runStep(){ //step running method
System.err.print(runner.getActionCount());
count++;
runner.step();// execute all scheduled actions at next tick
}
public void endRunner(){ //stop the model method
runner.stop();
runner.cleanUpRun();
runner.cleanUpBatch();
}
}

SimphonyActor code is as below::

# This is a simple actor that copies the input to the output.
# You can remove the ports, add new ports, and modify the script.
#import the TestMain Adapter class,
#This class is as an intermediate that can initialize the simphony model and it control its actions.
import test.TestMain as TestMain
# import message type from Ptolemy lib
import ptolemy.data.StringToken as StringToken
class Main :
"Copy the inputs to the output."
def __init__(self):
self.testmain=TestMain()
self.testmain.initialize() # initialize the simphony model
def fire(self) : # each signal will trigger the fire method of the actor.
for i in range(self.signal.getWidth()):
if self.signal.hasToken(i):
token = self.signal.get(i) # retrieve the signal
self.testmain.runStep() # model does step action
ctl=self.testmain.getMsgFromAgents() # get msg from the model inner,
if ctl != "no_msg": # decide whether to send msg out
self.outctl.send(0,StringToken(ctl))
for i in range(self.inctl.getWidth()):
if self.inctl.hasToken(i):
token = self.inctl.get(i)
msg=token.stringValue() # retrieve the msg from other actor,
self.testmain.pushMsgToAgent(msg) # invoke the method in model by sending msg to it

9. Add the director.
Because we using the discrete signal, we have to add the DE Director, and it is in the Directors Directory.After adding it ,we double click it, in the open dialog ,changing the parameters such as start time, stop time and synchronize to the RealTime.
Here these parameters is corresponding to the SmartSender Actor. The all fire times is the value (stop_time-start_time)/fireperiod.
Also here we change two SmartSender Actors name separately to “PyClock” and “SimClock”.


figure13

10. Add relations among the actors

1. link PyClock’s output port to PythonActor’s signal port, SimClock’s output port to SimphonyActor’s signal port.
2. link PythonActor’s outctl port to SimphonyActor’s inctl port.Because in the DE model, if two actors send messages to each other , in one link between them we should add a TimeDelay actor to avoid conficting.(The TimeDelay’s value can be set to very small).
3. link SimphonyActor’s outctl port to TimeDelay’s input port, and link TimeDelay’s output port to the PythonActor’s inctl port.

11. Running the model
You just need to set the parameters of the DE Director actor, two Clock actors and TimeDelay actor, then click the run button on the menu bar(it is a green right direction arrow).

Latest image