diff --git a/.gitignore b/.gitignore index 8a57b516d785835db0f24624fd3dd5a85a522cc1..5671e56980439c206317b89fab45256139a07999 100644 --- a/.gitignore +++ b/.gitignore @@ -23,8 +23,8 @@ target #output files -*.csv -*.png +src/main/resources/*.csv +src/main/resources/*.png # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* diff --git a/ODD.md b/ODD.md new file mode 100644 index 0000000000000000000000000000000000000000..ce1df435d4a5197809bd7d3858ebf5d647b4de0e --- /dev/null +++ b/ODD.md @@ -0,0 +1,134 @@ +# ODD description + +## Purpose and patterns + +This SMA aims at simulating the spreading of a disease, following the SEIRS model. +The main goal is focused on reproductibility and aim to display what is essential to design and share so our work and results can be reproducted and re-used by others persons. + +## Entities, states variables and scales + +### Scales + +The simulation is executed on a squared environment mesured with an abstract unit size. Size is configurable and there is no minimum or maximum. +In the (optionnal) graphical interpreation, each unit is represented by one pixel. +The simulation step is also an abstract unit and does not represent any real life unit. +The environment can be parametrable to have bounds, wich means that they can't go above the edges. If deactivated, agents wrap to the other side when they go pass a border. + +### Entities + +There is only one kind of entity in the environment, the **SEIRSAgent**. +Agents move on a random pattern. They will ask the environment wich they are allowed to go (up, down, left or right) and will choose one in random. +There is no collision, multiple agents can be on the same position. + +The infestion spead at the individual scale. If an infected agent overlap it's position with another agent, he can spread his disease on him. + +Each cycle exposed agents have a chance to become infected. +Each cycle infected agents have a chance to become recovered. +Each cycle recovered agents have a chance to become susceptible. + +## Design concepts + +### Basic principles + +As far as the epidemiological dynamics is concerned, we rely on much scientific evidence that +the disease could be represented by a SEIRS model with an infectious state that can be +presymptomatic or symptomatic, with a certain degree of survivability of the virus +in the environment and the possibility of people being infected by it. + +The individual agents’ behavior is very simple and roam aimlessly in random in the environment. + +### Emergence + +The main emergent (or at least complex to predict) results are the evolution of the number of +infected cases given the different parameters. +In most cases, the 4 differents populations stabilize but when given weak enough infection rate and long enough loosing immunity rate, you can obtain waves. + +### Objectives + +### Interaction + +Agent in the *susceptible* state can be infected if they are in contact with an agent in the *infected* state. + +Each susceptible agent in this situation will roll a pseudo-random number between 0 and 1 and if it's bigger than the given number in the parameters, they will switch to the exposed state. + +### Observation + +A graphical interface can be used if asked in the parameters file. +It will show a window that display the simulation at each step and the total of each population. +Note that it can slow down the speed of the simulation and if the environment is too big for the screen resolution, it won't fit properly. + +## Initialization + +In order to keep the model as generic as possible, many parameters and initial values are stored in independent external yaml file : + +```graphicalMode : Boolean``` + +When true, will display an IHM when running a simulation + +```incubationRate : Float [0..1]``` + +Chances each cycle that an EXPOSED agent become INFECTED. + +```infectionRate : Float [0..1]``` + +Chances each cycle that an SUCEPTIBLE agent become EXPOSED if an INFECTED agent is nearby. + +```looseImmunityRate : Float [0..1]``` + +Chances each cycle that an RECOVERED agent become SUCEPTIBLE again. + +```recoveryRate : Float [0..1]``` + +Chances each cycle that an INFECTED agent become RECOVERED. + +```nbOfCycles : Integer``` + +Determines for how much cycle the simulation will run. + +Setting it to -1 or lower will run the simulation until it's manually stopped. + +```nbOfPatientZero : Integer``` + +Determines how much agents will be already INFECTED when the simulation starts. + +```population : Integer``` + +How much agents will be present in the simuation. + +```recordExperiment : Boolean``` + +If true, the order in wich the agents wake up in the simuation will be recorded. + +```playRecord : Boolean``` + +If true, will use a scheduler that will wake up agents accordingly to the record. + +```seed : Integer``` + +Seed all the random elements in the simulation. + +See the [How the random works](#how-the-random-works) section for more details. + +```size : Integer``` + +Set the size in pixels of the square world. + +```wrappingWorld : Boolean``` + +If true, agents will be allowed to wrap to the other side when close to a border. + +```synchronousMode : Boolean``` + +If true, will use a synchronous, seeded scheduler. It is slower but more predictable. + +If false, will use a asynchronous scheduler. Faster but results might varies between multiple executions, **even on the same seed**. + +```timeBetweenCycles : Boolean``` + +Delay between each new cycles, in milliseconds. 0 or lower will try make the simulation run as fast as possible. + +```infectionStacks : Boolean``` + +If true, a SUCEPTIBLE agent will roll for infection for **every INFECTED agent nearby**. + +If false, a SUCEPTIBLE agent with INFECTED agents nearby will roll **only once, no matter how much infected agents are close.** diff --git a/__pycache__/outputToGraph.cpython-310.pyc b/__pycache__/outputToGraph.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d066e5f82c18e3857c0385111bae5138eeef39c Binary files /dev/null and b/__pycache__/outputToGraph.cpython-310.pyc differ diff --git a/multipleRuns.py b/multipleRuns.py new file mode 100644 index 0000000000000000000000000000000000000000..b324937fca8e6fe362618a7118ec83e5e1fa7d5b --- /dev/null +++ b/multipleRuns.py @@ -0,0 +1,16 @@ +from outputToGraph import run_java_jar, make_diagram, JAR_LOCATION,OUTPUT_FILE_LOCATION +import sys +import shutil + +def save_csv(i) : + old_location = OUTPUT_FILE_LOCATION + new_location = f"src/main/resources/output{i}.csv" + shutil.move(old_location,new_location) + +if ((len(sys.argv) > 1)): + for i in range (int(sys.argv[1])) : + run_java_jar(JAR_LOCATION) + make_diagram(OUTPUT_FILE_LOCATION) + save_csv(i) +else : + print("Usage : python3 multipleRuns.py <nb of runs>") \ No newline at end of file diff --git a/outputToGraph.py b/outputToGraph.py index a39d9c8e1612ba3740a4ecb1014f52537b6a3b5d..7d8c4366d7e28890d97683f2bb22f21b137d85c2 100644 --- a/outputToGraph.py +++ b/outputToGraph.py @@ -53,7 +53,7 @@ def make_diagram(filename): def run_java_jar(filename): import subprocess - subprocess.call(['java', '-jar', filename]) + subprocess.call(['java', '-jar',"--enable-preview", filename]) def copy_to_output_folder(filename): @@ -82,5 +82,6 @@ def edit_yaml(key, value): # copyToOutputFolder(f"output{i}.csv") # makeDiagram(f"{OUTPUT_FOLDER}/output{i}.csv") -run_java_jar(JAR_LOCATION) -make_diagram(OUTPUT_FILE_LOCATION) +if __name__ == "__main__": + run_java_jar(JAR_LOCATION) + make_diagram(OUTPUT_FILE_LOCATION) diff --git a/pom.xml b/pom.xml index 28e3de1e62dd55223da8bee198c9132cc761bdfe..b5be8144ba3a05072c3ab6c4f28851ceb31fbf36 100644 --- a/pom.xml +++ b/pom.xml @@ -31,14 +31,14 @@ <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> - <version>2.13.2.2</version> + <version>2.13.3</version> </dependency> <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml --> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> - <version>2.13.2</version> + <version>2.13.3</version> </dependency> </dependencies> diff --git a/src/main/java/agents/Agent.java b/src/main/java/agents/Agent.java index c4b9c12ddbcf33182028b28f270a63ec16e3b278..7117a8fcb8694728e1cee8ff79f1cb38e6c30f64 100644 --- a/src/main/java/agents/Agent.java +++ b/src/main/java/agents/Agent.java @@ -1,7 +1,5 @@ package agents; -import behaviors.Cyclic; - public sealed interface Agent permits CyclicAgent, ThreePhasedAgent{ diff --git a/src/main/java/agents/RandomWalkingAgent3P.java b/src/main/java/agents/RandomWalkingAgent3P.java index 7edbcae221f05cc1e16523883015f7263383d016..720f46613108b067a41c9cdb717653dfc6cfeb39 100644 --- a/src/main/java/agents/RandomWalkingAgent3P.java +++ b/src/main/java/agents/RandomWalkingAgent3P.java @@ -8,7 +8,7 @@ import java.awt.*; public class RandomWalkingAgent3P extends RandomWakingAgent implements ThreePhasedSEIRSAgent { - private String id; + private final String id; public RandomWalkingAgent3P(Point position, long seed, SEIRSEnvironment environment) { super(position, seed, environment); diff --git a/src/main/java/agents/seirs/CyclicSEIRSAgent.java b/src/main/java/agents/seirs/CyclicSEIRSAgent.java index 135b9f805acd4d660bff23a909a819d5d7e9198c..09fe34a2537c0bfd1383b02ad500336d643ab3ac 100644 --- a/src/main/java/agents/seirs/CyclicSEIRSAgent.java +++ b/src/main/java/agents/seirs/CyclicSEIRSAgent.java @@ -1,9 +1,6 @@ package agents.seirs; import agents.CyclicAgent; -import agents.states.SEIRSState; -import behaviors.Infectious; -import behaviors.Positionable2D; public interface CyclicSEIRSAgent extends CyclicAgent, SEIRSAgent { diff --git a/src/main/java/agents/seirs/FairInfectionRWAgent.java b/src/main/java/agents/seirs/FairInfectionRWAgent.java index 2aebf28e8763a562f185ed36e88a1fa2e40236c2..10a8d1cf0f07ab074b435b4ccb1384cc7bcd0e7c 100644 --- a/src/main/java/agents/seirs/FairInfectionRWAgent.java +++ b/src/main/java/agents/seirs/FairInfectionRWAgent.java @@ -45,9 +45,13 @@ public abstract class FairInfectionRWAgent extends Randomized implements SEIRSAg @Override public boolean isExposed() { + return rollExposition(environment, position, r.nextInt(10000)); + } + + static boolean rollExposition(SEIRSEnvironment environment, Point position, int i2) { boolean isExposed = false; - for (int i = 0 ; i<environment.getInfectedNeighbors(position).size() ; i++) { - int roll = r.nextInt(10000)+1; + for (int i = 0; i< environment.getInfectedNeighbors(position).size() ; i++) { + int roll = i2 +1; if (roll <= YamlReader.getParams().infectionRate()*10000) { isExposed = true; } diff --git a/src/main/java/agents/seirs/FairInfectionRWAgent3P.java b/src/main/java/agents/seirs/FairInfectionRWAgent3P.java index e69c6fd026325cdf7f6a1afc486ee99a3997d5d3..a2865f546b428a2451b24c41014a9acd04ccda56 100644 --- a/src/main/java/agents/seirs/FairInfectionRWAgent3P.java +++ b/src/main/java/agents/seirs/FairInfectionRWAgent3P.java @@ -1,13 +1,12 @@ package agents.seirs; -import agents.ThreePhasedAgent; import environment.SEIRSEnvironment; import java.awt.*; public class FairInfectionRWAgent3P extends FairInfectionRWAgent implements ThreePhasedSEIRSAgent { - private String id; + private final String id; public FairInfectionRWAgent3P(Point position, long seed, SEIRSEnvironment environment) { super(position, seed, environment); diff --git a/src/main/java/agents/seirs/FairInfectionRWAgentCyclic.java b/src/main/java/agents/seirs/FairInfectionRWAgentCyclic.java index 707f1d7505b3e92758c7b75e166b42ef76e25538..195d1cbedd26087e22bfc70bc92ca2177653e9ea 100644 --- a/src/main/java/agents/seirs/FairInfectionRWAgentCyclic.java +++ b/src/main/java/agents/seirs/FairInfectionRWAgentCyclic.java @@ -1,13 +1,12 @@ package agents.seirs; import environment.SEIRSEnvironment; -import utils.YamlReader; import java.awt.*; public class FairInfectionRWAgentCyclic extends FairInfectionRWAgent implements CyclicSEIRSAgent { - private String id; + private final String id; public FairInfectionRWAgentCyclic(Point position, long seed, SEIRSEnvironment environment) { super(position, seed, environment); this.id = String.valueOf(seed); diff --git a/src/main/java/agents/seirs/RandomWakingAgent.java b/src/main/java/agents/seirs/RandomWakingAgent.java index bccdf5d4a69f0a291829689afeaaf1ad9a9403ef..cef450d6fac8fcb1206f0f171ee72bc594e1c77e 100644 --- a/src/main/java/agents/seirs/RandomWakingAgent.java +++ b/src/main/java/agents/seirs/RandomWakingAgent.java @@ -9,6 +9,8 @@ import utils.YamlReader; import java.awt.*; import java.util.List; +import static agents.seirs.FairInfectionRWAgent.rollExposition; + public abstract class RandomWakingAgent extends Randomized implements SEIRSAgent { protected Point position; @@ -46,14 +48,7 @@ public abstract class RandomWakingAgent extends Randomized implements SEIRSAgent @Override public boolean isExposed() { - boolean isExposed = false; - for (int i = 0 ; i<environment.getInfectedNeighbors(position).size() ; i++) { - int roll = r.nextInt(10000)+1; - if (roll <= YamlReader.getParams().infectionRate()*10000) { - isExposed = true; - } - } - return isExposed; + return rollExposition(environment, position, r.nextInt(10000)); } @Override diff --git a/src/main/java/agents/seirs/RandomWalkingAgentCyclic.java b/src/main/java/agents/seirs/RandomWalkingAgentCyclic.java index 6c1874cb898ea5f04b76ab3edbee1d19339d9b89..42cdb7fea89c3255402a85f31daf7e5c7698100c 100644 --- a/src/main/java/agents/seirs/RandomWalkingAgentCyclic.java +++ b/src/main/java/agents/seirs/RandomWalkingAgentCyclic.java @@ -1,17 +1,12 @@ package agents.seirs; -import agents.states.SEIRSState; -import agents.states.SuceptibleSEIRSState; -import behaviors.Randomized; import environment.SEIRSEnvironment; -import utils.YamlReader; import java.awt.Point; -import java.util.List; public class RandomWalkingAgentCyclic extends RandomWakingAgent implements CyclicSEIRSAgent { - private String id; + private final String id; public RandomWalkingAgentCyclic(Point position, long seed, SEIRSEnvironment environment) { super(position, seed, environment); diff --git a/src/main/java/agents/states/ExposedSEIRSState.java b/src/main/java/agents/states/ExposedSEIRSState.java index b24651c0247761252d9589d5ebbc21ce66aad8f2..848e943c4deca9b948e29d559e24f260e1909fe2 100644 --- a/src/main/java/agents/states/ExposedSEIRSState.java +++ b/src/main/java/agents/states/ExposedSEIRSState.java @@ -1,6 +1,5 @@ package agents.states; -import agents.seirs.CyclicSEIRSAgent; import agents.seirs.SEIRSAgent; public class ExposedSEIRSState extends SEIRSState { diff --git a/src/main/java/agents/states/InfectedSEIRSState.java b/src/main/java/agents/states/InfectedSEIRSState.java index d8c18f291b233e4aa8c109cf15ec1341fc9234d7..b7ef749348924fd9f48699aee8dcffe770a093b7 100644 --- a/src/main/java/agents/states/InfectedSEIRSState.java +++ b/src/main/java/agents/states/InfectedSEIRSState.java @@ -1,6 +1,5 @@ package agents.states; -import agents.seirs.CyclicSEIRSAgent; import agents.seirs.SEIRSAgent; public class InfectedSEIRSState extends SEIRSState { diff --git a/src/main/java/agents/states/RecoveredSEIRSState.java b/src/main/java/agents/states/RecoveredSEIRSState.java index 0a05430d3368b05fc059ac3cfa3b70597a7fcbbc..c434926d51858d677417e076c7967f6afb6a6413 100644 --- a/src/main/java/agents/states/RecoveredSEIRSState.java +++ b/src/main/java/agents/states/RecoveredSEIRSState.java @@ -1,6 +1,5 @@ package agents.states; -import agents.seirs.CyclicSEIRSAgent; import agents.seirs.SEIRSAgent; public class RecoveredSEIRSState extends SEIRSState { diff --git a/src/main/java/agents/states/SEIRSState.java b/src/main/java/agents/states/SEIRSState.java index 966453fa1ab5a0aa596474a32a48cb4ac560e163..34bde7ddd6b1a6ac6deb6a4b2b93c951200b87fa 100644 --- a/src/main/java/agents/states/SEIRSState.java +++ b/src/main/java/agents/states/SEIRSState.java @@ -1,6 +1,5 @@ package agents.states; -import agents.seirs.CyclicSEIRSAgent; import agents.seirs.SEIRSAgent; public abstract class SEIRSState { diff --git a/src/main/java/agents/states/SuceptibleSEIRSState.java b/src/main/java/agents/states/SuceptibleSEIRSState.java index 87c5335c6ef7ce42db23d6c454eb79de17119887..e28076e7301389e4626f68b977e3572bbbe0e9af 100644 --- a/src/main/java/agents/states/SuceptibleSEIRSState.java +++ b/src/main/java/agents/states/SuceptibleSEIRSState.java @@ -1,6 +1,5 @@ package agents.states; -import agents.seirs.CyclicSEIRSAgent; import agents.seirs.SEIRSAgent; public class SuceptibleSEIRSState extends SEIRSState { @@ -20,4 +19,10 @@ public class SuceptibleSEIRSState extends SEIRSState { public String toString() { return SUCEPTIBLE; } + + public static void main(String[] args) { + float i = 4.12f; + int i2 = (int) i; + System.out.println((float)i2); + } } diff --git a/src/main/java/environment/ChunkedSEIRSEnvironment.java b/src/main/java/environment/ChunkedSEIRSEnvironment.java index d489eac3482fc4e1bd0d438eee72d14a0aa8e983..5e3e498793019c75c1afc3c20c7bb887d9ceab5b 100644 --- a/src/main/java/environment/ChunkedSEIRSEnvironment.java +++ b/src/main/java/environment/ChunkedSEIRSEnvironment.java @@ -1,7 +1,6 @@ package environment; import agents.Agent; -import agents.seirs.CyclicSEIRSAgent; import agents.seirs.SEIRSAgent; import agents.states.InfectedSEIRSState; import agents.states.SEIRSState; @@ -50,8 +49,8 @@ public class ChunkedSEIRSEnvironment implements SEIRSEnvironment { private Point getRelativePoint(int relativeTo, Point p) { return switch (relativeTo) { - case LEFT -> new Point(p.x-1,p.y); - case RIGHT -> new Point(p.x+1,p.y); + //case LEFT -> new Point(p.x-1,p.y); + case LEFT, RIGHT -> new Point(p.x+1,p.y); case UP -> new Point(p.x,p.y-1); case DOWN -> new Point(p.x,p.y+1); case CENTER -> p; diff --git a/src/main/java/environment/SEIRSEnvironment.java b/src/main/java/environment/SEIRSEnvironment.java index 7e2a804885d784ac46aed87fea5b3d118c12639e..445e0d142d07350cd32f435ec0fc0a526c042fce 100644 --- a/src/main/java/environment/SEIRSEnvironment.java +++ b/src/main/java/environment/SEIRSEnvironment.java @@ -1,6 +1,5 @@ package environment; -import agents.seirs.CyclicSEIRSAgent; import agents.seirs.SEIRSAgent; import java.awt.*; diff --git a/src/main/java/environment/WrappingChunkedSEIRSEnvironment.java b/src/main/java/environment/WrappingChunkedSEIRSEnvironment.java index 024638e8d46adcaf775118cb6b8005abb5ea79f4..84f15d0e9583a63586ce981c4bd9e025d794b790 100644 --- a/src/main/java/environment/WrappingChunkedSEIRSEnvironment.java +++ b/src/main/java/environment/WrappingChunkedSEIRSEnvironment.java @@ -1,6 +1,5 @@ package environment; -import agents.seirs.CyclicSEIRSAgent; import agents.seirs.SEIRSAgent; import behaviors.Positionable2D; diff --git a/src/main/java/sma/SEIRS_SMA.java b/src/main/java/sma/SEIRS_SMA.java index 50afb577fa495a13b51d765b75e9430401e5ebf7..5894c1174ab699ed0e1f228a6f870531282c3ed9 100644 --- a/src/main/java/sma/SEIRS_SMA.java +++ b/src/main/java/sma/SEIRS_SMA.java @@ -178,6 +178,7 @@ public class SEIRS_SMA extends Randomized implements SMA{ @Override public void run() { + calculateDensity(); Instant startTime = Instant.now(); System.out.println("Starting simulation at : "+ Date.from(startTime)); if (parameters.nbOfCycles() <0) { @@ -203,6 +204,12 @@ public class SEIRS_SMA extends Randomized implements SMA{ } } + private void calculateDensity() { + int pixels = parameters.size()*parameters.size(); + int agents = parameters.population(); + System.out.println("Population density is : " + (float)agents/pixels + " agents per pixel."); + } + public static void main(String[] args) { SMA sma = new SEIRS_SMA(YamlReader.getParams()); sma.run(); diff --git a/src/main/resources/output.png b/src/main/resources/output.png deleted file mode 100644 index 1d20c171776fa6ac52b3a67ff3a37291c362c6b2..0000000000000000000000000000000000000000 Binary files a/src/main/resources/output.png and /dev/null differ diff --git a/src/main/resources/parameters.yaml b/src/main/resources/parameters.yaml index 7e7cfa90d8f5ea7146d02d3af7b0cf1ec5bb9654..5d744438cd440a1aea3c1a6816509b579276e685 100644 --- a/src/main/resources/parameters.yaml +++ b/src/main/resources/parameters.yaml @@ -1,17 +1,17 @@ graphicalMode: true -incubationRate: 0.3 -infectionRate: 0.8 -looseImmunityRate: 0.008 -recoveryRate: 0.14 -nbOfCycles: 2000 +incubationRate: 0.33 +infectionRate: 0.5 +looseImmunityRate: 0.0027 +recoveryRate: 0.10 +nbOfCycles: 720 nbOfPatientZero: 1 -population: 3000 +population: 1000 recordExperiment: false playRecord: false seed: 120 -size: 1000 +size: 400 wrappingWorld : true synchronousMode: false timeBetweenCycles: 0 -infectionStacks : false -threePhased : true \ No newline at end of file +infectionStacks : true +threePhased : false \ No newline at end of file