Skip to main content

DandeLiion_logo

ci-testing documentation release OpenSSF Scorecard

DandeLiion client

The DandeLiion client is a client software written in Python to submit/run DandeLiion simulations on a remote server and to retrieve simulation results.

Installation

pip install dandeliion-client

NOTE: DandeLiion client 2.0 supports Python 3.10 through 3.13.

As of client version 1.1, pybamm is an optional dependency and will only be installed if you run:

pip install dandeliion-client[pybamm]

Prerequisites

Import DandeLiion client and all relevant packages such as numpy, matplotlib, and (optionally) PyBaMM to validate PyBaMM’s Experiment:

import dandeliion.client as dandeliion
import numpy as np
import matplotlib.pyplot as plt
import pybamm

The client version can be obtained using

dandeliion.__version__

Create an instance of dandeliion.client.Simulator, providing the API URL and API token:

api_url = "https://api.dandeliion.com"
api_key = "API_TOKEN"
simulator = dandeliion.Simulator(api_url, api_key)

Client 2.0 communicates exclusively with DandeLiion API v2. The service root, /api/v2, or the complete /api/v2/runs URL may be supplied. Legacy /v1 URLs are rejected.

Transport settings are keyword-only. The defaults use three total attempts, explicit connect/read timeouts, and polling backoff from one to ten seconds:

simulator = dandeliion.Simulator(
    api_url,
    api_key,
    request_timeout=(3.05, 30),
    result_timeout=(3.05, 300),
    poll_interval=1,
    max_poll_interval=10,
    max_attempts=3,
)

Every submission receives an idempotency key. The client generates one by default and reuses it for bounded transport retries, or the caller can provide a stable key:

solution = simulator.submit(
    parameters,
    is_blocking=False,
    idempotency_key="analysis-batch-0001",
)

If a response is lost, submitting the same JSON and key returns the original run without consuming a second Token Portal use. Reusing a key with different input raises DandeliionAPIException with code == "idempotency_conflict".

Token validation details

New simulation submissions expose the token validation performed by the DandeLiion API. After a successful submission, the returned solution provides the token status, UTC expiry, and post-submission shared usage balance:

solution = dandeliion.solve(
    simulator=simulator,
    params=params,
    experiment=experiment,
    is_blocking=False,
)

validation = solution.token_validation
if validation is not None:
    print(validation.status)
    print(validation.expires_at)
    print(validation.uses_remaining)

If a token is invalid, expired, deactivated, exhausted, or belongs to an inactive user, submission raises dandeliion.DandeliionTokenValidationError. Its validation property contains the same structured details:

try:
    solution = simulator.submit(parameters, is_blocking=False)
except dandeliion.DandeliionTokenValidationError as exc:
    print(exc.validation.status)
    print(exc.validation.expires_at)
    print(exc.validation.uses_remaining)

Other API failures remain DandeliionAPIException instances. They expose structured status_code, code, message, request_id, authorization_request_id, retry_after, and idempotency_key attributes. An authorization_unknown response is never automatically retried; provide its authorization_request_id to support for reconciliation.

Simulation parameters and models

Define the battery cell parameters by providing the BPX file name (as a string or pathlib.Path), a valid BPX as a dict, or BPX object itself. For example:

params = "example_bpx.json"

The API client will validate the BPX. Currently supported BPX version is 0.5.0.

Define a dictionary of extra parameters outside the BPX standard, such as mesh, initial state of charge or voltage, etc.:

extra_params = {}
extra_params["Mesh"] = {"x_n": 16, "x_s": 16, "x_p": 16, "r_n": 16, "r_p": 16}
extra_params["Initial SOC"] = 1.0
extra_params["Lumped thermal model"] = 1.0  # Sets lumped thermal model

List of all supported parameters (extra_params keys):

