/***
* Name: CityEscape
* Author: kevinchapuis
* Description: An evacuation model of a theoretical city with different alert communication strategies. Hazard is a very simple flood
* and we make the hypothesis that people die when they are in the flood (e.g. Tsunami). The flooding start x time after the beginning
* of the simulation. People escape when they perceive the flood or when they are alerted. There are 3 communication strategies: 
* - 'EVERYONE' = to alert everyone at the start of the simulation
* - 'STAGED' = to alert nb_people/nb_stages random people every (time_of_hazard-buffer_time)/nb_stages minutes
* - 'SPATIAL' = to alert nb_people/nb_stages closest people to exits every (time_of_hazard-buffer_time)/nb_stages minutes
* Where (time_of_hazard-buffer_time)/nb_stages correspond to the time between stages computed using the three parameters:
* - Time before hazard = time_of_hazard
* - Time alert buffer before hazard = buffer_time
* - Number of stages = nb_stages
* Tags: evacuation, traffic, hazard, gis, water
***/
model CityEscape

global {
	
	// Starting date of the simulation
	date starting_date <- #now;
	
	// Time step to represent very short term movement (for congestion)
	float step <- 10#sec;
	
	int nb_of_people;
	
	// To initialize perception distance of inhabitant
	float min_perception_distance <- 10.0;
	float max_perception_distance <- 30.0;
	
	// Represents the capacity of a road indicated as: number of inhabitant per #m of road
	float road_density;
	
	// Parameters of the strategy
	int time_after_last_stage;
	string the_alert_strategy;
	int nb_stages;
	
	// Parameters of hazard
	int time_before_hazard;
	float flood_front_speed;
	
	file road_file <- file("../includes/city_environment/road_environment.shp");
	file buildings <- file("../includes/city_environment/building_environment.shp");
	file evac_points <- file("../includes/city_environment/evacuation_environment.shp");
	file water_body <- file("../includes/city_environment/sea_environment.shp");
	geometry shape <- envelope(envelope(road_file)+envelope(water_body));
	
	// Graph road
	graph road_network;
	map road_weights;
	
	// Output the number of casualties
	int casualties;
	
	init {
				
		create road from:road_file;
		create building from:buildings;
		create evacuation_point from:evac_points;
		create hazard from: water_body;
		
		create inhabitant number:nb_of_people {
			location <- any_location_in(one_of(building));
			safety_point <- any(evacuation_point);
			perception_distance <- rnd(min_perception_distance, max_perception_distance);
		}
		
		create crisis_manager;
		
		road_network <- as_edge_graph(road);
		road_weights <- road as_map (each::each.shape.perimeter);
	
	}
	
	// Stop the simulation when everyone is either saved :) or dead :(
	reflex stop_simu when:inhabitant all_match (each.saved or each.drowned) {
		do pause;
	}
	
}

/*
 * Agent responsible of the communication strategy
 */
