Skip to main content

Client for accessing Dandeliion services

Project description

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 requires Python 3.9 and higher.

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/v1"
api_key = "API_TOKEN"
simulator = dandeliion.Simulator(api_url, api_key)

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)
success The simulation completed successfully
failed The simulation has failed. Print solution.log for more details.

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).

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)

After reloading, restored_solution will point to a new solution object representing the simulation.

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 object can be saved to a JSON file:

solution.dump("solution.json")

And restored using

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

Useful links

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

BPX website: https://bpxstandard.com/

Documentation, examples, and CHANGELOG

Project details


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-1.2.0.tar.gz (3.0 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-1.2.0-py3-none-any.whl (25.5 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for dandeliion_client-1.2.0.tar.gz
Algorithm Hash digest
SHA256 768d38f3c5d6641e19366d4359af16090d04cb647bc0420db45751ef8c0ba298
MD5 6fa1eae6bc256ac5aa6159d74e20d834
BLAKE2b-256 660411aa791b2e3d0e6fdf01368266e581835449606a4132e6f483bf801c5960

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for dandeliion_client-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 00450320e2a25c3bcead682c3a833f179f4dcdb71a418074431e93ece4200366
MD5 87fba43f65095b9bde6dcde3cfe7b7a9
BLAKE2b-256 b846734b9426da98cd86226f3fa23d230bf0f67c05a7a1d4b09a2e03aa5652c5

See more details on using hashes here.

Supported by

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