Parameter Description
'Mesh' Defines the number of mesh points in liquid and solid. Expects the following fields: x_n, x_s, x_p, r_n, r_p. Optional, all values default to 16. Cannot be less than 4. Mesh stretching factor: alpha (no implemented yet). See example above.
'Initial SOC' Defines the initial State of Charge of the cell (or pack, for the pack model). Should be in the interval [0, 1]. Default value is 1.0.
'Initial voltage [V]' Defines the initial voltage of the cell (or pack, for the pack model). Should be in the interval [V_min, V_max], where V_min and V_max are defined in the cell section of the BPX file. If both the initial SOC and initial voltage are provided, the initial SOC will be used and a warning will be produced.
'Time series input' Allows the user to define the input current or power as time series (see Drive cycles).
'Lumped thermal model' If this key is defined, the lumped thermal model will be used. The solver will read "Specific heat capacity [J.K-1.kg-1]", "Density [kg.m-3]", "External surface area [m2]", "Volume [m3]" from the cell section of the BPX file. In addition, "Heat transfer coefficient [W.m-2.K-1]" extra parameter should be defined (see below). Default model is isothermal.
'Heat transfer coefficient [W.m-2.K-1]' Heat transfer coefficient for the lumped thermal model. Cannot be negative.
'Module' Defines the battery pack geometry and pack related parameters.
'Safe mode' If defined, the solver will choose tighter convergence conditions, which may improve stability in some cases, but this also impacts performance.
'extra_resources' Set extra_resources to True if runtime is estimated to exceed 15 mins or memory required exceeds 3 GB (Default is False).

A note about the initial conditions

The user can define the initial conditions in the input BPX file:

  • The initial temperature of the cell is in the "Cell" section, "Initial temperature [K]" key.
  • The initial concentration in electrolyte is in the "Electrolyte" section, "Initial concentration in electrolyte [mol.m-3]" key.
  • The initial state of charge or initial voltage can be defined in the "User-defined" section, using "DandeLiion: Initial SOC" or "DandeLiion: Initial voltage [V]" keys. These keys will not invalidate the BPX file. Alternatively, "Initial SOC" or "Initial voltage [V]" can be added to the extra_params dictionary and passed to the solver.

Defining the model input

Define the experiment in PyBaMM’s format, for example:

experiment = dandeliion.Experiment(
    [
        (
            "Discharge at 1C until 3.8 V",
            "Hold at 3.8 V for 10 minutes (5 second period)",
            "Rest for 300 seconds",
            "Charge at 2000 mA for 1 hour or until 4.0 V",
            "Rest for 5 min",
            "Discharge at 20W for 25 minutes or until 2.5V",
            "Rest for 300 s (1 second period)",
            "Charge at C/2 until 4.0 V (0.5 min period)",
            "Hold at 4.0V for 0.1 hr (1s period)",
        )
    ]
    * 2,
    period="10 s",
)

If PyBaMM is installed, pybamm.experiment.Experiment will be used, which includes validation of the experiment. Otherwise, dandeliion.client.Experiment will be used, without validation.

Drive cycles

The client allows the user to define the input current (or power) as time series.

Consider we have a CSV file with the drive cycle, where the first column is the time (in seconds), and the second column is the current (in Amps) or power (in Watts). This file can be read to a numpy array using pandas package:

import pandas as pd

drive_cycle_data = pd.read_csv("US06.csv", comment="#", header=None).to_numpy()

# Negative current for discharge
drive_cycle = np.column_stack([drive_cycle_data[:, 0], -drive_cycle_data[:, 1]])

There are at least two ways of passing the time series to DandeLiion.

Method 1

Use this method if PyBaMM is not installed.

Define DandeLiion experiment with the "Time Series" instruction, for example:

experiment = dandeliion.Experiment(
    [
        ("Discharge at 1C until 3.8 V",),
        ("Time series",) * 3,
    ],
    period="10 s",
)

Here we discharge the cell until 3.8V first, and then apply the drive cycle 3 times.

Note that the period does not need to be defined, as the solver will automatically adjust the time step based on the time points in the time series. However, if the user needs to refine the time step for the time series specifically, it can be done within the time series instruction:

"Time series (1 s period)"

Finally, pass the time series to the client by using extra_params dictionary:

# Current vs time
extra_params["Time series input"] = {
    "Time [s]": drive_cycle[:, 0].tolist(),
    "Current [A]": drive_cycle[:, 1].tolist(),
}

or

# Power vs time
extra_params["Time series input"] = {
    "Time [s]": drive_cycle[:, 0].tolist(),
    "Power [W]": -drive_cycle[:, 1].tolist(),
}

Method 2

