JSIT Version: 0.2
Last Updated: 5th June 2018
The Java Simulation Infrastructure Toolkit (JSIT) is a set of Java libraries which adds a set of useful 'infrastructural' features for simulation:
JSIT also suggests some best-practice ways of splitting up your source code (separating simulation from experiments), though this is not required.
JSIT can be used in any Java-based simulation (or any simulation which can interoperate with Java), though the focus is on something that will integrate nicely with Java-based simulation frameworks such as MASON, Repast Simphony or AnyLogic. (JSIT was thought up precisely because all the Java-based simulation frameworks the author is aware of lack these important features, at least in the relatively 'full' form provided by JSIT.)
Using the core JSIT library in a given simulation framework requires a fair degree of thought and custom code, so JSIT also provides helper libraries for specific frameworks which provide robust, well-designed and reusable ways to more simply integrate with any model written using that framework and avoid having to integrate in a 'raw' way. (They also sometimes add useful helper features that leverage tools available in that framework.) Currently, a helper library is only available for AnyLogic, though there is an example model which shows one way to do a raw integration for a MASON-based model.
There is an open-access academic journal paper which gives much more detail on the rationale for JSIT and why modellers might want to use the features above (as well as the wider picture of best-practice software engineering principles for simulation, and why the helper library focus was on AnyLogic). Please cite this paper (using the citation below) when using JSIT for any models that are included in your own academic papers.
Rossiter, S. (2015). Simulation Design: Trans-Paradigm Best-Practice from Software Engineering. Journal of Artificial Societies & Social Simulations (JASSS).
Below is a basic list of current features (and some side notes on intended future functionality). The paper mentioned above provides some extra detail.
Logging
This is supported by the powerful and widely-used Logback open-source logging libraries.Stochasticity Control
static class fields since some instances of the class might be for
one run, and some for another).Publish-Subscribe Events Architecture
Run Reproducibility
The UML class diagram in Figure 1 summarises the core JSIT classes and how they fit into a user-written model:
Figure 1: Overview of JSIT class structure within a user model.
The user's model consists of a 'root' class and some set of model components
which it composes (here indicated by <ModelRoot> for the
root, and <ModelComponent> for some given component, which
could also be the root). The root class needs to implement the JSIT
MainModel interface (possibly via a superclass or other helper
class) and set up some concrete subclass of ModelInitialiser which
initialises the JSIT framework (in particular the logging, which we want to be
set up at the earliest possible opportunity so that messages can be logged
during 'early' parts of model initialisation).
The concrete ModelInitialiser will set up a concrete
Sampler subclass which links the set of stochasticity classes to
some actual framework implementation. JSIT is not intended to provide some
'canonical' set of stochastic implementations; the JSIT classes are
wrappers to some underlying implementation which ensure a consistent API and
(crucially) allow for the stochasticity to be controlled external to the model's
code.
Helper libraries do virtually all the work above for you for a given simulation framework. With all this set up, the model's user code can then use the JSIT features as it needs to:
Logger instances for
logging (using Logback as the underlying
implementation) and a Logback configuration file to control the logging per
run.ModelInitialiser will automatically store
settings for each run (including all model parameters and information on the
environment, where this includes provenance for the model code via its location
in a version control system).StandardStochasticAccessor, which is explained later.AbstractStochasticItem subclasses are used for probability
distributions (and related components such as distribution-lookups via Java enum
'keys'), and per-run configuration files to control this stochasticity.EventManager is used to publish and subscribe to
events (using EventSource and EventReceiver interfaces
which are not shown in the diagram).
JSIT also provides some helper tools for committing model code to version-control systems which sets up the meta-data needed to have an audit trail of model versions.
The JSIT implementation uses a lot of Java Generics functionality (which means that Java 5+ is required) so that there is useful compile-time type-checking for things like probablity distributions.
Figure 2 below shows the equivalent of figure 1 for an AnyLogic model, and is explained in more detail later. Note for now that
MainModel_AnyLogic (which implements the MainModel
interface).HyperArrayLookup) which leverages an AnyLogic component.
Figure 2: Overview of JSIT class structure within an AnyLogic user model (using the JSIT AnyLogic helper library).
Two example models are available for JSIT:
AMD_HealthSocialCareSim
SourceForge project.sprossiter/JSIT_DemoMASON
GitHub project.JSIT users will either be using a framework-specific helper library, or coding their own 'raw' integration with JSIT (effectively their own set of helper classes, which can be as 'minimal' or model-specific as desired). For each area of usage, I break down the information as follows:
NB: Whenever I refer to filesystem paths, I use Linux-style forward slashes as separators (cf. backslashes on Windows). Where these paths are actually included in configuration files read by Java, you can actually leave them as forward slashes on Windows, because Java will still interpret them properly.
Your model's source code, and any libraries used, should exist under a single 'simulation' directory so that JSIT knows what comprises the code of the model. This should ideally be separate from any code for model experiments because, otherwise, JSIT cannot distinguish between the model itself having changed or just some details of an experiment using it. (Since JSIT records details of all the parameters set for each experiment in per-run outputs, experiments can be reproduced from this information and the correct version of the simulation code.)
There should be separate directories for JSIT-specific inputs (which apply to all experiments run) and outputs (where individual runs will have sub-directories automatically created by JSIT with timestamped names). Since these relate only to experiments, it makes sense to group them with the source code for your experiments.
Source and Libs folders shown for
experiments.
ModelX as a project or (perhaps better to
ensure that the separation is a clean one) have your Sim and
Experiments directories as separate projects, with the latter
dependent on the former.Let's assume in what follows that your model is called ModelX
and that you have directory structure as below (which is a sensible 'canonical'
structure given the above):
ModelX
Sim
Source
Libs
Experiments
Source
Libs
Inputs
Outputs
We'll talk about the 'simulation folder' and the 'experiments folder' in what
follows. Let's also assume that your model code is all in a single Java package
uk.ac.uniofsim.modelx.
If you are storing your model code in an SVN VCS, you will need an implementation of the Apache Subversion Java binding library (JavaHL) installed. This is a mixture of native (OS-specific) and JAR libraries.
On Linux, there should be a package for this. On Ubuntu it is libsvn-java.
This installs the native libraries and puts the svn-javahl.jar JAR
file in /usr/share/java. You will need to reference this as a
run-time dependency for your model (i.e., a JAR on the classpath when you run
it).
On Windows, install either the SlikSVN or CollabNet SVN client to
install the native JavaHL libraries. This can exist alongside TortoiseSVN, which most Windows SVN users
tend to use for its GUI (but which does not include JavaHL). Then include the
relevant JavaHL JAR file from the JSIT distribution
(libsvn-java-<ver>.jar) for your SVN client version as a
run-time dependency for your model (i.e., a JAR on the classpath when you run
it).
In your simulation libraries folder, add JARs for the JSIT core library and all its run-time dependencies (and their dependencies):
Add all of these as compile and run-time dependencies for your simulation and its experiments (in the IDE you develop it in or however you compile and launch your model).
bin folder,
and any non-Java files in your source folders are also copied there.
Thus, putting logback.xml in your Experiments/Source
folder will mean it is on the classpath (in the generated bin
folder) at run-time.
System.out.println("Classpath: " + System.getProperty("java.class.path"));
In a directory that will be on the Java classpath when running your
experiments, include a logging configuration file
logback.xml, customised from the template provided in
src/main/config/logback.xml (see the comments therein). Unless you
want to adjust the default logging behaviour, the only things you need to change
in the template is your model's Java package name as below:
<logger name="uk.ac.uniofsim.modelx" level="INFO" additivity="false">
<appender-ref ref="MSGS_RUN_SIFTER" />
<appender-ref ref="CONSOLE" />
</logger>
In your simulation directory, include a customised JSIT model version file modelVersion.properties. If
your model code is in a Subversion (SVN) VCS, use
src/main/config/SVN/modelVersion.properties as a template, or
src/main/config/SVN_Legacy/modelVersion.properties if you are using
a pre-1.7 SVN client. Otherwise use
src/main/config/NoVCS/modelVersion.properties as a template. Fill
in the ModelName and ModelVersionNum fields (example
below for SVN 1.7+):
# Model Version Control Properties File for SVN 1.7+ Client # USERS SHOULD ONLY EDIT ModelName and ModelVersionNum # Extra entries will be added on commit ModelName = ModelX ModelVersionNum = 0.1 ModelVCS = SVN VersionFileRepoURL = $HeadURL$ LastCommitRev = $Revision$
This will be automatically updated if you commit model changes to a version control system (or 'checkpoint' the model code if using the NoVCS file) using JSIT tools.
import java.io.File;
System.out.println("Working directory: " + new File(".").getAbsolutePath());
In your experiments' inputs directory, include a model
source paths file modelSourcePaths.properties which tells
JSIT the paths from where your experiments are run to where certain aspects of
the model code resides. Use the template in
src/main/config/modelSourcePaths.properties. Given the directory
structure earlier, this would have non-comment contents as below:
ModelVersionFileDir = Sim SimSourcePath = SimThe second entry here (which can include multiple directories) is only needed if you have the source code of the model available at run-time, as is the case in the example given . It is used so that JSIT can check whether the simulation source code specified has been changed from the VCS version it came from; you can also omit this second entry if you do have the source code available, but don't want this check to be done.
In either your 'root' model directory or your simulation directory, include a customised commit/checkpoint batch script. If it is in your root directory, it will be used to commit changes to all your model code (or checkpoint the code if you are not using a VCS). If it is in your simulation directory, it will be used to commit simulation-only changes. In either case, any changes to simulation code (cf. other model code) will result in the model properties file being automatically updated so as to record version-control details of the simulation code.
For Windows, use src/main/scripts/commitModel.bat as a template;
for Linux use src/main/scripts/commitModel.sh (set as executable).
The script should be altered to include the path to your simulation code
directory (relative to where the script is). For Windows with the directory
structure above, and the script in the model root folder, the script would have
non-comment contents as below:
java -cp "Sim\Libs\*" ^ uk.ac.soton.simulation.jsit.core.ModelVersioningAssistant COMMIT Sim echo ------------------------------------------------------- echo Commit complete (check for errors above). Press any key pause
For completeness, your file structure should therefore be as below on Windows assuming everything stated earlier (JAR file versions may differ):
ModelX
commitModel.bat
Sim
modelVersion.properties
Source
Libs
commons-codec-1.10.jar
commons-configuration-1.10.jar
commons-io-2.4.jar
commons-lang-2.6.jar
commons-logging-1.2.jar
commons-math3-3.6.1.jar
jsit-core-0.2.jar
logback-classic-1.1.2.jar
logback-core-1.1.2.jar
slf4j-api-1.7.4.jar
xmlpull-1.1.3.1.jar
xpp3_min-1.1.4c.jar
xstream-1.4.7.jar
Experiments
Source
logback.xml
Libs
Inputs
modelSourcePaths.properties
Outputs
There are some important specifics with respect to AnyLogic.
By default, AnyLogic models generate code in a Java package which has the
same name as your ALP file. However, you can change this in the Advanced part of
the model's Properties; the below assumes you have changed this to
uk.ac.uniofsim.modelx as earlier.
AnyLogic models are stored in .alp files and normally the
experiments (AnyLogic Experiments) are included in the same file. However, it
is possible to separate the two, as JSIT prefers, because you can
include (instances of) Agents from another model file. Thus, you can have a
simulation file (say ModelX.alp for our running example) with no
Experiments, and an experiments-only one (say
ModelX_Experiments.alp).
Unfortunately, an Experiment's top-level agent must still exist in the same file, so you need to proceed as below:
Main_Model and Main_ModelFullViz in
AMD_HealthSocialCare.alp, and their wrapper Agents
Main_ModelWrapper and Main_ModelFullVizWrapper in
AMD_ExperimentsDemos.alp.LoggingConfig
folder outside of the ALP file Source folder because
AnyLogic currently (v7.1.2) has an issue where class folder model
dependencies that are not within the same directory as the model file are
referenced internally using absolute file paths, which means that they
will break if the model moves to a new absolute path (e.g., from under one
user's home directory to another's), even if the relative path to the dependency
stays the same. This was fixed in AnyLogic 7.1.2 for JAR files (and so is not an
issue for other dependencies as below), but the problem remains for class
folders.AnyLogic does not copy across files to its 'hidden' build area
which are not referenced from the ALP file, so you cannot include
logback.xml in the same directory as the ALP file and have it on
the Java classpath at runtime. The simplest way to achieve it is to put
logback.xml in its own LoggingConfig or similar
directory (under the folder where the ALP file is) and add this as a dependency
to the experiments ALP file (specifying it as an External Class Folder and not
importing it to the model folder).
All the JAR file dependencies discussed earlier, plus the JSIT AnyLogic
helper library jsit-anylogic-<ver>.jar, need adding to your
AnyLogic simulation file as dependencies (in the Dependencies section of the
model's Properties).
Dependencies should be set as Java archive files accessed from their original
location and referred to by model folder (see Figure 3 below), which
ensures that relative paths to them are stored in the ALP file (and thus that
the code is transferable to different directories; e.g., another user's home
directory).

Figure 3: Screenshot of the AnyLogic add dependency (classpath entry) window showing the required settings.
If you are using wrapper Agents as above in a separate experiments
ALP file, then these dependencies will automatically be used at
run-time because you are referencing that ALP file as a dependency
(which happens automatically when you drag an instance of the main Agent into
your wrapper Agent). However, you will also need to add the two JSIT libraries
(only) as dependencies for the experiments ALP file because they are needed at
compile time (jsit-core-<ver>.jar and
jsit-anylogic-<ver>.jar); you can reference them from the
main model ones (canonically in Sim/Libs).
Given all this, the canonical file structure would be as follows for an AnyLogic model (also showing the ALP files):
ModelX
commitModel.bat
Sim
modelVersion.properties
Source
ModelX.alp
Libs
commons-codec-1.10.jar
commons-configuration-1.10.jar
commons-io-2.4.jar
commons-lang-2.6.jar
commons-logging-1.2.jar
commons-math3-3.6.1.jar
jsit-anylogic-0.2.jar
jsit-core-0.2.jar
logback-classic-1.1.2.jar
logback-core-1.1.2.jar
slf4j-api-1.7.4.jar
xmlpull-1.1.3.1.jar
xpp3_min-1.1.4c.jar
xstream-1.4.7.jar
Experiments
Source
ModelX_Experiments.alp
LoggingConfig
logback.xml
Libs
Inputs
modelSourcePaths.properties
Outputs
The runtime working directory for AnyLogic models is set as the directory
where the source ALP file is which is being run. Thus, when running an
experiment using the canonical file structure above, the working directory is
the ModelX/Experiments/Source folder, and your
modelSourcePaths.properties entries should be as below:
ModelVersionFileDir = ../../Sim SimSourcePath = ../../Sim
The top-level Agent in your simulation ALP file needs to be a subclass of
uk.ac.soton.simulation.jsit.anylogic.MainModel_AnyLogic, which is
itself a subclass of Agent. (Set this in the 'Extends other agent'
section of the Advanced Agent properties.) Doing this ensures that JSIT is
initialised at the earliest possible moment so that, for example, messages can
be logged in initialisation logic.
MainModel is the
uk.ac.soton.simulation.jsit.core.MainModel interface in the JSIT
source code.This MainModel_AnyLogic superclass provides default
implementations for the MainModel methods (AnyLogic functions)
which define some aspects of how JSIT is configured. You can override these
(provide your own implementation in an AnyLogic function) to adjust this default
behaviour. Some relate to aspects in later sections, and so are discussed there.
Ones you might want to override for the core JSIT setup are as below:
getInputsBasePath (no parameters): returns a
String which gives the relative path to your inputs directory
(relative to the base Java working directory when a model experiment is run).
This defaults to Inputs which matches the example directory
structure.getOutputsBasePath (no parameters): returns a
String which gives the relative path to your outputs directory
(relative to the base Java working directory when a model experiment is run).
This defaults to Outputs which matches the example directory
structure.runSpecificEnvironmentSetup (no parameters and no return
value): performs any run-specific (or model-specific) setup logic that the
modeller wants to do at this early JSIT initialisation stage. Defaults to doing
nothing.As outlined in Figure 1, you will need to implement your own subclasses of
the JSIT ModelInitialiser and Sampler classes. To get
a feel for how that can be done, have a look at the MASON-based demo
model.
This is a standard process for any non-AnyLogic model, using SLF4J classes. However, AnyLogic currently has issues with its threading strategy for models which means the normal process cannot be used, and an alternative method is required.
This is the normal way that SLF4J and Logback would be used; the Logback manual can be consulted for further detail.
Any classes that need to log messages should have the following code
(assuming a class name of ModelComponentA here):
import org.slf4j.*;
private static final Logger logger
= LoggerFactory.getLogger(ModelComponentA.class);
To log a message of a particular level (Error, Warning, Info, Debug,
Trace), use code similar to the below (which also shows how you can use
if tests to avoid the overhead of constructing the message if that
level is not enabled at run-time, and how to use the Logback-specific
{} syntax in message strings to 'fill-in' variable values rather
than using string concatenation):
logger.error("Something's gone horribly wrong");
logger.warn("Something might have gone horribly wrong");
logger.info("Have wangled wodger {} at time {}", wodgerID, currTime);
if (logger.isDebugEnabled()) {
logger.debug("My complex debug of {}, {}, {} and {}", a, b, c, d);
}
if (logger.isTraceEnabled()) {
logger.trace("My super-complex trace of {}, {} and {}", x, y, z);
}
JSIT creates per-run diagnostic log files in timestamped folders within your outputs folder.
missingOutputFolder directory, instead of the proper log file for
the run (and messages in this dummy file may come from multiple runs).
Simulation objects (e.g., Agents) need to use AnyLogicLogger
loggers instead of the normal SLF4J ones; these have exactly the same SLF4J
interface (they implement the Logger interface), but the logging
methods (like the warn and info variants) have code
which works around the threading issues. They also add a special extra method
ensureExternalLoggingForBlock (see the Javadoc for full details)
which can be used to ensure that log messages from third-party libraries
(including from JSIT itself) also avoid the problems.
However, there needs to be one AnyLogic logger for each run's set of
instances for a class, not one per class as in the standard approach. If
you use multi-run Experiments with parallel execution, then you will get
instances of your model classes (e.g., Agents) for multiple runs together in the
same JVM. For example, a Patient Agent might have 10 instances as
part of run 1, 20 as part of run 2 and 300 as part of run 3. All 330 instances
would potentially be existing at the same time in the same JVM. We need ways to
separate out the AnyLogic loggers that runs 1-3 use.
You have three alternatives:
this with a reference to some (any) Agent in your model.AnyLogicLogger
variable directly in that instance, set up in agent startup code:
logger = AnyLogicLoggerAccessor.getAccessorFreeAnyLogicLogger(
TheComponent.class, this);
where TheComponent is the name of the class, and
logger is your AnyLogicLogger instance (typically an
AnyLogic variable).logger would be a static variable.AnyLogicLoggerAccessor to hold the loggers per run, and retrieve
the correct one each time you need it:
// Set up 'empty' accessor
loggerAccessor = new AnyLogicLoggerAccessor(TheComponent.class);
// Add logger for this run (done once per run), passing an Agent in the model
loggerAccessor.addLoggerForRun(this);
// Later on when using the logger to log messages
loggerAccessor.getLoggerForRun(this).info("My important message");
Normally you'd set up the empty accessor as part of setting your static
variable's initial value, and you'd add the per-run logger at simulation
initialisation time. To help with the latter, MainModel_AnyLogic
has a method doAllStaticPerRunLoggerSetup which your main model
class (which is a subclass of MainModel_AnyLogic) must implement,
and which will be called early on during model initialisation. It is expected
that you will use this method to set up the per-run loggers for all classes that
need one. (You can just directly do this for all the classes, or code your own
functions (methods) so that the top-level class chains down through the embedded
Agents, each of which adds its own per-run logger.) NB: You will need import statements for the relevant classes, which is easiest to do for their entire packages as below. (For AnyLogic Agents, this is in the Advanced Java properties.)
import org.slf4j.*; import uk.ac.soton.simulation.jsit.anylogic.*;
Each stochastic item in your simulation (e.g., a normal distribution or
simple Bernoulli trial probability) is specified as an instance of a JSIT
AbstractStochasticItem subclass. Typically all instances of a model
component (for the same model run) will share an instance, though there may be
cases where you want per-instance distributions. The actual runtime sampling is
still done by the toolkit you are using; by using the JSIT 'wrappers', you gain
a consistent API and, more importantly, the ability to collapse selected items
to a single 'mid' value (typically the mean) for a run, which makes testing and
model exploration significantly easier in many cases.
The runtime control is done by supplying a stochasticity
control file in your inputs directory called
stochControl.properties. A template is provided in the
config directory of the JSIT distribution (with comments there
detailing the format).
NB: ModelInitialiser has a
disableStochOverrides method which can be called to disable use of
the stochasticity control file (if one exists). This allows JSIT users to set up
a stochasticity control file (used, for example, during testing) but not
actually have it used in a 'normal' run (by having such runs call this method at
model startup).
Because some simulations may have multiple model runs in parallel (within the same JVM) this means that we cannot always assume that all instances of a model component (Java class) are part of the same model. Thus, stochastic items can be registered per-model-instance and stored/accessed via a stochastic accessor (in a very similar way to how logging works for AnyLogic; see earlier). For the same reasons as with logging, AnyLogic models should use a different form of accessor to non-AnyLogic models.
As well as standard distributions, JSIT also provides some useful classes for custom distributions and 'distribution lookups': a set of distributions (of the same type) keyed by a set of Java enumerations. The latter can be used, for example, to represent a lookup of death rates (Bernoulli distributions) by age-range and gender classifications.
Have a look at the MASON-based demo model.
Have a look at the AnyLogic-based demo model.
Models that want to use JSIT events should create an instance of
EventManager from the JSIT core library. Model components can be
event sources or event receivers (or both), the specifics depending on which
events architecture you are using (see below).
All domain events will be logged to a special events log file
modelEvents.log per run (in the JSIT-specified output folder).
The basic relationships between classes is shown in figure 4 below:
Figure 4: Overview of JSIT domain events architecture for marker-based events, as a UML class diagram.
Event source classes should define an enum (which specifies the alternative
events that this class can produce) and then implement the
appropriately-parameterised EventSource interface. Whenever they
create an event, they should call the publish method on the event
manager (providing an optional log message for the event).
Event receivers should implement the EventReceiver interface and
register with the event manager for the events they want to subscribe to (either
for all instances of a source class, or only for particular instances). For any
extra information the receiver wants about the event (other than its type
provided by the source's getLastEventType method) the receiver
would need to cast the source and access information directly; this thus
requires that
See the example models for more details.
The basic relationships between classes is shown in figure 5 below:
Figure 5: Overview of JSIT domain events architecture for message-class events, as a UML class diagram.
Event source classes should define classes for each message (typically as
static nested classes) and then implement the EventMessageSource
interface. Whenever they create an event, they should call the
publish method on the event manager passing the message object
(optionally logging the event via its toString method).
Event receivers should implement a receiving class per-message-type (via an
appropriately-parameterised subclass of the abstract
EventMessageReceiver class) and then register instances of these
receiving classes with the event manager for the events they want to subscribe
to (either for all instances of a message class, or only for instances from a
particular source).
As long as your model version file and model source paths file are in place,
there is nothing else that has to be done (unless you are not using a
helper library). Settings for the run will be produced in a
settings.xml XML file in your outputs folder (in a run-specific
sub-folder, along with your logs).
The settings file includes:
In particular, the checksum can be used to check if two runs definitely used exactly the same runtime code.
There are also runtime checks as to whether the source code for the model has been changed from that 'registered' via version-control (which may mean this run is not reproducible); see the Model Version Control section.
For an AnyLogic model (when run from the AnyLogic client rather than exported), the source code is the only code that the user maintains. (This is built and compiled by AnyLogic automatically before any run.) Thus, the JSIT run reproducibility and version control functionality is particularly useful since the source 'is' the model and distinctions between source and runtime code are less problematic.
The helper library uses the code structure generated by AnyLogic to automatically identify all the model's parameters (i.e., the Parameters in the top-level Agent).
One caveat is that AnyLogic uses java.util.Random as its random
number generator (RNG). This has the issue that the seed used to instantiate it
cannot be retrieved after it has been created, so JSIT cannot know what
randomness settings you used for this run. To help with this, the AnyLogic JSIT
helper library provides a RandomWithSeedAccess alternative (a
subclass of Random) which you can use instead. Just use this as a
custom generator (in the Experiment's randomness properties), giving it a
specific seed or no parameter for a random seed; e.g.,
new RandomWithSeedAccess(1)or
new RandomWithSeedAccess()
If you want to use your own preferred RNG, then have it implement the
uk.ac.soton.simulation.jsit.anylogic.SeedAccessibleRNG interface so
that JSIT can read the seed (and it will need to be a subclass of
Random for AnyLogic to use it).
Another issue is that, in an AnyLogic multi-run Experiment (e.g., Parameter
Variation or Monte Carlo), setting a fixed seed for reproducibility means that
all the runs will get the same seed and so will have no stochastic
variation. If you want reproducible runs with stochastic variation, you
can use RandomWithSeedAccess instantiated so that it uses the
provided seed as a base which is incremented for each further
instantiation (and thus you get a reproducible set of seed values for the runs
in your multi-run experiment); e.g.,
new RandomWithSeedAccess(1, true)See the Javadoc for more details, including some peculiarities due to AnyLogic's threading strategy.
You will need to include code in your ModelInitialiser subclass
to identify and prepare for serialisation (using XStream) your model's parameter
values for this run. This typically means using a Java class for your model's
parameters which just has fields for the parameters (and nothing else) so that
XStream will serialise that cleanly.
To get a feel for how that can be done, have a look at the MASON-based demo model.
As discussed earlier, you specify in your model version file which version control system you are using for your model. You use your commit script to commit (when using a VCS) or checkpoint (when not) your model. (I'll just refer to 'commits' generally in what follows, although the non-VCS case is technically something slightly different.)
Your commit message should be specified in a workingChanges.txt
file in the same directory as the commit script (which is the root directory of
the commit). After your commit, these changes will be appended to the
jsitCommitHistory.txt (which is created if it does not exist)
together with a header showing the commit time and source directories checksum
(see later). You can therefore use this second file as a history of all your
commits, which you could potentially store in the VCS as well.
At runtime, if you specified the SimSourcePath property in your
model source paths file, JSIT will check whether the source code for the model
has been changed from its state at the last commit, and will issue a warning
message if so (which means that you may not be able to reproduce this run of the
model).
NB: The source code for your model is taken as all the files in your source directories, whether they are under version-control or not. (Your model version file and any hidden folders, such as those used by SVN, are ignored.) This is normally what you want to happen because your source folders may include other not-in-the-VCS code that your model uses at runtime; typically third-party libraries (which it is not best-practice to include in the VCS). You want to check that none of these files have been altered at runtime from their state at commit-time. However, having not-in-VCS code in your source folders is not ideal from a run reproducibility perspective because you have no straightforward way of retrieving these non-VCS files if you want to reproduce a run. However, as the Run Reproducibility section states, JSIT does record all the libraries (and other classpath folders) used for a run (but not currently any provenance of where these files came from) which will help in reproducing the code.
When you run your commit script it will checkpoint your simulation source directories by calculating a checksum (MD5 hash) for its contents and storing this, together with a checkpoint timestamp, in the model version file. This checksum excludes your model version file and any hidden files. If, at runtime, the files on the simulation source code path do not match this checksum, the source code has changed and is flagged.
You can therefore use the batch script as a 'poor man's version-control-system' to checkpoint versions of the model code. (If you want these different versions to be retrievable in future, it is your responsibility to store them in some suitable way; this is what a VCS is designed for, so I strongly recommend you use one instead!) By looking at the model version file in a particular copy of the model code, you can see which checkpointed version it is.
When you run your commit script it will commit your simulation source code to the SVN server and, in the model version file, store a commit timestamp and checksum (MD5 hash) for the source code directories (as specified in the model source paths configuration file). Using SVN keyword substitution, the model version file also stores details of the on-server URL and SVN revision for the commit.
When running a model, you may have checked-out source code (i.e., with SVN metadata) or exported source code (i.e., just the 'plain' source files). In both cases, JSIT will check whether the source code has changed checksum (which, as mentioned earlier, will include any not-in-VCS code). Where you have checked-out source, JSIT will also check if this source has been modified from what was checked out (via SVN operations), so you can determine if any changes are in VCS-controlled source or elsewhere.
If you forget and commit the source code without using the batch script, just run it later to register a follow-on JSIT commit.
Note: It is your choice how you structure your source directories, and which you specify in the model source paths configuration file. If you want to only specify directories which include in-VCS code you can do, but that means any not-in-VCS files used at runtime (e.g., JAR libraries) will not be included in the source checks. In any case, you can use the separate checksum of all runtime code in your JSIT-produced per-run settings file (see Run Reproducibility) to determine if the total set of runtime code has changed from a previous run.
There is a full Javadoc-based JSIT API reference.