Skip to content

Simulation lifecycle

This page follows a batch of configurations from the moment a front end submits it to the moment its results are on disk and in memory. It ties together the pieces described on the Core, Utils, Desktop and Server pages.

A front end assembles two inputs — a list of SimulationConfig (one per experiment) and a single BaseConfig (the shared evaluation parameters) — and calls submit. From there the engine takes over on its worker thread, and the front end only observes.

sequenceDiagram
  autonumber
  participant UI as Front end
  participant H as SimulationHandler
  participant W as simulation-worker
  participant Sim as Simulator (per mode)
  participant Plat as 3SIM / BSCM
  participant Disk as Disk

  UI->>H: submit(configs, baseConfig, mode)
  H->>H: SimulationInfo = QUEUED
  H-->>UI: simulationId
  H->>W: process(info)

  W->>H: ensureInitialized(mode)
  H->>Sim: initAnalysis() (once per mode)
  Sim->>Plat: standalone init

  loop each run in the group
    W->>W: resolve model path
    W->>Sim: runSimulation(config, base, simId, runId)
    Sim->>Plat: build parameters and run
    Plat-->>Sim: SimulationResult
    Sim->>Disk: write per-run JSON
    Sim-->>W: SimulationRun
    W->>H: append to results, run FINISHED
  end

  W->>H: group FINISHED or ERROR
  UI->>H: getSimulation(id) (polled)
  H-->>UI: SimulationInfo with results

The engine initialises the standalone Palladio environment only the first time a given mode is used; subsequent groups in the same mode reuse the cached, already-initialised simulator.

Both a group (SimulationInfo) and each run within it (RunInfo) carry a RunStatus. They move through the same states, but independently.

stateDiagram-v2
  [*] --> QUEUED
  QUEUED --> RUNNING
  RUNNING --> FINISHED
  RUNNING --> ERROR
  FINISHED --> [*]
  ERROR --> [*]

The relationship between the two levels:

  • A group starts QUEUED, becomes RUNNING when the worker picks it up, and ends FINISHED if every run finished, or ERROR if any run failed (hasErrors()), or if the whole group failed fatally.
  • A single run that throws is marked ERROR with its message recorded, and the worker continues with the next run. One failed run does not stop the others; it only affects the group’s final status.

Before each run executes, the worker decides which blockchain-system model to load, taking the first available of:

flowchart TB
  A["config.blockchainSystemModelFilePath"] -->|empty| B["baseConfig.blockchainSystemModelFilePath"]
  B -->|empty| C["modelPath passed to submit"]
  C -->|empty| D["testmodels/threesim-<config_id>/Net.blockchainsystem"]
  A -->|set| Use["use this path"]
  B -->|set| Use
  C -->|set| Use
  D --> Use

The final fallback is deterministic: it maps a configuration’s config_id onto its dedicated model folder, and fails fast if that folder is missing.

Each finished run yields two things:

  • A JSON file on disk: indiv_json/result_run_<simId>_<runId>.json for normal runs, or result_selfishmining/result_run_<simId>_<runId>.json for attack runs. The file is a fully typed SimulationRun — run metadata, the input SimulationConfig, the BaseConfig, the typed SimulationResult, and the timing and memory numbers.
  • An in-memory SimulationRun object, appended to SimulationInfo.results. This is what the server prints and what a front end can read directly once the group is finished, without re-reading the files.

Persistence and the in-memory result are independent: if the disk write fails, the run is still returned and collected in memory, so a run’s result is never silently lost.