Use this method if PyBaMM is installed and you want to use PyBaMM’s experiment class (which performs basic validation of the experiment instructions).

Define time series in the PyBaMM experiment:

experiment = pybamm.Experiment(
    [
        ("Discharge at 1C until 3.8 V",),
        (pybamm.step.current(drive_cycle),) * 3,
    ],
    period="10 s",
)

Unlike method 1, there is no need to update the extra_params dictionary.

Starting the simulation

Start the simulation in the cloud using dandeliion.client.solve method:

solution = dandeliion.solve(
    simulator=simulator, params=params, experiment=experiment, extra_params=extra_params, is_blocking=True
)

The solve method parameters:

  • simulator is the dandeliion.client.Simulator object that stores the API key and URL, required.
  • params refers to the BPX file (or dict or object), required. See Simulation parameters and models.
  • experiment is optional (”1C discharge for 1 hour or until the "Lower voltage cut-off [V]" from BPX” will be used by default).
  • extra_params is optional (initial conditions from the BPX, fully charged state, and single cell Newman model with constant temperature will be used by default).
  • is_blocking is optional (default is True). It determines whether the solve command is blocking until computation has finished or returns right away. In the latter case, the Solution object may still point to an unfinished run (its status can be checked by printing solution.status).

The solve method returns dandeliion.client.Solution object that can be used to get access to the solution for further analysis and plotting, and also provides several helper methods and properties.

To get the simulation status:

print(solution.status)

Possible solution.status values:

solution.status Description
queued The simulation is in the queue and will be started as soon as possible
running The simulation is currently running (use solution.log to get more details)
cancel_requested Cancellation has been requested and the provider is stopping
succeeded The simulation completed successfully
failed The simulation has failed. Print solution.log for more details.
cancelled The simulation was cancelled
timed_out The simulation exceeded its backend runtime limit

To get the simulation log, which can contain warnings, error messages, and other simulation-related information, use:

print(solution.log)

This can be called during the simulation (in non-blocking mode).

Cancel a queued or running simulation with:

status = solution.cancel()
print(status)  # "cancel_requested" or "cancelled"

Cancellation is idempotent. A running Lambda simulation may continue at the provider after the public run becomes cancelled; its later output is not made available.

Running the simulation in non-blocking mode

In many cases, it's useful to run the simulation in non-blocking mode by passing is_blocking=False to the solve function. In this mode, solve immediately returns a solution object, even if the simulation is still running (or queued).

The user can request the simulation status and the backend logs by calling solution.status and solution.log.

If the user wants the program to wait for the simulation to finish, this can be done by calling

solution.join()

This will block execution until the simulation finishes. If the simulation has already completed, join() returns immediately.

If there is a chance that the user’s connection will drop for any reason, the user can save the current solution object to a file. This file can later be used to reconnect to the simulation on the server and retrieve the latest updates:

solution_file = "solution.json"

# Save solution object
solution.dump(solution_file)

...

# Restore solution (reconnect to the server)
restored_solution = dandeliion.Simulator.restore(
    solution_file,
    api_url=api_url,
    api_key=api_key,
)

Client 2.0 restore bundles never persist the API token, API origin, or reusable server URLs. Reconnecting an unfinished bundle therefore requires both an explicit v2 api_url and api_key; this prevents a modified file from sending credentials to an attacker-controlled host.

Accessing simulation results

To print all available keys (outputs) in the solution object:

for key in sorted(solution.keys()):
    print(key)

Currently available outputs:

Current [A]
Electrolyte concentration [mol.m-3]
Electrolyte potential [V]
Electrolyte x-coordinate [m]
Temperature [K]
Time [s]
Voltage [V]
X-averaged negative electrode exchange current density [A.m-2]
X-averaged negative electrode potential [V]
X-averaged negative electrode surface concentration [mol.m-3]
X-averaged positive electrode exchange current density [A.m-2]
X-averaged positive electrode potential [V]
X-averaged positive electrode surface concentration [mol.m-3]

To get access to the particular output, for example, to the voltage vs time vector:

V_vs_t = solution["Voltage [V]"]

To print the final values of time, voltage, and temperature:

