Automate a simulation with Python
In this section, we use Python to construct a simulation request, submit it, follow its progress, and download its results. The script contains no example model:
build_request is where you add your own request.
If your request already exists as JSON, use Automate a simulation with a shell script, or load that JSON from build_request as shown below.
Prepare Python
Download run_simulation.py into your project. Complete Set up API access so VANELLUS_API_KEY is available in your terminal. Ensure Requests is installed in the Python environment you use.
Check that the script is available:
python run_simulation.py --help
Add your request
Open run_simulation.py and find build_request:
def build_request() -> JsonObject:
"""Build and return the complete simulation request supplied by the user."""
# Replace this exception with the Python code that constructs your request.
raise NotImplementedError("Add your simulation request to build_request()")
Replace the exception with the functions and values that construct your complete simulation request. Keep model construction inside build_request or helpers called by it; the API and result-handling functions below it can then remain unchanged.
If you maintain the request as JSON, the same hook can load it instead:
def build_request() -> JsonObject:
"""Load and return the complete simulation request maintained as JSON."""
# Loading here gives the Python and shell workflows the same request source.
return json.loads(Path("request.json").read_text(encoding="utf-8"))
Set dry_run and solver controls in the request itself. Progress updates are recorded automatically and remain available from the progress stream. This keeps the submitted model explicit and makes the same request usable by other API tools.
Run the simulation
Run the completed script:
python run_simulation.py
Use --output for the parent result directory and --name for a specific folder:
python run_simulation.py --output results/design-tests --name baseline
The script refuses to submit when the named folder already contains files. Without --name, it writes to results/simulation-<id>.
The reusable workflow is contained in run_simulation:
def run_simulation(
request: JsonObject,
api_key: str,
output_root: Path,
output_name: str | None = None,
) -> Path:
"""Submit, follow, download, and return one simulation's result directory."""
# Reject an occupied custom folder before submitting a simulation that may use credits.
if output_name is not None:
planned_output = output_root / output_name
if planned_output.exists() and (not planned_output.is_dir() or any(planned_output.iterdir())):
raise FileExistsError(f"Output directory is not empty: {planned_output}")
simulation_id = submit_request(request, api_key=api_key)
status = wait_for_completion(api_key=api_key, simulation_id=simulation_id)
output_directory = output_root / (output_name or f"simulation-{simulation_id}")
download_results(
api_key=api_key,
simulation_id=simulation_id,
output_directory=output_directory,
)
# Preserve the exact client-side request alongside the server-normalized request.json.
submitted_path = output_directory / "submitted-request.json"
submitted_path.write_text(json.dumps(request, indent=2) + "\n", encoding="utf-8")
final_state = str(status["status"])
print(f"Simulation finished with status {final_state}")
if final_state == "max_iterations_reached":
print("Warning: inspect convergence before using this result")
elif final_state == "canceled":
print("Warning: the downloaded result is from a canceled simulation")
elif final_state not in DOWNLOADABLE_STATES:
raise RuntimeError(f"Simulation ended as {final_state}; diagnostic results were retained")
return output_directory
The function:
- Submits the dictionary returned by
build_request. - Prints API warnings and live residual updates.
- Polls the authoritative status endpoint.
- Requests graceful cancellation if you press Ctrl-C.
- Safely extracts the downloaded ZIP.
- Returns the result directory as a
Path.
The result folder also contains submitted-request.json, preserving the exact Python-built dictionary beside the server-normalized request.json.
dry_run, residual_converged, and monitor_converged are treated as normal downloadable outcomes. The client downloads max_iterations_reached and canceled results with a warning, while error, diverged, and killed stop without attempting a download.
Adapt it for repeated runs
Give build_request parameters for the values you want to vary, then call run_simulation in your own loop. Because each call returns its result directory, the surrounding program can retain or post-process each result without changing the API workflow.
Run simulations serially unless you intentionally want several credit-consuming jobs active at once. Give each run a distinct output_name.
See also
- Automate a simulation with a shell script runs a request that already exists as JSON.
- Monitor and control a running simulation gives individual lifecycle operations.
- Simulation status and result files explains terminal states and result availability.