
io.github.agentsoz.bushfiretute.matsim.ABMModel Maven / Gradle / Ivy
Show all versions of bushfire-tutorial Show documentation
package io.github.agentsoz.bushfiretute.matsim;
/*
* #%L
* BDI-ABM Integration Package
* %%
* Copyright (C) 2014 - 2017 by its authors. See AUTHORS file.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import io.github.agentsoz.bdiabm.data.ActionContent;
import io.github.agentsoz.bdimatsim.AgentActivityEventHandler.MonitoredEventType;
import io.github.agentsoz.bdimatsim.*;
import io.github.agentsoz.bdimatsim.app.BDIActionHandler;
import io.github.agentsoz.bdimatsim.app.BDIPerceptHandler;
import io.github.agentsoz.bdimatsim.app.MATSimApplicationInterface;
import io.github.agentsoz.bushfiretute.*;
import io.github.agentsoz.bushfiretute.datacollection.ScenarioTwoData;
import io.github.agentsoz.bushfiretute.shared.ActionID;
import io.github.agentsoz.bushfiretute.shared.PerceptID;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.population.*;
import org.matsim.core.mobsim.qsim.ActivityEndRescheduler;
import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils;
import org.matsim.core.network.NetworkImpl;
import org.matsim.core.utils.geometry.CoordImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scenarioTWO.agents.EvacResident;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class ABMModel implements MATSimApplicationInterface {
private final Logger logger = LoggerFactory.getLogger("");
private final MATSimModel model;
private final BDIModel bdiModel;
private Replanner replanner = null;
public ABMModel(BDIModel bdiModel) {
this.bdiModel = bdiModel;
this.model = new MATSimModel(bdiModel, new MATSimBDIParameterHandler());
model.registerPlugin(this);
}
/**
* Provides a custom Replanner (extended) to use with MATSim.
*/
@Override
public Replanner getReplanner(ActivityEndRescheduler activityEndRescheduler) {
if (replanner == null) {
replanner = new CustomReplanner(model, activityEndRescheduler);
}
return replanner;
}
/**
* Use this to pre-process the BDI agents list if needed. For instance,
* tasks like adding/removing specific agents, or renaming agents IDs,
* should be done here. This function is called just prior to the
* BDI agent counterparts in MATSim being created.
*
*/
@Override
public void notifyBeforeCreatingBDICounterparts(List> bdiAgentsIDs) {
}
/**
* Initialise the BDI agents with any application specific data. This
* function is just immediately after the MATSim counterparts have been
* created.
*/
@Override
public void notifyAfterCreatingBDICounterparts(List> bdiAgentsIDs) {
Map,? extends Link> links = model.getScenario().getNetwork().getLinks();
for (Id agentId : bdiAgentsIDs) {
@SuppressWarnings("unused")
MATSimAgent agent = model.getBDIAgent(agentId);
EvacResident bdiAgent = bdiModel.getBDICounterpart(agentId.toString());
if (bdiAgent == null) {
logger.warn("No BDI counterpart for MATSim agent '" + agentId
+ "'. Should not happen, but will keep going");
continue;
}
Plan plan = WithinDayAgentUtils.getModifiablePlan(model.getMobsimAgentMap().get(agentId));
List planElements = plan.getPlanElements();
// Assign start location
double lat = links.get(model.getMobsimAgentMap().get(agentId).getCurrentLinkId()).getFromNode().getCoord().getX();
double lon = links.get(model.getMobsimAgentMap().get(agentId).getCurrentLinkId()).getFromNode().getCoord().getY();
bdiAgent.startLocation = new double[] { lat, lon };
bdiAgent.currentLocation = "home"; // agents always start at home
bdiAgent.log("is at home at location "+lon+","+lat);
for (int i = 0; i < planElements.size(); i++) {
PlanElement element = planElements.get(i);
if (!(element instanceof Activity)) {
continue;
}
Activity act = (Activity) element;
// Get departure time
if (act.getType().equals("Evacuation")) {
PlanElement pe = plan.getPlanElements().get(i + 1);
if (!(pe instanceof Leg)) {
logger.error("Utils : selected plan element to get deptime is not a leg");
continue;
}
Leg depLeg = (Leg) pe;
double depTime = depLeg.getDepartureTime();
logger.trace("departure time of the depLeg : {}", depTime);
bdiAgent.setDepTime(depTime);
}
// Assign coords of safe destination
if (act.getType().equals("Safe")) {
double safeX = act.getCoord().getX();
double safeY = act.getCoord().getY();
bdiAgent.endLocation = new double[] { safeX, safeY };
bdiAgent.log("safe location is at "+safeX+","+safeY);
}
}
// Assign dependent persons (to pick up before evacuating)
assignDependentPersons(bdiAgent);
}
}
/**
* This is where we register all application specific BDI actions, and/or
* overwrite default ones (like {@link MATSimActionList#DRIVETO}).
*
* This is also the place to register action-dependent percepts.
* For instance, {@link MATSimPerceptList.ARRIVED} is conditional on the
* agent arriving at the network link in action
* {@link MATSimActionList.DRIVETO}, and so must be registered at the
* same time.
*
* Action-independent percepts should be registered using
* {@link this#registerNewBDIPercepts(MATSimPerceptHandler)}.
*
* Note that actions/percepts are registered per agent,
* i.e. handlers passed in belong to specific agents.
*/
@Override
public void registerNewBDIActions(MATSimActionHandler withHandler) {
// overwrite default DRIVETO
withHandler.registerBDIAction(MATSimActionList.DRIVETO, new BDIActionHandler() {
@Override
public boolean handle(String agentID, String actionID, Object[] args, MATSimModel model) {
// Get nearest link ID and calls the CustomReplanner to map to MATSim.
Id newLinkId;
double[] coords = (double[]) args[1];
if (args[1] instanceof double[]) {
newLinkId = ((NetworkImpl) model.getScenario().getNetwork())
.getNearestLinkExactly(new CoordImpl(coords[0], coords[1])).getId();
} else {
throw new RuntimeException("Destination coordinates are not given");
}
((CustomReplanner)model.getReplanner()).addNewLegToPlan(Id.createPersonId(agentID), newLinkId, (String) args[2]);
// Now register a event handler for when the agent arrives at the destination
MATSimAgent agent = model.getBDIAgent(agentID);
EvacResident bdiAgent = bdiModel.getBDICounterpart(agentID.toString());
bdiAgent.log("has started driving to coords "+coords[0] + "," + coords[1]
+" i.e. link "+newLinkId.toString());
agent.getPerceptHandler().registerBDIPerceptHandler(
agent.getAgentID(),
MonitoredEventType.ArrivedAtDestination,
newLinkId,
new BDIPerceptHandler() {
@Override
public boolean handle(Id agentId, Id linkId, MonitoredEventType monitoredEvent, MATSimModel model) {
MATSimAgent agent = model.getBDIAgent(agentId);
EvacResident bdiAgent = bdiModel.getBDICounterpart(agentId.toString());
Object[] params = { linkId.toString() , Long.toString(bdiAgent.getCurrentTime())};
agent.getActionContainer().register(MATSimActionList.DRIVETO, params);
agent.getActionContainer().get(MATSimActionList.DRIVETO).setState(ActionContent.State.PASSED);
agent.getPerceptContainer().put(MATSimPerceptList.ARRIVED, params);
return true; //unregister this handler
}
});
return true;
}
});
// register new action
withHandler.registerBDIAction(ActionID.CONNECT_TO, new BDIActionHandler() {
@Override
public boolean handle(String agentID, String actionID, Object[] args, MATSimModel model) {
String destination = (String) args[1];
// connect To route replanner method
Id newLinkId = ((CustomReplanner)model.getReplanner()).replanCurrentRoute(Id.createPersonId(agentID), destination);
if (newLinkId == null) {
logger.warn("CONNECT_TO: returned a null link from the target activity");
return true;
}
// Now register a event handler for when the agent arrives at the destination
MATSimAgent agent = model.getBDIAgent(agentID);
EvacResident bdiAgent = bdiModel.getBDICounterpart(agentID.toString());
bdiAgent.log("replanned to drive to connecting link " + newLinkId.toString());
agent.getPerceptHandler().registerBDIPerceptHandler(
agent.getAgentID(),
MonitoredEventType.ArrivedAtDestination,
newLinkId,
new BDIPerceptHandler() {
@Override
public boolean handle(Id agentId, Id linkId, MonitoredEventType monitoredEvent, MATSimModel model) {
MATSimAgent agent = model.getBDIAgent(agentId);
EvacResident bdiAgent = bdiModel.getBDICounterpart(agentId.toString());
Object[] params = { linkId.toString() , Long.toString(bdiAgent.getCurrentTime())};
agent.getActionContainer().register(ActionID.CONNECT_TO, params);
agent.getActionContainer().get(ActionID.CONNECT_TO).setState(ActionContent.State.PASSED);
agent.getPerceptContainer().put(PerceptID.ARRIVED_CONNECT_TO, params);
return true; //unregister this handler
}
});
return true;
}
});
// register new action
withHandler.registerBDIAction(ActionID.DRIVETO_AND_PICKUP, new BDIActionHandler() {
@Override
public boolean handle(String agentID, String actionID, Object[] args, MATSimModel model) {
// Get nearest link ID and calls the CustomReplanner to map to MATSim.
Id newLinkId;
double[] coords = (double[]) args[1];
if (args[1] instanceof double[]) {
newLinkId = ((NetworkImpl) model.getScenario().getNetwork())
.getNearestLinkExactly(new CoordImpl(coords[0], coords[1])).getId();
} else {
throw new RuntimeException("Destination coordinates are not given");
}
((CustomReplanner)model.getReplanner()).addNewLegAndActvityToPlan(Id.createPersonId(agentID), newLinkId, (int) args[3]);
// Now register a event handler for when the agent arrives at the destination
MATSimAgent agent = model.getBDIAgent(agentID);
EvacResident bdiAgent = bdiModel.getBDICounterpart(agentID.toString());
bdiAgent.log("will drive to pickup from coords "+coords[0] + "," + coords[1]
+" i.e. link "+newLinkId.toString());
// Now register a event handler for when the agent arrives and finished picking up the destination
agent.getPerceptHandler().registerBDIPerceptHandler(
agent.getAgentID(),
MonitoredEventType.EndedActivity,
newLinkId,
new BDIPerceptHandler() {
@Override
public boolean handle(Id agentId, Id linkId, MonitoredEventType monitoredEvent, MATSimModel model) {
MATSimAgent agent = model.getBDIAgent(agentId);
Object[] params = { linkId.toString() };
agent.getActionContainer().register(ActionID.DRIVETO_AND_PICKUP, params);
agent.getActionContainer().get(ActionID.DRIVETO_AND_PICKUP).setState(ActionContent.State.PASSED);
agent.getPerceptContainer().put(PerceptID.ARRIVED_AND_PICKED_UP, params);
return true; //unregister this handler
}
});
return true;
}
});
// register new action
withHandler.registerBDIAction(ActionID.SET_DRIVE_TIME, new BDIActionHandler() {
@Override
public boolean handle(String agentID, String actionID, Object[] args, MATSimModel model) {
double newEndTime = (double) args[1];
String actType = (String) args[2];
((CustomReplanner)model.getReplanner()).forceEndActivity(Id.createPersonId( agentID ),actType, newEndTime);
// Now set the action to passed straight away
MATSimAgent agent = model.getBDIAgent(agentID);
EvacResident bdiAgent = bdiModel.getBDICounterpart(agentID.toString());
bdiAgent.log("has set the drive time for activity " + actType + " to " + newEndTime);
Object[] params = {};
agent.getActionContainer().register(ActionID.SET_DRIVE_TIME, params);
agent.getActionContainer().get(ActionID.SET_DRIVE_TIME).setState(ActionContent.State.PASSED);
return true;
}
});
}
/**
* Register any action-independent percepts here. Percepts that are
* conditional on actions (such as {@link MATSimPerceptList#ARRIVED} that
* is specific {@link ActionID#DRIVETO}
*/
@Override
public void registerNewBDIPercepts(MATSimPerceptHandler withHandler) {
// For all agents, register a percept for when they arrive at the safe
// destination. We do this here, irrespective of whether there is any
// BDI reasoning involved, i.e., where the percept is not conditional
// on a BDI (drive) action.
// Such as for MATSim agents that leave as
// planned (according to their MATSim plan).
// FIXME: add Safe arrival percept for all agents (in a for loop)
for (Id agentID : model.getBDIAgentIDs()) {
MATSimAgent agent = model.getBDIAgent(agentID);
EvacResident bdiAgent = bdiModel.getBDICounterpart(agentID.toString());
Id newLinkId;
newLinkId = ((NetworkImpl) model.getScenario().getNetwork())
.getNearestLinkExactly(new CoordImpl(bdiAgent.endLocation[0], bdiAgent.endLocation[1])).getId();
agent.getPerceptHandler().registerBDIPerceptHandler(agent.getAgentID(),
MonitoredEventType.ArrivedAtDestination, newLinkId, new BDIPerceptHandler() {
@Override
public boolean handle(Id agentId, Id linkId, MonitoredEventType monitoredEvent,
MATSimModel model) {
MATSimAgent agent = model.getBDIAgent(agentId);
EvacResident bdiAgent = bdiModel.getBDICounterpart(agentId.toString());
Object[] params = { "Safe" , Long.toString(bdiAgent.getCurrentTime())};
agent.getPerceptContainer().put(MATSimPerceptList.ARRIVED, params);
return true; // unregister this handler
}
});
}
}
public void run(String file, String[] args) {
model.run(file, args);
}
/**
* Randomly assign dependent persons to be picked up. Uses
* Pk ({@link Config#getProportionWithKids()}) and
* Pr ({@link Config#getProportionWithRelatives()) probabilities to calculate
* normalised probabilities, and then allocate kids and/or relatives
* with those probabilities. If both input probabilities are non-zero,
* then all four allocations are possible (no kids or relatives, one or the
* other, both kids and relatives).
*
* Some examples:
*
* - Pk=0.0, Pr=0.0: results in always no kids or relatives
* - Pk=0.0, 0.0<Pr<1.0: results in always relatives
* - 0.0<Pk<1.0, Pr=0.0: results in always kids
* - 0.0<Pk<1.0, 0.0<Pr<1.0: results in all four combinations of kids and relatives
*
*
* @param bdiAgent
*/
private void assignDependentPersons(EvacResident bdiAgent) {
if( ScenarioTwoData.totPickups <= Config.getMaxPickUps() ) {
double[] pDependents = {Config.getProportionWithKids(), Config.getProportionWithRelatives()};
pDependents = Util.normalise(pDependents);
Random random = BushfireMain.getRandom();
if (random.nextDouble() < pDependents[0]) {
// Allocate dependent children
ScenarioTwoData.agentsWithKids++;
double[] sclCords = Config.getRandomSchoolCoords(bdiAgent.getId(),bdiAgent.startLocation);
if(sclCords != null) {
bdiAgent.kidsNeedPickUp = true;
bdiAgent.schoolLocation = sclCords;
bdiAgent.prepared_to_evac_flag = false;
ScenarioTwoData.totPickups++;
bdiAgent.log("has children at school coords "
+ sclCords[0] + "," +sclCords[1]);
}
else{
bdiAgent.log("has children but there are no schools nearby");
ScenarioTwoData.agentsWithKidsNoSchools++;
}
}
if (random.nextDouble() < pDependents[1]) {
// Allocate dependent adults
ScenarioTwoData.agentsWithRels++;
bdiAgent.relsNeedPickUp = true;
bdiAgent.prepared_to_evac_flag = false;
ScenarioTwoData.totPickups++;
bdiAgent.log("has relatives");
}
if (!bdiAgent.relsNeedPickUp && !bdiAgent.kidsNeedPickUp) {
bdiAgent.log("has neither children nor relatives");
}
}
}
}