print(f"Final time [s]: {solution['Time [s]'][-1]}")
print(f"Final voltage [V]: {solution['Voltage [V]'][-1]}")
print(f"Final temperature [K]: {solution['Temperature [K]'][-1]}")

To plot the current and voltage vs time. Here we access scalar values vs time:

fig, axs = plt.subplots(2, 1, figsize=(10, 8))
axs[0].plot(solution["Time [s]"], solution["Current [A]"], label="DandeLiion")
axs[0].set_xlabel("time [s]")
axs[0].set_title("Current [A]")
axs[0].legend()
axs[1].plot(solution["Time [s]"], solution["Voltage [V]"], label="DandeLiion")
axs[1].set_xlabel("time [s]")
axs[1].set_title("Voltage [V]")
axs[1].legend()
plt.tight_layout()
plt.show()

To plot the concentration in the electrolyte vs x at the last time step. Here we access spatially dependent values vs time:

plt.plot(
    solution["Electrolyte x-coordinate [m]"] * 1e6,
    solution["Electrolyte concentration [mol.m-3]"][-1],
    label="Dandeliion",
)
plt.xlabel(r"x [$\mu$m]")
plt.title("Electrolyte conc. (end of experiment) [mol.m-3]")
plt.legend()
plt.show()

If the user needs the solution at the t_eval times, the following code can be used (works only correctly on columns with timeline data):

t_eval = np.arange(0, 3600, 1)
V_vs_t_eval = solution["Voltage [V]"](t=t_eval)

This is a linear interpolation with constant extrapolation.

Save and load simulation results

The entire solution can be saved as an atomic, versioned JSON bundle:

solution.dump("solution.json")

And restored using

restored_solution = dandeliion.Simulator.restore("solution.json")

For successful runs, dump() streams the complete API result directly into the bundle without materialising it in memory. Restored result fields are parsed lazily, so solution["field"] loads only the fields needed by the caller. Incomplete runs are stored with result: null and can be reconnected later with explicit credentials. Client 2.0 intentionally rejects legacy v1 restore files.

Migrating from client 1.x

  • Change the configured URL from https://api.dandeliion.com/v1 to https://api.dandeliion.com.
  • Expect succeeded instead of success, plus the new cancel_requested, cancelled, and timed_out states.
  • Remove any code that expects WebSocket status updates; join() and solution.status now use bounded REST polling.
  • Treat DandeliionAPIException as a structured error and retain idempotency_key when manually replaying an uncertain submission.
  • Recreate saved data with client 2.0. V1 restore files and v1 server runs cannot be reconnected.
  • Python 3.9 is no longer supported. Use Python 3.10–3.13.

Maintainers can run the opt-in production contract check with scripts/production_smoke.py. It is disabled unless --confirm-consume-use and DANDELIION_API_TOKEN are supplied because it creates one real simulation and consumes one Token Portal use.

Useful links

BPX repo: https://github.com/FaradayInstitution/BPX

BPX website: https://bpxstandard.com/

Documentation, examples, and CHANGELOG

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dandeliion_client-2.0.0.tar.gz (2.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dandeliion_client-2.0.0-py3-none-any.whl (36.5 kB view details)

Uploaded Python 3

File details

Details for the file dandeliion_client-2.0.0.tar.gz.

File metadata

  • Download URL: dandeliion_client-2.0.0.tar.gz
  • Upload date:
  • Size: 2.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for dandeliion_client-2.0.0.tar.gz
Algorithm Hash digest
SHA256 b0b42421f8392c9cf40b03293924a39dde58d24828789f82132dd3ca7e75f88f
MD5 d39be031b74a1cc62336565fbc937243
BLAKE2b-256 af55e39d4386d03f2a95d94427c58c75278bbd70975116eaaf77563c72ad5ec7

See more details on using hashes here.

File details

Details for the file dandeliion_client-2.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for dandeliion_client-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 224d0915da18197c74dc44ebfbf19d21de99fd3c98586371920154379e191cf7
MD5 7b23bd9a9e7f889275ddb3ee3d3eed50
BLAKE2b-256 38a46cdddf84e22a560c23f5409379d02f50aaeb5462b40cfabb5de8ada654e6

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 files

1.2.0

2 files

1.1.0

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

0.2.1.post1

1 file

0.2.1

1 file

0.2.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page