Saving and Restoring the Model Snapshot
In AnyLogic Professional Edition you are able to save the full state
of a model (the snapshot) during runtime to a file, restore it at a
later time and continue running simulation from the same point.
This feature may be useful in several cases:
- Resilience: when a
simulation takes very long time to complete, it may make sense to save
its state periodically so that you do not have to start everything from
scratch when e.g. the computer crashes.
- Skipping warm-up period:
if you plan to run several scenarios with a simulation that differ only
after the model warms up, you may run the model up to the end of its
warm-up period only once, save the state, and then reload it for every
scenario.
- Running distributed
simulations: many parallel/distributed simulation frameworks
require the ability to roll-back the model to some previous state
(checkpoint). This may be needed to synchronize the clocks of
concurrent simulations when one of them happens to “run too far”.
- Any other case when you need to refer to a particular state of
the model without running the simulation from the initial state.
AnyLogic model snapshot implementation is based on Java
serialization mechanism.
Saving a model snapshot
To
save a snapshot of
a model
- First, check that your model is serializable,
i.e. states of all model elements can be saved, and therefore all the
information about the current state of the model can be saved in a
model snapshot file. To check this, select the model item (e.g. Model) in the Projects view, and then select Tools > Check for Snapshot Compatibility
from the main menu. If there are some unserializable elements in your
model, fix this, and then continue.