species crisis_manager {
	
	/*
	 * Time between each alert stage (#s)
	 */
	float alert_range;
	
	/*
	 * The number of people to alert every stage
	 */
	int nb_per_stage;
	
	/*
	 * Parameter to compute spatial area of each stage in the SPATIAL strategy
	 */
	geometry buffer;
	float distance_buffer;
	
	init {
		// For stage strategy
		int modulo_stage <- length(inhabitant) mod nb_stages; 
		nb_per_stage <- int(length(inhabitant) / nb_stages) + (modulo_stage = 0 ? 0 : 1);
		
		// For spatial strategy
		buffer <- geometry(evacuation_point collect (each.shape buffer 1#m));
		distance_buffer <- world.shape.height / nb_stages;
		
		alert_range <- (time_before_hazard#mn - time_after_last_stage#mn) / nb_stages;
	}
	
	/*
	 * If the crisis manager should send an alert
	 */
	reflex send_alert when: alert_conditional() {
		ask alert_target() { self.alerted <- true; }
	}
	
	/*
	 * The conditions to send an alert : return true at cycle = 0 and then every(alert_range)
	 * depending on the strategy used
	 */
	bool alert_conditional {
		if(the_alert_strategy = "STAGED" or the_alert_strategy = "SPATIAL"){
			return every(alert_range);
		} else {
			if(cycle = 0){
				return true;
			} else {
				return false;
			}
		}
	}
	
	/*
	 * Who to send the alert to: return a list of inhabitant according to the strategy used
	 */
	list alert_target {
		switch the_alert_strategy {
			match "STAGED" {
				return nb_per_stage among (inhabitant where (each.alerted = false));
			}
			match "SPATIAL" {
				buffer <- buffer buffer distance_buffer;
				return inhabitant overlapping buffer;
			}
			match "EVERYONE" {
				return list(inhabitant);
			}
			default {
				return [];
			}
		}
	}
	
}

/*
 * Represent the water body. When attribute triggered is turn to true, inhabitant
 * start to see water as a potential danger, and try to escape
 */
species hazard {
	
	// The date of the hazard
	date catastrophe_date;
	
	// Is it a tsunami ? (or just a little regular wave)
	bool triggered;
	
	init {
		catastrophe_date <- current_date + time_before_hazard#mn;
	}
	
	/*
	 * The shape the represent the water expend every cycle to mimic a (big) wave
	 */
	reflex expand when:catastrophe_date < current_date {
		if(not(triggered)) {triggered <- true;}
		shape <- shape buffer (flood_front_speed#m/#mn * step) intersection world;
	}
	
	aspect default {
		draw shape color:#blue;
	}
	
}

/*
 * Represent the inhabitant of the area. They move at foot. They can pereive the hazard or be alerted
 * and then will try to reach the one randomly choose exit point
 */
species inhabitant skills:[moving] {
	
	// The state of the agent
	bool alerted <- false;
	bool drowned <- false;
	bool saved <- false;
	
	// How far (#m) they can perceive
	float perception_distance;
	
	// The exit point they choose to reach
	evacuation_point safety_point;
	// How fast inhabitant can run
	float speed <- 10#km/#h;
	
	/*
	 * Am I drowning ?
	 */
	reflex drown when:not(drowned or saved) {
		if(first(hazard) covers self){
			drowned <- true;
			casualties <- casualties + 1; 
		}
	}
	
	/*
	 * Is there any danger around ?
	 */
	reflex perceive when: not(alerted or drowned) and first(hazard).triggered {
		if self.location distance_to first(hazard).shape < perception_distance {
			alerted <- true;
		}
	}
	
	/*
	 * When alerted people will try to go to the choosen exit point
	 */
	reflex evacuate when:alerted and not(drowned or saved) {
		do goto target:safety_point on: road_network move_weights:road_weights;
		if(current_edge != nil){
			road the_current_road <- road(current_edge);  
			the_current_road.users <- the_current_road.users + 1;
		}
	}
	
	/*
	 * Am I safe ?
	 */
	reflex escape when: not(saved) and location distance_to safety_point < 2#m{
		saved <- true;
		alerted <- false;
	}
	
	aspect default {
		draw drowned ? cross(4,0.2) : circle(1#m) color:drowned ? #black : (alerted ? #red : #green);
	}
	
}

/*
 * The point of evacuation
 */
species evacuation_point {
	
	int count_exit <- 0 update: length((inhabitant where each.saved) at_distance 2#m);
		
	aspect default {
		draw circle(1#m+49#m*count_exit/nb_of_people) color:#green;
	}
	
}

/*
 * The roads inhabitant will use to evacuate. Roads compute the congestion of road segment
 * accordin to the Underwood function.
 */
species road {
	
	// Number of user on the road section
	int users;
	// The capacity of the road section
	int capacity <- int(shape.perimeter*road_density);
	// The Underwood coefficient of congestion
	float speed_coeff <- 1.0;
	
	// Update weights on road to compute shortest path and impact inhabitant movement
	reflex update_weights {
		speed_coeff <- max(0.05,exp(-users/capacity));
		road_weights[self] <- shape.perimeter / speed_coeff;
		users <- 0;
	}
	
	// Cut the road when flooded so people cannot use it anymore
	reflex flood_road {
		if(hazard first_with (each covers self) != nil){
			road_network >- self; 
			do die;
		}
	}
	
	aspect default{
		draw shape width: 4#m-(3*speed_coeff)#m color:rgb(55+200*users/capacity,0,0);
	}	
	
}

/*
 * People are located in building at the start of the simulation
 */
species building {
	aspect default {
		draw shape color: #gray border: #black;
	}
}

experiment "Run" {
	float minimum_cycle_duration <- 0.1;
		
	parameter "Alert Strategy" var:the_alert_strategy init:"STAGED" among:["NONE","STAGED","SPATIAL","EVERYONE"] category:"Alert";
	parameter "Number of stages" var:nb_stages init:6 category:"Alert";
	parameter "Time alert buffer before hazard" var:time_after_last_stage init:5 unit:#mn category:"Alert";
	
	parameter "Road density index" var:road_density init:6.0 min:0.1 max:10.0 category:"Congestion";
	
	parameter "Speed of the flood front" var:flood_front_speed init:5.0 min:1.0 max:30.0 unit:#m/#mn category:"Hazard";
	parameter "Time before hazard" var:time_before_hazard init:5 min:0 max:10 unit:#mn category:"Hazard";
	
	parameter "Number of people" var:nb_of_people init:500 min:100 max:20000 category:"Initialization";
	
	output {
		display my_display type:opengl{ 
			species road;
			species hazard;
			species evacuation_point;
			species building;
			species inhabitant;
		}
		monitor "Number of casualties" value:casualties;
	}	
	
}