Step 3: Simulation classes
Simulation-time Assets
Create a new tutorial.model
package.
This package will store the Model
class (will be created soon)
and several model-related classes used during simulation.
Create the following new classes in the tutorial.model
package:
package tutorial.model;
public abstract class Asset {
private final String id;
private final String name;
protected Asset(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
package tutorial.model;
public class Warehouse extends Asset {
public Warehouse(String id, String name) {
super(id, name);
}
}
package tutorial.model;
public class Store extends Asset {
public Store(String id, String name) {
super(id, name);
}
}
Currently the source code of these classes is the same as for their counterparts in the tutorial.scenario
package.
However, making these model-time classes separate will
allow them to evolve independently of the input data format.
As we will see in Part 2, instances of these classes will be placed inside
a model-time road graph.
We will make out simulation model operate on these classes instead of those found in the Scenario
object.
The Mapping
class
During simulation, it is sometimes necessary to get the original, Scenario-contained object
thaw was used to construct the simulation object.
For such purposes, a new Mapping
class is created.
Create a new Mapping
class in the tutorial
package:
package tutorial;
import com.amalgamasimulation.utils.container.BiMap;
public class Mapping {
public BiMap<tutorial.scenario.Asset, tutorial.model.Asset> assetsMap = new BiMap<>();
}
Here, a bidirectional mapping for assets is stored. Note that it is not initialized automatically, we will need to do it when creating model-time Assets.
The Model
class
We are going to create the class that stores the model state and coordinates the simulation-related objects - the Model
class.
The class com.amalgamasimulation.engine.Model
from the Amalgama Platform will be the base class for our Model
class.
This class provides some convenience methods that we will use in the upcoming steps.
To create an instance of com.amalgamasimulation.engine.Model
we need an Engine
, so we will pass an Engine
instance
as a constructor parameter to our Model
class.
We have already created the Scenario
class that represents the model input,
so let’s pass a scenario object to the constructor of our Model
class
and use it to change the settings of the Engine
instance.
Create a new 'tutorial.model' package.
Create a new 'Model.java' file in the 'tutorial.model' package with the following contents:
package tutorial.model;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.distribution.ExponentialDistribution;
import org.apache.commons.math3.distribution.RealDistribution;
import org.apache.commons.math3.random.RandomGenerator;
import com.amalgamasimulation.engine.Engine;
import com.amalgamasimulation.utils.random.DefaultRandomGenerator;
import tutorial.Mapping;
import tutorial.scenario.Scenario;
public class Model extends com.amalgamasimulation.engine.Model {
private final Scenario scenario;
private final RandomGenerator randomGenerator = new DefaultRandomGenerator(1);
private Mapping mapping = new Mapping();
private List<Warehouse> warehouses = new ArrayList<>();
private List<Store> stores = new ArrayList<>();
public Model(Engine engine, Scenario scenario) {
super(engine);
engine.setTemporal(scenario.getSimulationStartDt(), ChronoUnit.HOURS);
engine.scheduleStop(engine.dateToTime(scenario.getSimulationEndDt()), "Stop");
this.scenario = scenario;
initializeModelObjects();
}
public double getRouteLength(Asset asset1, Asset asset2) {
return scenario.getRouteLength(mapping.assetsMap.key(asset1), mapping.assetsMap.key(asset2));
}
private void initializeModelObjects() {
initializeAssets();
}
private void initializeAssets() {
for (var scenarioWarehouse : scenario.getWarehouses()) {
var wh = new Warehouse(scenarioWarehouse.getId(), scenarioWarehouse.getName());
warehouses.add(wh);
mapping.assetsMap.put(scenarioWarehouse, wh);
}
for (var scenarioStore : scenario.getStores()) {
var store = new Store(scenarioStore.getId(), scenarioStore.getName());
stores.add(store);
mapping.assetsMap.put(scenarioStore, store);
}
}
public RandomGenerator getRandomGenerator() {
return randomGenerator;
}
public List<Warehouse> getWarehouses() {
return warehouses;
}
public List<Store> getStores() {
return stores;
}
}
You may have noticed that there are a lot of yet unused imports here.
We are going to build up the Model
class gradually, adding objects and methods in several stages,
so we intentionally add all imports at the outset to stay fully focused on the main code thereafter.
The randomGenerator
fields will be the 'single source of randomness' in the model,
so as to keep modeling results reproducible.
In the Model
class constructor, the Engine.setTemporal()
method maps the initial moment of the simulation to astronomical time.
We also ask the engine to use 'hours' for time units, since we are going to measure time in hours and truck speed in km/h.
See Model time and date article for more details.
Now that we have applied the time mapping, we can use dateToTime()
method of the Engine
instance to map 'astronomical' time to non-negative model time of the moment when the engine should stop the simulation.
The last line of the class constructor calls the initializeModelObjects()
method.
Currently only assets get initialized.