- Snapshots can be saved and restored only when the model execution
is paused. Run the model and then pause the execution by clicking the Pause
control.
- Now you will need the snapshot management commands. The snapshot
commands are located in the developer
panel. To open the developer panel click the rightmost Open developer panel
control, located in the model window.
- By default, the snapshot commands are hidden. To see them, click
the show/hide
snapshot options control in the topmost line of the
developer panel.
- Click the save snapshot
toolbar button.
- The Save model snapshot
dialog box will open.
- Specify the name and location of the snapshot file and click the Save button when finished.
- The model state is saved. You can now continue the simulation,
stop, close it, or even shut down your computer.
Restoring a saved model snapshot
To restore a snapshot
of a model
- To
restore the saved model state you first need to run the same experiment
(you do not need to actually run the simulation, it is sufficient just
to launch the experiment).
- Now you will need snapshot management commands. Snapshot
commands are located in the developer
panel. To open the developer panel, in the model window, click the
rightmost control Open developer panel
.
- By default, the snapshot commands are hidden. To see them, click
the show/hide
snapshot options control in the topmost line of the
developer panel.
- Click the restore snapshot
toolbar button.
- The Open model snapshot
dialog box will open.
- Choose the snapshot file you want to restore and click the Open button when finished.
- The
model state saved in that file will be restored and the model will be
put in the paused state.
Saving and restoring model snapshots via API
Saving / restoring model snapshot from all experiments except for
the custom experiment
You can save and restore model snapshots using API
functions which both take one parameter –
the name of the snapshot file. Call them
as follows:
- getExperimentHost().saveSnapshot(String
fileName) - Pauses experiment if it is currently running, saves
snapshot and then resumes experiment if it was running. On any error
throws nothing. If you need custom error processing, please use another
function notation (see below).
Parameter:
fileName - the name of the snapshot file
- getExperimentHost().saveSnapshot(java.lang.String
fileName, java.lang.Runnable successfulCallback,
java.util.function.Consumer<java.lang.throwable> errorCallback)
- Pauses experiment if it is currently running, saves snapshot and then
resumes experiment if it was running.
Parameters:
fileName - the name of the snapshot file
successfulCallback
- called when the snapshot is successfully saved
errorCallback - called in case of any
error
Usage example. If the snapshot was successfully saved, we print the "Saved!" text to the console. In case of an
error, we
print the "Error!" text and the error
stack trace to the console.
getExperimentHost().saveSnapshot(
"file.als",
() -> traceln("Saved!"),
e -> {
traceln("Error!");
e.printStackTrace();
}
);
- getExperimentHost().loadSnapshot(String
fileName) - Stops experiment and loads
snapshot (in its “not running” state), doesn’t resume simulation of
loaded snapshot.
On any error throws nothing, silently rollbacks to the current
experiment and resumes it if it was running. If you need custom error
processing, please use another function notation (see below).
When snapshot is loaded, presentation forgets everything about the
model which was running before (including the engine, experiment, and
agents), therefore, it is recommended not to keep references to model
objects after this function call.
Parameter:
fileName - the name of the snapshot file
- getExperimentHost().loadSnapshot(java.lang.String
fileName, java.lang.Runnable successfulCallback,
java.util.function.Consumer<java.lang.throwable> errorCallback)
- Stops experiment and loads snapshot (in its 'not running' state),
doesn't resume simulation of loaded snapshot. On any error throws
nothing, silently rollbacks to the current experiment and resumes it if
it was running. When snapshot is loaded,
presentation forgets everything about the model which was running
before (including the engine, experiment, and agents), therefore, it is
recommended not to keep references to model objects after this function
call.
Parameters:
fileName - the name of the snapshot file
successfulCallback
- called when the snapshot is successfully loaded
errorCallback - called in case of any
error
Usage example. If the snapshot was successfully loaded, we print the "Loaded!" text to the console, set the window
to display the top-level agent's presentation and launch the model. In
case of an error, we
print the "Error!" text and the error
stack trace to the console.
getExperimentHost().loadSnapshot(
"file.als",
() -> {
traceln("Loaded!");
getExperimentHost().setPresentable(
getExperiment().getEngine().getRoot() );
getExperiment().run();
},
e -> {
traceln("Error!");
e.printStackTrace();
}
);
Saving / restoring model snapshot from
custom experiment
You can also save and restore model snapshots from custom experiment
code using the following functions of Engine:
Examples:
When you create a custom
experiment, the experiment has default auto-generated code (see the custom experiment's properties,
Code section) containing this
line of code: Engine engine = createEngine();
This code creates engine and
stores it in the local variable engine.
Use it to call the functions mentioned above. To call the functions,
write the following code:
engine.saveRootObjectSnapshot("C:\Model\Model.als");
Main root = (Main)engine.loadRootObjectSnapshot("C:Model\Model.als");
Or, instead of typing the code, you can
automatically insert the load snapshot function call while creating a custom experiment, by
selecting the checkbox Load top-level
agent from snapshot on the second page of the New Experiment wizard.
Loading a top-level agent from
snapshot into an experiment
There is one more use case of model snapshots. You can load a
top-level agent from a snapshot into an experiment. It can be any
experiment - possibly of even another type than the one that was
launched when you have saved the model snapshot. The only self-evident
restriction is that the model should be the same.
Loading a top-level agent from a snapshot into an experiment, you
load the saved state of a model from the snapshot file. The experiment
will be started from the time when the model state was saved.
Unlike restoring snapshots,
when loading a top-level agent from a snapshot, experiment settings are
not taken from the saved snapshot.
To load a top-level
agent from a snapshot into an experiment
- In the Projects view,
select the required experiment.
- Open the Advanced
section of the experiment's Properties.
- Select the Load root agent from
snapshot checkbox.
- Choose the snapshot file, which top-level agent's state you want
to load into the experiment. Type the full path to the snapshot file in
the edit box to the right of the checkbox. You can browse to the
required file using the Browse
button.
How to ensure your model is serializable
For 99% of models you do not have to do anything to enable their
serialization, i.e. saving their state at runtime: all AnyLogic objects
are serializable, including all standard library objects. However,
there are things you should be aware of.
As you know, AnyLogic allows you to use arbitrary Java classes in
your model, namely in:
- Parameters
- Variables
- Collections
- User’s Java classes
- Additional class code
If you wish all the objects to be saved and restored, you have to
either use only serializable classes, or define custom serialization,
or exclude them from the snapshot at all.
In the properties of Parameter, Variable
and Collection you will find the checkbox Save in Snapshot that is checked by
default. If you leave that checkbox checked AnyLogic will take care of
serialization, but the type of parameter, variable or a collection
element should then be one of:
- Java primitive type (int, double, etc.)
- Array of primitive type (int[], double[], boolean[][],
etc.)
- A class implementing java.io.Serializable
interface (like Integer, Date, HyperArray,
etc.)
- Array of serializable objects (Integer[],
Date[] etc.)
By unchecking that checkbox you tell AnyLogic to exclude the
corresponding object from standard serialization. You may then specify
custom serialization for it by defining two functions in the agent's Additional class code:
- The function void writeCustomData(
ObjectOutputStream os ) throws java.io.IOException. In the body
of the function use output stream to write values of java primitive
types and objects (which should implement java.io.Serializable).
- The function void readCustomData(
ObjectInputStream is ) throws java.io.IOException,
ClassNotFoundException. In the body of the function use input
stream to read values of java primitive types and objects (which should
be serializable) in the same order they are saved in writeCustomData function.
Please note that if you are defining custom serialization for a
field defined in the Additional class
code (i.e. not graphically and for which there is no Save in snapshot checkbox that you
can uncheck) you need to declare them as transient
by adding this word before the field name.
The wizard for creating a new Java class within AnyLogic IDE now
has an option Enable saving this
class in model snapshots. If it is checked, the new class will
be generated as implementing java.io.Serializable
interface and contain the code line
private static
final long serialVersionUID = <some_value> ;
where <some_value> is a kind of class fingerprint which normally
should be changed when class structure changes (e.g. when some fields
are added to (removed from) the class). Please note that in the models
created earlier the Java classes were not implementing that interface
by default and you may need to add the corresponding code manually.
Same applies to the inner classes if there are any.
The Java classes used as statechart transition messages MUST be
serializable.
Also, there is one common recommendation for making advanced
structures Serializable. All user-defined cross-object links except
links to the embedded objects must have custom serialization (and Save
in snapshot checkbox should be unchecked for the corresponding model
elements). An example of one-level-links is field “next” of some
object, which stores reference to the object of the same class and thus
also having field “next” with reference to third object and so on. Link
to owner is link from one object located inside another object, to its
parent.
Some classes require additional custom deserialization code for
their instances, if any are created manually (i.e. in the code) and
stored in the serializable fields (e.g. in collections or variables) of
model objects. These classes are:
- Agent, AgentCollection
and subclasses
- Agent, Environment
- Database, TextFile
- Statechart, Transition classes, Event
classes
- Port
- Shapes not present on the presentation or icon of agent
Note, that custom deserialization code is automatically generated
for all elements added from the Palette
in the AnyLogic graphical editor.
Java Classes which need custom serialization, i.e. require special
handling during the serialization and deserialization process must
implement special functions with these exact signatures:
private void
writeObject(java.io.ObjectOutputStream out)
throws IOException
private void
readObject(java.io.ObjectInputStream in)
throws IOException,
ClassNotFoundException;
The following description is borrowed from API JavaDoc of standard
Java interface java.io.Serializable:
- The writeObject function is
responsible for writing the state of the object for its particular
class so that the corresponding readObject
function can restore it. The default mechanism for saving the Object's
fields can be invoked by calling out.defaultWriteObject.
The function does not need to concern itself with the state belonging
to its superclasses or subclasses. State is saved by writing the
individual fields to the ObjectOutputStream
using the writeObject function or by
using the functions for primitive data types supported by DataOutput.
- The readObject function is
responsible for reading from the stream and restoring the classes
fields. It may call in.defaultReadObject
to invoke the default mechanism for restoring the object's non-static
and non-transient fields. The defaultReadObject
function uses information in the stream to assign the fields of the
object saved in the stream with the correspondingly named fields in the
current object. This handles the case when the class has evolved to add
new fields.
Restrictions
Static elements
The following AnyLogic elements can be declared as static:
Please note that static elements are
not saved into a snapshot file.
If you do not alter the value of a static element in your model, the
state of the restored model will be exactly the same as the previously
saved state. However, if you somehow change the value of a static
element (e.g. in agent's On startup
code), the modified value won't be saved into a snapshot, and therefore
the restored state of the model will differ from the state you have
previously saved.
There is one more case to be mentioned with the static constant
variable and some other variable inside a model referring to the same
Java object. As a result, when you check the constant and the variable
for equality, you will get true.
However, if you save the model state into a snapshot, the constant and
the variable in the restored model will refer to different objects, and
they won't be considered as equal anymore, thus the restored model
behavior will differ.
The general conclusion is to avoid using static elements if you plan to
save/restore your model state using the AnyLogic snapshot feature.
Non-serializable objects
Some Java objects cannot be serialized, for example objects of class
BufferedImage. Variables of such types
should be excluded from standard snapshot serialization. If you wish to
save and restore these objects anyway, you can write some code in the
functions writeCustomData and readCustomData. This code should take care of
custom serialization and loading of some base content of a given
non-serializable object (of course, it is possible to obtain that base
content from the object and setup it back in the future).
For example, for the BufferedImage
base content could be: width, height, image type parameters (which are
used in the constructor) and int[] array
of pixel colors (which can be retrieved by calling getRGB function). The code restoring BufferedImage (in the readCustomData
function) should create an instance by invoking constructor with above
parameters and then call setRGB function
to restore the picture.
Open database connections and open files cannot save and restore
their states. Therefore after restoring the snapshot the connections
and files will be in closed state. This does not mean you cannot
continue accessing them. For example, if the model writes to a kind of
a log file, it can continue writing there, provided the file is in
“append” mode. Connectivity objects will create their connections on
first access, as they usually do. But Statement
and ResultSet objects (if any are
opened) cannot be included in the snapshot (they are not serializable).
It is recommended to open ResultSets,
access and close them entirely within the code of some function body
(or any code section). Storing references to Statement
and ResultSet in the variables of agent
is not recommended. Also, note that snapshot cannot affect external
data sources and in some cases manual database backup and restore may
be required.
At the moment OptQuest optimization engine cannot save and restore
its state, therefore you cannot save snapshots of Optimization
experiments. In the future however OptTek will release a serializable
version of OptQuest.