AnyLogic
Expand
Font size

Cloud JavaScript API

JavaScript API is used to build custom web interfaces for cloud-based AnyLogic models with or without animation.

To start using the API

  1. Obtain the cloud_client library by adding the <script> tag to the head of your webpage:
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
  2. Obtain the API key in the AnyLogic Cloud UI.
  3. Create a new instance of the CloudClient object:
    cloudClient = CloudClient.create( <apiKey> ) if you are using the public version of AnyLogic Cloud, or
    cloudClient = CloudClient.create( <apiKey>, <URL of the Cloud instance> );
  4. Use the API to work with the cloud-based models.
  • AnyLogic Cloud JavaScript API is fully asynchronous and extensively uses the promise technology
  • Your IDE compiler settings should allow ECMAScript 6

API reference

The JavaScript API treats names (of model, experiment, inputs, and outputs) as case-insensitive.

CloudClient class

The CloudClient class is responsible for authentication and communication with the AnyLogic Cloud.

Typically, there is only one object of class CloudClient in your JavaScript code.

Function Description
create(apiKey, host) A static function that creates the API client with given API key, and optionally the host name.
If the host is omitted, the public cloud host name https://cloud.anylogic.com is assumed.
getModels() Returns available models as an array of Model objects by promise.
getModelById(id) Returns the Model object with a given id by promise.
getModelByName(name) Returns the Model object with a given name by promise.
getModelVersionById(model, versionId) Returns the Version object of a given Model with a given id by promise.
getModelVersionByNumber(model, versionNumber) Returns the Version object of a given Model with a given number by promise (version numbering starts with 1).
getLatestModelVersion(modelorname) Returns the latest Version object of a given model (which can be either a Model, or a model name) by promise.
createDefaultInputs(version) Сreates and returns an Inputs object for a given model Version with default input values.
createInputsFromExperiment(version, experimentName) Сreates and returns by promise the Inputs object for a given model Version object copied from the experiment with the given name.
createSimulation(inputs) Creates and returns a ModelRun object of the SIMULATION type with given Inputs (the model and the version are identified by Inputs).
createParameterVariation(inputs) Creates and returns a ModelRun object of the PARAMETER_VARIATION type with given Inputs (the model and the version are identified by the Inputs).
createMonteCarloFirstOrder(inputs) Creates and returns a ModelRun object of the MONTE_CARLO type with given Inputs (the model and the version are identified by the Inputs).
startAnimation(inputs, divId) Starts an animated model run with given Inputs (which fully identify the model and the model version), and embeds the animation into the HTML element with the given id. Constructs and returns the corresponding Animation object by promise.

Inputs class

An object of the Inputs class is constructed in preparation of a model run of any kind by calling the CloudClient functions: createDefaultInputs() or createInputsFromExperiment(). This object contains full information about the model, model version, and the input values.

Do not mistake this class for the inputs field of the Version object.
Function Description
getInput(name) Returns the value (an object) of the input with a given name. See Data conversion for possible types.
setInput(name, value) Sets the value of the input with a given name.
setRangeInput(name, min, max, step) Sets a range for the input with a given name (in a parameter variation experiment).
setNumberOfReplications(num) Sets a number of replications for the Monte-Carlo 1st order experiment. The default value is 3.

Inputs of distribution type (for Monte Carlo 2nd order experiments) are coming in future releases of the AnyLogic Cloud API.

SingleRunOutputs class

An object of this class is returned after a call of getOutputs() or getOutputsAndRunIfAbsent() of a ModelRun constructed for a single run simulation experiment.

Function Description
names() Returns the array with all output names.
findNameIncluding(namePart) Searches for an output name that has namePart as a substring and returns it. If there is no such name or more than one such name is found, throws Error. This function is useful because full names of the outputs may be complex: see Outputs object.
value(name) Returns the value of the output with a given name. The type of value depends on the output: see Data conversion.
getRawOutputs() Returns an array of all output items. Each item has the following fields: name, type, units, value.
For possible values of the type and units fields, see Output types and Units sections correspondingly.
The value field contains an object constructed as described in Data conversion.
Example
This is an example of raw outputs:
[
    {
        name: "Queue size stats",
        type: "STATISTICS_CONTINUOUS",
        units: null,
        value: {
            count: 1255584,
            max: 7,
            mean: 0.9988466028875719,
            min: 0,
            totalTime: 999999.2699032243,
            type: "CONTINUOUS",
            variance: 0.0027334062484944965
        }
    },
    {
        name: "Total time in system|Total time in system",
        type: "HISTOGRAM_DATA",
        units: null,
        value: {
            hits: (20) [800912, 159870, 29594, 5804, 3399, 1073, 257, 56, 12, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            hitsOutHigh: 0,
            hitsOutLow: 0,
            intervalWidth: 1.6,
            lowerBound: 1.6,
            statistics: {
                count: 1000987,
                max: 18.533258249757637,
                mean: 2.5012383408878067,
                min: 1.6001570096705109,
                type: "DISCRETE",
                variance: 1.2835661096259896
            }
        }
    },
    {
          name: "Utilization|Server utilization",
          type: "DOUBLE",
          units: null,
          value: 0.31275860811685163,
    },
    {
          name: "Mean queue size|Mean queue size",
          type: "DOUBLE",
          units: null,
          value: 0.9988466025848514
    }
]

MultiRunOutputs class

An object of this class is returned after a call of the getOutputs() or getOutputsAndRunIfAbsent() functions of a ModelRun constructed for a parameter variation or another multi-run experiment. It simplifies navigation within the complex outputs structure.

You need to specify the required outputs explicitly when calling the getOutputs() functions.
Function Description
getInputNames() Returns the array of names of inputs that are varied across runs.
getOutputNames() Returns the array of names of requested outputs.
getValuesOfInput(name) Returns the array of values of the input with a given name across all runs in some fixed sequence. You can only query values of the varied inputs. The fixed inputs are not stored in MultiRunOutputs.
getValuesOfOutput(name) Returns the array of values of the output with a given name across all runs in some fixed sequence. This function can be used together with getValuesOfInput().
getRawData() returns a table (a two-dimensional array) with values of all variable inputs and all outputs with a header row.
For example, the following is the raw data of a parameter variation experiment with one variable parameter Mean service time and one scalar output Utilization|Server utilization:
[["Mean service time", "Utilization|Server utilization"], [1.8, 0.5621987153519676], [1.9, 0.5939408971748594], [2, 0.6253419155200399]]

ModelRun class

The ModelRun class is responsible for communication with and control of a model run (without animation) executed in Cloud. Basically, it as a front-end mirror of a back-end experiment run.

Objects of the ModelRun class are created and returned by calling the createSimulation(), createParameterVariation(), or createMonteCarloFirstOrder() functions of CloudClient. A ModelRun object contains full information about the model, version, inputs, and experiment type.

Function Description
run() Requests to run the experiment. Whether or not the simulation will actually be executed depends on the availability of the outputs. The function returns the same ModelRun object by promise once the HTTP request completes; it does not wait for simulation completion or outputs availability and does no polling.
stop() Requests to stop the model execution. Returns the same ModelRun object by promise once the HTTP request completes.
waitForCompletion(pollingPeriod) Waits for the experiment to complete and returns the same ModelRun object by promise. The pollingPeriod parameter is optional. The default value: 5000ms.
getStatus() Returns the status of the model execution as last updated by polling (does not initiate any extra communication with the server). Possible values are the same as described in the Experiment Run State section.
getProgress() Returns the fully parsed message field of the Experiment Run State object by promise. To find out total experiment progress use getProgress().then( progress => progress.total).
getOutputs(requiredOutputNames) If the run has already been completed, returns the run outputs (either the SingleRunOutputs or MultiRunOutputs object) by promise, otherwise throws 404 Error. requiredOutputNames is the array of output names that are to be returned.
If requiredOutputNames is omitted, the behavior is different for single and multi-run experiments: for a single run, all outputs are returned, for a multi run only outputs of scalar types are returned.
getOutputsAndRunIfAbsent(requiredOutputNames, pollingPeriod) If the run has already been completed, returns the run outputs (either the SingleRunOutputs or MultiRunOutputs object) by promise, otherwise requests to run the experiment, waits for completion by polling, and then returns the outputs by promise. requiredOutputNames has the same meaning as in getOutputs(). The pollingPeriod parameter is optional. The default value: 5000ms.

Animation class

The Animation class is responsible for communication with and control of an animated simulation run. An object of the Animation class is created and returned by calling the startAnimation() function of CloudClient.

Function Description
stop() Requests to stop the model execution. Does not return anything.
pause() Requests to pause the model execution. Returns the same Animation object by promise.
resume() Requests to resume the (paused) model execution. Returns the same Animation object by promise.
setSpeed(speed) Requests to set the execution speed of the model to a given value (model time units per real second). Returns the same Animation object by promise.
setVirtualTime() Requests to switch to the virtual time (fastest possible) execution mode. Returns the same Animation object by promise.
navigateTo(viewArea) Requests to navigate to a view area with the given name. Returns the same Animation object by promise.
setPresentable(pathToPresentable) Requests to navigate to a given agent. The path should start with experiment.root followed by Java path to the agent (if that is not root). Returns the same Animation object by promise.
setValue(pathToField, value) Requests to set the value of a given object in the model (for example, a parameter or variable). The path to the object should look like experiment.root.agent1.myParameter. Returns the same Animation object by promise.
getValue(pathToField) Requests the value of a given object in the model and returns it by promise. The path to the object should look like experiment.root.agent1.myParameter. The returned value is in JSON: see Data conversion section to learn how Java objects are mapped to JSON objects.
getState() Requests the state of the animated model being executed and returns it by promise. Please do not confuse the state of animated model with the status of the non-animated run that is returned by ModelRun.getStatus().
The returned value will be one of the following:
"IDLE"
"RUNNING"
"PAUSED"
"FINISHED"
"ERROR"
"PLEASE_WAIT"
"INITIALIZE"
"STOPPED"
"TIME_LIMIT"
"TERMINATED"
callFunction(pathToFunction, args) Requests to call a function in the model, possibly providing arguments. Returns what the function returns, by promise. The path to the function should look like experiment.root.agent1.myFunction.
args is a JavaScript array of the function argument objects. See Data conversion section to learn how Java objects are mapped to JSON objects.
waitForCompletion() Waits for animated simulation to complete and returns the same Animation object by promise.
Even if the simulation run has finished (has reached the stop time), it will not be considered as completed until the user clicks Stop, or stop() method has been called, or the server terminates the simulation due to an error.

Writing HTML and CSS for AnyLogic Cloud

If you are going to embed model animation in your web page, make sure there is no CSS style and HTML id intersection between your part of the page and embedded AnyLogic animation part.

Download the example CSS for model animation: presentation-html.css and presentation-svg.css files , and the HTML IDs in the svg-template.html file.

2D animation uses numeric IDs for SVG elements, so don’t use numeric IDs in the rest of the page.

Examples

Simulation run without animation (minimalistic)

In this example we run a simulation experiment without animation. First, we find the model and its latest version. Next, we specify the input parameters of the model. If simulation with such inputs has already been completed (and the outputs are stored in Cloud), the outputs are simply transferred to the web front-end. Otherwise, simulation is run in the fast mode without animation. The outputs in this case are generated and delivered to the front-end.

To do it, use the ModelRun.getOutputsAndRunIfAbsent() function.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Run simulation. Minimalistic</title>
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
    <script src="js/main.js"></script>
</head>
<body>
    <button id="run-button" onclick="runSimulation()">Run simulation</button>
    <div id="info">The simulation results will be displayed here</div>
</body>
</html>
JavaScript
let cloudClient = create("e05a6efa-ea5f-4adf-b090-ae0ca7d16c20");

let runButton;
let info;
let inputs;

window.onload = () => {
    runButton = document.getElementById("run-button");
    info = document.getElementById("info");
};

function runSimulation() {
    runButton.disabled = true;
    cloudClient.getLatestModelVersion( "Service System Demo" )
        .then( version => {
            inputs = cloudClient.createDefaultInputs( version );
            inputs.setInput( "Server capacity", 8 );
            let simulation = cloudClient.createSimulation(inputs);
            info.innerHTML = "Getting outputs, running simulation if absent...";
            return simulation.getOutputsAndRunIfAbsent();
        })
        .then( outputs => {
            let html = "For Server Capacity = " + inputs.getInput( "Server capacity" ) + ":<br>";
            html += "Mean queue size = " + outputs.value( "Mean queue size|Mean queue size" ) + "<br>";
            html += "Server utilization = " + outputs.value( "Utilization|Server utilization" ) + "<br>";
            info.innerHTML = html;
        })
        .catch( error => {
            info.innerHTML = error.status + "<br>" + error.message;
            console.error( error );
        })
        .finally( () => {
            runButton.disabled = false;
        });
}

The AnyLogic Cloud API JavaScript client is in the HTML as described above installation guide. There is also a button and a div element to display the simulation results.

As the web page and the scripts get loaded, a CloudClient object is created, given the API key. The second optional parameter of the CloudClient.create() static function is the Cloud URL. It is omitted here, so the public version AnyLogic Cloud is assumed.

When the user clicks Run, the runSimulation() function is called. It disables the button and initiates a chain of client-server communication using the JavaScript promise:

  1. The server is asked to find the latest version of the model named Service System Demo.
  2. When (and if) such model and version is found (the first then), we create the Inputs object with default input values. And in those inputs, we change the value of Server capacity parameter to 8. We ask the CloudClient to create a simulation object with the inputs. This is a purely front-end operation, there is no communication with the server. This code block finishes with the call of getOutputsAndRunIfAbsent(), which checks the simulation status: if such simulation has been completed it gets the outputs. If it hasn’t been run yet, it runs the simulation and waits for the outputs. The SingleRunOutputs object is returned by promise.
  3. When the outputs are delivered, we display some output values and the corresponding input using the API of SingleRunOutputs and Inputs. That’s it. If no error occurs during that sequence of operations, we proceed directly to the finally block, where we re-enable the Run button.
  4. If an error occurs during any step, the catch block is invoked. Here we display the error message and copy it to console. Then the finally block is executed.
    To get the value of a particular output, we need to specify its name exactly as it is constructed when the model is uploaded to Cloud (for more information refer to the section on the Output object. The case does not matter: you can use either lower case or upper case.

The function runSimulation() completes immediately, and the code fragments written in the then, catch, and finally blocks are invoked later when the corresponding events occur.

Querying simulation results of a completed run

As you know, AnyLogic Cloud stores the input/output pairs of all the ever-completed simulation runs. In this example, we will show you how to query the outputs of a completed model run (which is identified by the inputs). If the results exist, they will be displayed, otherwise, we will display the corresponding message instead of running the simulation as we did in the previous example.

The key function used here is ModelRun.getOutputs().

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Query outputs of a completed run</title>
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
    <script src="js/main.js"></script>
</head>
<body>
    Server capacity: <span id="parameter-value">5</span>
    <input id="parameter-range" type="range" min="2" max="10" step="1" value="5" onchange="changeValueText()">
    <button id="query-button" onclick="queryOutputs()">Query outputs</button>
    <div id="info">The simulation results will be displayed here</div>
</body>
</html>
JavaScript
let cloudClient = create("e05a6efa-ea5f-4adf-b090-ae0ca7d16c20");

let parameterValue;
let parameterRange;
let queryButton;
let info;

window.onload = () => {
    parameterValue = document.getElementById("parameter-value");
    parameterRange = document.getElementById("parameter-range");
    queryButton = document.getElementById("query-button");
    info = document.getElementById("info");
};

function queryOutputs() {
    parameterRange.disabled = true;
    queryButton.disabled = true;
    cloudClient.getModelByName("Service System Demo")
        .then( model => cloudClient.getLatestModelVersion(model) )
        .then( version => {
            let inputs = cloudClient.createDefaultInputs(version);
            inputs.setInput( "Server capacity", parameterRange.value);
            let simulation = cloudClient.createSimulation(inputs);
            info.innerHTML = "Getting outputs...";
            return simulation.getOutputs();
        })
        .then( outputs => {
            let html = "Mean queue size = " + outputs.value(outputs.findNameIncluding("Mean Queue Size")) + "<br>";
            html += "Server utilization = " + outputs.value(outputs.findNameIncluding("Server utilization")) + "<br>";
            info.innerHTML = html;
        })
        .catch( error => {
            info.innerHTML = error.status + "<br>" + error.message;
            console.error(error);
        })
        .finally( () => {
            parameterRange.disabled = false;
            queryButton.disabled = false;
        });
}

function changeValueText() {
    parameterValue.innerText = parameterRange.value;
}

In the HTML code there is a range input element that we use to set the value of the Server capacity. Having constructed the inputs and the simulation objects, we call simulation.getOutputs(). If the outputs exist, they are returned by promise and we get into the following then block. If there are no outputs, an error occurs, we process to the catch block and display the error. However, the same catch block is executed if any other type of error occurs. If you want to invoke a specific handler in case of absent results, you can modify the code in the following way:

...
return simulation.getOutputs()
    .catch( error => {
        if( error.status == 404 ) {
            //custom handler code
            console.error("Simulation results not found for these inputs");
        }
        throw new Error("Outputs not found for Server Capacity = " + parameterRange.value);
    });
...

Simulation run with progress polling

In case simulation is run without animation it makes sense to have some progress indication.

In this example, we use the ModelRun.getProgress() function to obtain the progress of the running simulation. Ther is also a different way of running the simulation and getting results: the sequence of the run(), waitForCompletion(), and getOutputs() function calls.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Simulation with Progress indication</title>
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
    <script src="js/main.js"></script>
</head>
<body>
    <button id="run-button" onclick="runSimulation()">Run simulation</button>
    <progress id="progress" value="0" max="100"></progress>
    <div id="info">The simulation results will be displayed here</div>
</body>
</html>
JavaScript
let cloudClient = create("e05a6efa-ea5f-4adf-b090-ae0ca7d16c20");

let runButton;
let progress;
let info;
let simulation;

window.onload = () => {
    runButton = document.getElementById( "run-button" );
    progress = document.getElementById( "progress" );
    info = document.getElementById( "info" );
};

function runSimulation() {
    runButton.disabled = true;
    cloudClient.getLatestModelVersion( "Service System Demo" )
        .then( version => {
            let inputs = cloudClient.createDefaultInputs( version );
            inputs.setInput( "Server capacity", 21 );
            inputs.setInput( "{STOP_TIME}", 10000000 );
            simulation = cloudClient.createSimulation(inputs);
            startPolling();
            return simulation.run();
        })
        .then( simulation => simulation.waitForCompletion() )
        .then( simulation => simulation.getOutputs() )
        .then( outputs => {
            let html = "Mean queue size = " + outputs.value( "Mean queue size|Mean queue size" ) + "<br>";
            html += "Server utilization = " + outputs.value( "Utilization|Server utilization" ) + "<br>";
            info.innerHTML = html;
        })
        .catch( error => {
            info.innerHTML = error.status + "<br>" + error.message;
            console.error( error );
        })
        .finally( () => {
            stopPolling();
            runButton.disabled = false;
        });
}

let pollingInterval;

function startPolling() {
    pollingInterval = setInterval(
        () => {
            simulation.getProgress()
                .then( progressinfo => {
                    if( progressinfo ) { //can be undefined in the beginning
                        progress.value = progressinfo.total;
                    }
                });
        },
        1000
    );
}

function stopPolling() {
    setTimeout( () => clearInterval( pollingInterval ), 2000 );
}

During the standard construction of the inputs, we change the system input {STOP_TIME} to a bigger value to make the simulation run longer, so we can really see the progress bar moving. Such input exists in every model uploaded to Cloud. However, if a run with the same stop time and other inputs has been performed already, the simulation won’t run, and the outputs will be delivered almost instantly. So, to watch the progress moving, consider setting other values for the inputs.

The startPolling() functions is called just before simulation.run(), and initiates a repeated call of simulation.getProgress() with one second interval. The function getProgress() returns information about the progress by promise. For a simple simulation run, we are only interested in the total field. For multi-run experiments it contains more details.

The result may also be an empty object if we poll too soon (while the run has not yet been created on the server), therefore, we need to check the existence of the polling result. The function stopPolling() is called once the simulation results have been delivered (or an error occurs). It clears the one second interval but waits for additional two seconds to make sure that the final progress value has been received.

The call of simulation.run() initiates the simulation run (only if such run has not been done before) and returns the same simulation object by promise. It does not wait for the simulation to complete. To wait for the experiment to complete, we use the simulation.waitForCompletion() function, which also returns the same object. It is only now that we query the simulation results by calling simulation.getOutputs(). This sequence is here solely for demo purposes. It could be replaced by a single call of simulation.getOutputsAndRunIfAbsent().

Running a custom workflow

This example implements a custom workflow where some simulations are run in parallel and some — sequentially, based on the results of other simulations. This scenario may be useful for those who want to set up, for example, a custom optimization algorithm in AnyLogic Cloud.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Custom workflow</title>
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
    <script src="js/main.js"></script>
</head>
<body>
    <button id="run-button"
onclick="runSimulations()">Run 2+1 simulations</button>
    <div id="info">The simulation results will be displayed here</div>
</body>
</html>
JavaScript
let cloudClient = create("e05a6efa-ea5f-4adf-b090-ae0ca7d16c20");

let version;
let runButton;
let info;

window.onload = () => {
    runButton = document.getElementById( "run-button" );
    info = document.getElementById( "info" );
};

let sc1 = 4;
let sc2 = 9;
let sc3;

function runSimulations() {
    runButton.disabled = true;
    cloudClient.getLatestModelVersion( "Service System Demo" )
        .then( v => {
            version = v;
            //create two inputs instances with different parameter values
            let inputs1 = cloudClient.createDefaultInputs( version );
            let inputs2 = cloudClient.createDefaultInputs( version );
            inputs1.setInput( "Server capacity", sc1 );
            inputs2.setInput( "Server capacity", sc2 );
            //create two simulation objects with different inputs
            let simulation1 = cloudClient.createSimulation( inputs1 );
            let simulation2 = cloudClient.createSimulation( inputs2 );
            info.innerHTML = "Running two parallel simulations...<br>";
            //run the two simulations in parallel, wait for both results
            return Promise.all( [simulation1.getOutputsAndRunIfAbsent(), simulation2.getOutputsAndRunIfAbsent()] );
        })
        .then( outputarray => {
            //both outputs are here
            let su1 = outputarray[0].value( "Utilization|Server utilization" );
            let su2 = outputarray[1].value( "Utilization|Server utilization" );
            let html = "Two parallel simulations completed.<br>";
            html += "With " + sc1 + " servers, Server utilization = " + su1 + "<br>";
            html += "With " + sc2 + " servers, Server utilization = " + su2 + "<br>";
            //based on the results, set up the inputs for the third simulation
            sc3 = (su1 + su2) > 0.4 ? sc2-1 : sc1+1;
            let inputs3 = cloudClient.createDefaultInputs( version );
            inputs3.setInput( "Server capacity", sc3 );
            let simulation3 = cloudClient.createSimulation( inputs3 );
            html += "Running third simulation with " + sc3 + " servers...<br>";
            info.innerHTML += html
            return simulation3.getOutputsAndRunIfAbsent();
        })
        .then( outputs3 => {
            let su3 = outputs3.value( "Utilization|Server utilization" );
            let html = "Third simulation completed.<br>";
            html += "With " + sc3 + " servers, Server utilization = " + su3 + "<br>";
            info.innerHTML += html
        })
        .catch( error => {
            info.innerHTML = error.status + "<br>" + error.message;
            console.error( error );
        })
        .finally( () => {
            runButton.disabled = false;
        });
}

In this scenario, we first perform two simulation runs in parallel with the Server Capacity parameter set to 4 in one run and 9 in another. The two runs are initiated by calling simulation1.getOutputsAndRunIfAbsent() and simulation2.getOutputsAndRunIfAbsent(). The function getOutputsAndRunIfAbsent() (as all functions in AnyLogic Cloud JavaScript API that involve communication with the server) is asynchronous — it immediately returns a promise, and then we need to provide the handler in the then block. In this case we have two promises from the two runs — and we need to wait for both to complete. This is accomplished by using the Promise.all() function returning a single promise, which resolves after all the promises (passed as an iterable array in our case) have resolved. The outputs of simulation runs performed in parallel are returned also as an array in the same order. In the then block that follows, we analyze the values of Server Utilization in both runs and set up the input of the third simulation run based on those values.

When you run multiple simulations in parallel manually, like in this example, each call of ModelRun.getOutputsAndRunIfAbsent() or ModelRun.waitForCompletion() performs its own independent polling. Many parallel runs may result in high HTTP traffic. If that is suspected, consider changing the optional parameter pollingPeriod of these functions.

Running parameter variation

This example shows how to run a parameter variation experiment. One of the input parameters will be varied in range. To demonstrate one more feature of the AnyLogic Cloud API, we will take the input values from an existing simulation experiment defined in the standard AnyLogic Cloud web interface and change a parameter value from a scalar to a range.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Parallel Simulations</title>
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
    <script src="js/main.js"></script>
</head>
<body>
    <button id="run-button" onclick="runVariation()">Run variation</button>
    <div id="info">The parameter variation results will be displayed here</div>
</body>
</html>
JavaScript
let cloudClient = create("e05a6efa-ea5f-4adf-b090-ae0ca7d16c20");

let runButton;
let info;

window.onload = () => {
    runButton = document.getElementById( "run-button" );
    info = document.getElementById( "info" );
};

function runVariation() {
    runButton.disabled = true;
    cloudClient.getLatestModelVersion( "Service System Demo" )
        .then( version => {
            return cloudClient.createInputsFromExperiment( version, "Baseline" );
        })
        .then( inputs => {
            inputs.setRangeInput( "Mean service time", 1.8, 2, 0.1 );
            let variation = cloudClient.createParameterVariation( inputs );
            info.innerHTML = "Running parameter variation...<br>";
            return variation.getOutputsAndRunIfAbsent( ["Total time in system|Total time in system"] );
        })
        .then( outputs => {
            let html = "Parameter variation completed.<br>";
            let invalues = outputs.getValuesOfInput("Mean service time");
            let outvalues = outputs.getValuesOfOutput("Total time in system|Total time in system");
            for( let i=0; i<invalues.length; i++ ) {
                html += "When Mean service time = " + invalues[i] +
                        ", mean Total time in system = " + outvalues[i].statistics.mean + "<br>"
            }
            info.innerHTML += html
        })
        .catch( error => {
            info.innerHTML = error.status + "<br>" + error.message;
            console.error( error );
        })
        .finally( () => {
            runButton.disabled = false;
        });
}

To reuse the set of inputs from an experiment defined in the AnyLogic Cloud web interface we use the function CloudClient.createInputsFromExperiment() instead of createDefaultInputs(). Having copied the inputs of the "Baseline" experiment, we change the input "Mean service time" to a range type (from 1.8 to 2.0 with step 0.1) by calling Inputs.setRangeInput(). This means three simulation runs will be performed.

Then we need to create a ModelRun object of the parameter variation type. To do this, we execute the CloudClient.createParameterVariation() function.

The run of parameter variation is invoked by calling the getOutputsAndRunIfAbsent() function just like in our previous examples, but there is one important difference. Full outputs of a multiple run experiment may be a very large piece of data, so the API user has to explicitly specify which outputs need to be delivered. This is done by listing the output names in the array passed to getOutputsAndRunIfAbsent() or getOutputs() as a parameter. In this example, we are interested in the "Total time in system|Total time in system" output. If the parameter is omitted for a multi run experiment, only scalar outputs will be returned, if any.

Outputs of a multi run experiment are returned as a MultiRunOutputs object, which has a number of functions simplifying navigation. In this simple example we get array of the input values and the corresponding array of the output values, and display them.

Running the Monte Carlo 1st order experiment

In this example, we will run a Monte Carlo 1st order experiment on a specified model with the explicitly set number of replications. We will use the input values from an existing simulation experiment defined in the standard AnyLogic Cloud web interface.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Monte Carlo 1st order</title>
    <script src="https://cloud.anylogic.com/assets/js-client-1.1/cloud-client.js"></script>
    <script src="js/main.monte_carlo_first.js"></script>
</head>
<body>
    <p>
      Enter the name of the model:
    </p>
    <p>
      <input type="text" id="model-name" placeholder="Model Name" value="Service System Demo" />
    </p>
    <p>
      Set the number of replications:
    </p>
    <p>
      <input type="number" id="number-of-replications" placeholder="Number of replications" value="3"/>
    </p>
    <p>
      <button id="run-button" onclick="runMonteCarlo()">Run MonteCarlo</button>
    </p>
    <div id="info">The Monte Carlo 1<sup>st</sup> order results will be displayed here</div>
</body>
</html>
JavaScript
let cloudClient = CloudClient.create( "e05a6efa-ea5f-4adf-b090-ae0ca7d16c20" );

let runButton;
let runButton2;
let info;
let modelName;
let numberOfReplications;

window.onload = () => {
  modelName = document.getElementById( "model-name" );
  runButton = document.getElementById( "run-button" );
  runButton2 = document.getElementById( "run-button2" );
  info = document.getElementById( "info" );
  numberOfReplications = document.getElementById("number-of-replications");
};

function runMonteCarlo() {
    runButton.disabled = true;
    cloudClient.getLatestModelVersion( modelName.value )
        .then( version => {
            return cloudClient.createInputsFromExperiment( version, "Baseline" );
        })
        .then( inputs => {
            inputs.setInput( "Mean service time", 2);
            inputs.setNumberOfReplications(numberOfReplications.value);
            let monteCarlo = cloudClient.createMonteCarloFirstOrder( inputs );
            info.innerHTML = "Running Monte Carlo 1st order...<br>";
            //for a multi run, we have to explicitly name the outputs of interest
            return monteCarlo.getOutputsAndRunIfAbsent(  );
        })
        .then( outputs => {
            let html = "Monte Carlo 1st order completed.<br>";
            let outvalues = outputs.getValuesOfOutput("Mean queue size|Mean queue size");
            
            html += "Mean queue size = " + outvalues.statistics.mean + "<br>"
           
            info.innerHTML += html
        })
        .catch( error => {
            info.innerHTML = error.status + "<br>" + error.message;
            console.error( error );
        })
        .finally( () => {
            runButton.disabled = false;
        });
}

In the HTML code, there are two fields: in the first one, you specify the name of the model at the public version of Cloud, and in the second, the number of replications for the Monte Carlo 1st order experiment.

After specifying the necessary information, click the button to execute the experiment.

Cloud runs the experiment with the number of replications provided. Outputs of a multi-run experiment are returned as a MultiRunOutputs object, which has a number of functions simplifying navigation — like in the example, where we use the getValuesOfOutput() function to request the mean value of the specified output, which is then displayed in the browser window.

Simulation run with animation (minimalistic)

This example demonstrates how to embed the animation of a cloud-based model into a web page.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Run animation. Minimalistic</title>
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
    <script src="js/main.js"></script>
</head>
<body>
    <button id="run-button" onclick="runAnimation()">Run animation</button>
    <div id="animation-container" style="width: 1200px; height: 700px; border: 1px solid blue;">
    </div>
</body>
</html>
JavaScript
let cloudClient = create("e05a6efa-ea5f-4adf-b090-ae0ca7d16c20");

function runAnimation() {
    runButton = document.getElementById( "run-button" );
    runButton.disabled = true;
    cloudClient.getLatestModelVersion( "Bass Diffusion Demo 8.5.0" )
        .then( version => {
            let inputs = cloudClient.createDefaultInputs( version );
            inputs.setInput( "Contact Rate", 30 );
            return cloudClient.startAnimation( inputs, "animation-container" );
        })
        .then( animation => {
            return animation.waitForCompletion();
        })
        .catch( error => {
            console.error( error );
        })
        .finally( () => {
            runButton.disabled = false;
        });
}

Within the HTML code you should create the animation placeholder. In this example it is the initially empty 1200x700 <div> element, with the "animation-container" ID. Then, when the user presses the Run animation button, the function runAnimation() is called. It disables the button, searches for the latest version of the model, creates a default input set, and changes one of the input values. The call of cloudClient.startAnimation() launches the model, connects the model to the frontend, and starts streaming animation into the provided container. That call completes as the HTTP request completes and returns the Animation object by promise. Then we use animation.waitForCompletion() to wait for the user to press Stop.

Even if the animated simulation finishes (for example, reaches its stop time), it is not considered as stopped until either of the following:
  • The user presses the Stop button on the animation control panel
  • The server decides to stop it for any reason
The finally block is then executed and re-enables the Run button.

Animated run with external pause and resume buttons and state polling

This example contains buttons to pause and resume the simulation. The key functions of the Animation object used here are pause(), resume(), and getState().

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Animation. Pause Resume</title>
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
    <script src="js/main.js"></script>
</head>
<body>
    <button id="run-button" onclick="runAnimation()">Run animation</button>
    <button id="pause-button" onclick="pauseAnimation()" disabled>Pause</button>
    <button id="resume-button" onclick="resumeAnimation()" disabled>Resume</button>
    <div id="animation-container" style="width: 1200px; height: 700px; border: 1px solid blue;">
    </div>
</body>
</html>
JavaScript
let cloudClient = create("e05a6efa-ea5f-4adf-b090-ae0ca7d16c20");

let runButton;
let pauseButton;
let resumeButton;
let animation;

window.onload = () => {
    runButton = document.getElementById( "run-button" );
    pauseButton = document.getElementById( "pause-button" );
    resumeButton = document.getElementById( "resume-button" );
};

function runAnimation() {
    runButton.disabled = true;
    cloudClient.getModelByName( "Bass Diffusion Demo 8.5.0" )
        .then( model => cloudClient.getLatestModelVersion( model ) )
        .then( version => {
            let inputs = cloudClient.createDefaultInputs( version );
            inputs.setInput( "Contact Rate", 40 );
            return cloudClient.startAnimation( inputs, "animation-container" );
        })
        .then( a => {
            animation = a;
            startPolling();
            return animation.waitForCompletion();
        })
        .catch( error => {
            console.error( error );
        })
        .finally( () => {
            stopPolling();
            runButton.disabled = false;
            pauseButton.disabled = true;
            resumeButton.disabled = true;
        });
}

function pauseAnimation() {
    pauseButton.disabled = true;
    animation.pause();
}

function resumeAnimation() {
    resumeButton.disabled = true;
    animation.resume();
}

let pollingInterval;

function startPolling() {
    pollingInterval = setInterval(
        () => {
            animation.getState()
                .then( state => {
                    pauseButton.disabled = state != "RUNNING";
                    resumeButton.disabled = state != "PAUSED";
                });
        },
        500
    );
}

function stopPolling() {
    clearInterval( pollingInterval );
}

In the HTML code, there are two new buttons above the animation container that are initially disabled. In JavaScript, we remember these elements as the page gets loaded. We also remember the Animation object returned by cloudClient.startAnimation() by promise. The actions assigned to the buttons simply call pause() and resume() of that object. Once the animation starts, we begin to poll its state every 0.5 second, see the function startPolling(). A call of getState() returns the animation state by promise, and we use it to enable or disable the buttons. The polling is stopped once the model is stopped or an error occurs, see the finally block.

In animated run, the inputs are not validated until they are passed to the model. Therefore, if there is an error in the input values (such as a type error), the model will fail to start and you will see an error message within the animation window, but the catch block won’t be reached.

Also, in this example we show a different, more general, way of getting to the latest version of the model: first, we get the Model object by its name, then we ask for the latest version of the model.

Animated run with external control of a model parameter

This example shows how to control a model input parameter while the model is running using the AnyLogic Cloud API and how to call a function defined in the model. We will run the model with the default input parameter value and provide external HTML controls to change it. The key functions used here are setValue() and callFunction().

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Animation. Parameter Control</title>
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
    <script src="js/main.js"></script>
</head>
<body>
    <button id="run-button" onclick="runAnimation()">Run animation</button>
    <input id="parameter-range" onchange="changeParameter()" type="range" min="0" max="100" disabled>
    <button id="reset-button" onclick="resetParameter()" disabled>Reset parameter</button>
    <div id="animation-container" style="width: 1200px; height: 700px; border: 1px solid blue;">
    </div>
</body>
</html>
JavaScript
let cloudClient = create("e05a6efa-ea5f-4adf-b090-ae0ca7d16c20");

let runButton;
let parameterRange;
let resetButton;
let animation;

window.onload = () => {
    runButton = document.getElementById( "run-button" );
    parameterRange = document.getElementById( "parameter-range" );
    resetButton = document.getElementById( "reset-button" );
};

function runAnimation() {
    runButton.disabled = true;
    cloudClient.getLatestModelVersion( "Bass Diffusion Demo 8.5.0" )
        .then( version => {
            let inputs = cloudClient.createDefaultInputs( version );
            return cloudClient.startAnimation( inputs, "animation-container" );
        })
        .then( a => {
            animation = a;
            parameterRange.disabled = false;
            resetButton.disabled = false;
            return animation.waitForCompletion();
        })
        .catch( error => {
            console.error( error );
        })
        .finally( () => {
            runButton.disabled = false;
            parameterRange.disabled = true;
            resetButton.disabled = true;
        });
}

function changeParameter() {
    animation.setValue( "experiment.root.ContactRate", parseFloat( parameterRange.value ) );
}

function resetParameter() {
    parameterRange.value = 30;
    animation.callFunction( "experiment.root.set_ContactRate", [30] );
}

In the HTML code, there is a slider and a button. Initially, these controls are disabled.

The changeParameter() function uses the AnyLogic Cloud API function setValue(), the first parameter of which is the path to the object whose value we wish to set.

The path should always start with the word "experiment" that is followed by "root" in most cases: experiment.root takes you to the root agent of the model. Typically this is the (only) instance of the class Main. As our Contact Rate parameter is located in Main, the full path to it is experiment.root.ContactRate.

We are using the original Java name of the parameter here and not its UI label Contact Rate.

Also, as the parameter is numeric, we need to convert parameterRange.value, which is a string, to a float number.

Similarly, the resetParameter() function uses the API function callFunction() to force the value of Contact Rate to 30. It is done by invoking the function set_ContactRate() defined in the model class Main (in this case, the function was auto-generated by AnyLogic, but it could be a user-defined function). The path to the function is obviously "experiment.root.set_ContactRate" and the arguments of the function call must be passed as a JavaScript array. In our case there is only one parameter — the new value 30. The functions defined in the model by the user are called in the exactly same way.

Retrieving information from a running animated model

In this example, we will retrieve information from a running model using the getValue() function of the Animation object.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <title>AnyLogic Cloud API Example - Animation. Get data</title>
    <script src="https://cloud.anylogic.com/assets/js-client-8.5.0/cloud-client.js"></script>
    <script src="js/main.get_data.js"></script>
</head>
<body>
    <button id="run-button" onclick="runAnimation()">Run animation</button>
    <button id="get-scalar-button" onclick="getScalarValue()" disabled>Get scalar value</button>
    <button id="get-dataset-button" onclick="getDataSet()" disabled>Get dataset</button>
    <div id="info">The data retrieved from the model will be displayed here</div>
    <div id="animation-container" style="width: 1200px; height: 700px; border: 1px solid blue;">
    </div>
</body>
</html>
JavaScript
let cloudClient = create("e05a6efa-ea5f-4adf-b090-ae0ca7d16c20");

let runButton;
let getScalarButton;
let getDataSetButton;
let info;
let animation;

window.onload = () => {
    runButton = document.getElementById( "run-button" );
    getScalarButton = document.getElementById( "get-scalar-button" );
    getDataSetButton = document.getElementById( "get-dataset-button" );
    info = document.getElementById( "info" );
};

function runAnimation() {
    runButton.disabled = true;
    cloudClient.getLatestModelVersion( "Bass Diffusion Demo 8.5.0" )
        .then( version => {
            let inputs = cloudClient.createDefaultInputs( version );
            return cloudClient.startAnimation( inputs, "animation-container" );
        })
        .then( a => {
            animation = a;
            getScalarButton.disabled = false;
            getDataSetButton.disabled = false;
            return animation.waitForCompletion();
        })
        .catch( error => {
            info.innerText = error.message;
            console.error( error );
        })
        .finally( () => {
            runButton.disabled = false;
            getScalarButton.disabled = true;
            getDataSetButton.disabled = true;
        });
}

function getScalarValue() {
    let value = animation.getValue( "experiment.root.Adopters" )
        .then( value => {
            info.innerText = "Adopters = " + value;
        });
}

function getDataSet() {
    let value = animation.getValue( "experiment.root._ds_Adopters" )
        .then( value => {
            info.innerText = "Adopters over time: time=[" + value.dataX + "] Adopters=[" + value.dataY + "]";
        });
}

In the HTML code there are two buttons: one for querying a scalar value, and another for querying a dataset (both buttons are initially disabled), and a <div> element to display the obtained information.

The first parameter of getValue() is the full path to the object being accessed, which starts with experiment.root (just like in setValue() and callFunction() in the previous example). The scalar value is the System Dynamics variable Adopters, and the dataset is the one automatically created for the same variable (in AnyLogic such datasets are auto-generated and have prefix _ds_. In case you have a user-defined dataset, you should obviously use that dataset’s name).

The structure of the object returned by getValue() depends on its type. For primitive types it is simple. To learn more about complex types, see Data conversion

In this example, the AnyLogic DataSet Java object is converted into a JSON object with dataX and dataY fields. Should we have a custom chart (like a Plotly or Tableau chart) as a part of our front-end, we could feed that data into the chart to visualize it, possibly with live updates of certain frequency.

How can we improve this article?