Feel free to join the next Helmholtz Hacky Hour #26 on Wednesday, April 21, 2021 from 2PM to 3PM!

Commit 0466b6d4 authored by Martin Lange's avatar Martin Lange

text for submodels, references

parent c505787f
Pipeline #12096 passed with stage
in 12 seconds
# ECS Tutorial with Java and Artemis-odb
This projects demonstrates the use of an Entity-Component-System for the implementation of an individual-based model (IBM). We use Java and the ECS library [Artemis-odb](https://github.com/junkdog/artemis-odb) to build a grassing model.
This projects demonstrates the use of an Entity-Component-System for the implementation of an individual-based model (IBM). We use Java and the ECS library [Artemis-odb](https://github.com/junkdog/artemis-odb) to build a simple grassing model.
The model description is structured following the ODD protocol (Grimm et al. 2006, 2010) to demonstrate the good fit between ECS and ODD.
......@@ -16,7 +16,7 @@ The purpose of this model is to demonstrate the use of Entity-Component-Systems
## Entities, state variables and scales
In an ECS, all entities are generic, but characterized by the components they possess. Comonents contain an entity's state variables.
In an ECS, all entities are generic, but characterized by the components they possess. Components contain an entity's state variables.
In this grassing model, the only entities are grassers. All grassers possess the components `Position`, `Heading` and `Energy`. Behaviour of grassers is governed by the components `IsGrassing` or `IsSearching`, which each grasser possess one of.
......@@ -210,6 +210,10 @@ Submodels are described in their order of execution.
### Grass growth
Grass growth is logistic, with a capacity of 1.0.
The grassing system does not use any entities, but requires access to the `Grass` grid, which is "injected" by the ECS through the line `@Wire Grass grass;`.
```java
/// file:src/main/java/grassing/sys/LogisticGrassGrowth.java
package grassing.sys;
......@@ -220,8 +224,7 @@ import grassing.res.Grass;
public class LogisticGrassGrowth extends BaseSystem {
@Wire
Grass grass;
@Wire Grass grass;
private final float growthRate;
private final float capacity;
......@@ -243,6 +246,10 @@ public class LogisticGrassGrowth extends BaseSystem {
### Grasser metabolism
The grasser metabolism works on all entities that have the component `Energy`.
In each iteration, grassers lose `consumption` energy. When it's energy drops to zero, a grasser dies.
```java
/// file:src/main/java/grassing/sys/Metabolism.java
package grassing.sys;
......@@ -276,6 +283,12 @@ public class Metabolism extends IteratingSystem {
### Grasser reproduction
The grasser reproduction works on all entities that have the components `Position` and `Energy`.
In each iteration, each grasser reproduces with a fixed probability.
When reproducing, energy is split in half between the grasser and it's offspring. The offspring starts with the grassing behaviour.
```java
/// file:src/main/java/grassing/sys/Reproduction.java
package grassing.sys;
......@@ -292,6 +305,7 @@ public class Reproduction extends IteratingSystem {
ComponentMapper<Position> mPosition;
ComponentMapper<Energy> mEnergy;
@Wire Randomness random;
private final float reproductionProb;
......@@ -319,6 +333,12 @@ public class Reproduction extends IteratingSystem {
### Grassing
The grassing behaviour system works on all entities that have the components `Position`, `Energy` and `IsGrassing`.
If the grasser's energy is 1.0 or above, it does nothing. Otherwise, the grasser decides if there is enough grass in it's landscape cell. If this is the case, the grasser consumes grass, otherwise it switches to the searching behaviour.
The behaviour is switched by removing component `IsGrassing` and adding a new component `IsSearching`.
```java
/// file:src/main/java/grassing/sys/GrassingBehaviour.java
package grassing.sys;
......@@ -337,6 +357,7 @@ public class GrassingBehaviour extends IteratingSystem {
ComponentMapper<Energy> mEnergy;
ComponentMapper<IsGrassing> mGrassing;
ComponentMapper<IsSearching> mSearching;
@Wire Grass grass;
private final float minGrass;
......@@ -368,6 +389,12 @@ public class GrassingBehaviour extends IteratingSystem {
### Searching
The grassing behaviour system works on all entities that have the components `Position` and `IsSearching`.
The grasser decides if there is enough grass in it's landscape cell. If this is the case, the grasser switches to the searching behaviour. Otherwise, it continues searching ba a random walk.
The behaviour is switched by removing component `IsSearching` and adding a new component `IsGrassing`.
```java
/// file:src/main/java/grassing/sys/SearchBehaviour.java
package grassing.sys;
......@@ -388,8 +415,8 @@ public class SearchBehaviour extends IteratingSystem {
ComponentMapper<Heading> mHeading;
ComponentMapper<IsGrassing> mGrassing;
ComponentMapper<IsSearching> mSearching;
@Wire
Grass grass;
@Wire Grass grass;
@Wire Randomness random;
private final float minGrass;
......@@ -453,117 +480,20 @@ final static float RANDOM_WALK_SPEED = 0.25f;
final static float RANDOM_WALK_ANGLE = MathUtil.deg2rad(30);
```
## Appendix
### Utilities
## References
```java
/// file:src/main/java/grassing/util/Grid.java
package grassing.util;
Grimm V, Berger U, Bastiansen F, Eliassen S, Ginot V, Giske J, Goss-Custard J, Grand T, Heinz S, Huse G, Huth A, Jepsen JU, J酶rgensen C, Mooij WM, M眉ller B, Pe鈥檈r G, Piou C, Railsback SF, Robbins AM, Robbins MM, Rossmanith E, R眉ger N, Strand E, Souissi S, Stillman RA, Vab酶 R, Visser U, DeAngelis DL. 2006. **A standard protocol for describing individual-based and agent-based models**. *Ecological Modelling* 198:115-126.
public abstract class Grid {
Grimm V, Berger U, DeAngelis DL, Polhill G, Giske J, Railsback SF. 2010. **The ODD protocol: a review and first update**. *Ecological Modelling* 221: 2760-2768.
final public int width;
final public int height;
final public int layers;
final public int length;
protected final Object data;
protected Grid(Object data, int width, int height, int layers) {
this.data = data;
this.width = width;
this.height = height;
this.layers = layers;
this.length = width * height * layers;
}
protected Grid(Object data, int width, int height) {
this(data, width, height, 1);
}
public final int getIndex(int x, int y) {
return (y * width + x) * layers;
}
public final int getIndex(int x, int y, int l) {
return (y * width + x) * layers + l;
}
public final boolean contains(int x, int y) {
return x >= 0 && y >= 0 && x < width && y < height;
}
public final boolean contains(int x, int y, int l) {
return x >= 0 && y >= 0 && l >= 0 && x < width && y < height && l < layers;
}
public static final class Float extends Grid {
public Float(int width, int height, int layers) {
super((Object) (new float[width * height * layers]), width, height, layers);
}
public Float(int width, int height) {
this(width, height, 1);
}
private float[] getData() {
return (float[]) data;
}
public final float get(int idx) {
return getData()[idx];
}
public final float get(int x, int y) {
return getData()[getIndex(x, y)];
}
public final float get(int x, int y, int l) {
return getData()[getIndex(x, y, l)];
}
public final void set(int idx, float value) {
getData()[idx] = value;
}
public final void set(int x, int y, float value) {
getData()[getIndex(x, y)] = value;
}
public final void set(int x, int y, int l, float value) {
getData()[getIndex(x, y, l)] = value;
}
}
}
```
```java
/// file:src/main/java/grassing/util/MathUtil.java
package grassing.util;
public abstract class MathUtil {
public static Point heading(float angle) {
return new Point((float) Math.cos(angle), (float) Math.sin(angle));
}
public static float deg2rad(float deg) {
return (float) (deg * Math.PI / 180f);
}
}
```
```java
/// file:src/main/java/grassing/util/Point.java
package grassing.util;
public class Point {
public float x;
public float y;
## Appendix
public Point(float x, float y) {
this.x = x;
this.y = y;
}
}
```
This section describes code that is not vital for understanding the model or the ECS concept.
### Graphics
The graphics system draws the amount of grass per cell by shades of green. Further, it draws grassers as triangles pointing in the direction of their heading. Grassers are coloured according their current behaviour: white for grassing, yellow for searching.
```java
/// file:src/main/java/grassing/sys/Graphics.java
package grassing.sys;
......@@ -584,11 +514,12 @@ import java.awt.*;
public class Graphics extends BaseSystem {
private EntitySubscription grasserSubs;
protected ComponentMapper<Position> mPosition;
protected ComponentMapper<Heading> mHeading;
protected ComponentMapper<IsGrassing> mGrassing;
protected ComponentMapper<IsSearching> mSearching;
private EntitySubscription grasserSubs;
@Wire Grass grass;
......@@ -671,8 +602,94 @@ public class Graphics extends BaseSystem {
}
```
### Utilities
```java
/// file:src/main/java/grassing/util/Grid.java
package grassing.util;
public abstract class Grid {
final public int width;
final public int height;
final public int length;
protected final Object data;
protected Grid(Object data, int width, int height) {
this.data = data;
this.width = width;
this.height = height;
this.length = width * height;
}
public final int getIndex(int x, int y) {
return (y * width + x);
}
public final boolean contains(int x, int y) {
return x >= 0 && y >= 0 && x < width && y < height;
}
public static final class Float extends Grid {
public Float(int width, int height) {
super((Object) (new float[width * height]), width, height);
}
private float[] getData() {
return (float[]) data;
}
public final float get(int idx) {
return getData()[idx];
}
public final float get(int x, int y) {
return getData()[getIndex(x, y)];
}
public final void set(int idx, float value) {
getData()[idx] = value;
}
public final void set(int x, int y, float value) {
getData()[getIndex(x, y)] = value;
}
}
}
```
```java
/// file:src/main/java/grassing/util/MathUtil.java
package grassing.util;
public abstract class MathUtil {
public static Point heading(float angle) {
return new Point((float) Math.cos(angle), (float) Math.sin(angle));
}
public static float deg2rad(float deg) {
return (float) (deg * Math.PI / 180f);
}
}
```
```java
/// file:src/main/java/grassing/util/Point.java
package grassing.util;
public class Point {
public float x;
public float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
}
```
### Dependencies with Gradle
Gradle is used as build tool and for dependency management.
```groovy
/// file:build.gradle
apply plugin: 'application'
......@@ -700,6 +717,8 @@ rootProject.name = 'Grassing'
### Git ignore file
Regarding VCS/Git, we ignore Gradle files and build output.
```
/// file:.gitignore
.gradle
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment