Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![PyThrust Banner](https://raw.githubusercontent.com/Setuav/PyThrust/main/docs/images/PyThrust_banner.png)
![PyThrust Banner](docs/images/PyThrust_banner.png)

[![CI/CD Pipeline](https://github.com/Setuav/PyThrust/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/Setuav/PyThrust/actions/workflows/ci-cd.yml)
[![Docs](https://github.com/Setuav/PyThrust/actions/workflows/docs.yml/badge.svg)](https://github.com/Setuav/PyThrust/actions/workflows/docs.yml)
Expand All @@ -11,13 +11,13 @@

PyThrust is an open-source framework for electric propulsion system analysis, co-design, and parameter optimization in UAV applications. It can be used for multidisciplinary design optimization (MDO) within OpenMDAO. It includes steady-state performance solvers, auto-tuning calibration tools to fit manufacturer test data, and database search tools to map theoretical designs onto real-world brushless motor and propeller catalogs.

## Design and Analysis Visualization
## Feature Visuals

| 1. Propulsion Co-Design Optimization | 2. Propulsion Calibration & Auto-Tuning |
| System Resistance Calibration | OpenMDAO Hover Co-Design |
| :---: | :---: |
| ![Propulsion Co-Design Optimization](https://raw.githubusercontent.com/Setuav/PyThrust/main/docs/images/optimize_and_plot_results.png) | ![Propulsion Calibration & Auto-Tuning Results](https://raw.githubusercontent.com/Setuav/PyThrust/main/docs/images/calibration_results.png) |
| **3. Propeller Aerodynamic Coefficients** | **4. Hover Efficiency Heatmap** |
| ![Propeller Aerodynamic Coefficients](https://raw.githubusercontent.com/Setuav/PyThrust/main/docs/images/propeller_coefficients.png) | ![Hover Efficiency Heatmap](https://raw.githubusercontent.com/Setuav/PyThrust/main/docs/images/efficiency_heatmap.png) |
| ![System Resistance Calibration](docs/images/calibration_results.png) | ![OpenMDAO Hover Co-Design](docs/images/optimize_and_plot_results.png) |
| **Empirical Propeller Database** | **Hover Efficiency Map** |
| ![Empirical Propeller Database](docs/images/propeller_coefficients.png) | ![Hover Efficiency Map](docs/images/efficiency_heatmap.png) |

## Documentation

Expand All @@ -31,7 +31,7 @@ Key sections:
- [Propulsion Solver](https://setuav.github.io/PyThrust/usage/)
- [Motor Calibration](https://setuav.github.io/PyThrust/motor_calibration/)
- [Examples](https://setuav.github.io/PyThrust/examples/)
- [Mathematical Model](https://setuav.github.io/PyThrust/theory/)
- [Propulsion and Battery Theory](https://setuav.github.io/PyThrust/theory/)
- [Component Databases](https://setuav.github.io/PyThrust/databases/)

## License
Expand Down
15 changes: 15 additions & 0 deletions data/batteries/example_liion_cell.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "Example Li-ion Cell",
"source": "Synthetic example data for PyThrust tests and examples",
"cell": {
"capacity_ah": 4.2,
"cutoff_voltage_v": 2.5,
"charge_voltage_v": 4.2,
"max_current_a": 20.0
},
"curves": {
"dod": [0.0, 0.1, 0.2, 0.4, 0.6, 0.8, 1.0],
"ocv_v": [4.20, 4.08, 3.98, 3.83, 3.70, 3.48, 3.20],
"resistance_ohm": [0.020, 0.021, 0.022, 0.026, 0.031, 0.039, 0.055]
}
}
40 changes: 36 additions & 4 deletions docs/api_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,31 @@ This page summarizes the main public classes and helpers. For implementation det

Use `get_no_load_current(rpm)` and `get_winding_resistance(current_a)` when evaluating speed-dependent or current-dependent motor behavior.

## Battery, System, and Propeller Specs
## Battery Models

PyThrust exposes battery models from `pythrust.battery`:

| Class | Purpose |
|---|---|
| `FixedVoltageBattery` | Historical fixed pack-voltage model with scalar discharge efficiency |
| `RateMapBattery` | Equivalent-circuit model using cell OCV and resistance curves |
| `BatteryState` | State of charge/depth of discharge passed to dynamic battery models |
| `BatteryPoint` | Evaluated terminal voltage, current, power, C-rate, efficiency, and feasibility |

`BatterySpec` remains available from `pythrust.propulsion` as a compatibility
alias for `FixedVoltageBattery`. New code should import `FixedVoltageBattery`
or `RateMapBattery` directly.

`RateMapBattery.from_json(path, series=..., parallel=...)` loads one cell
dataset and applies the requested pack topology at runtime.

## System and Propeller Specs

| Class | Purpose |
|---|---|
| `BatterySpec` | Pack voltage and discharge efficiency |
| `SystemSpec` | Lumped electrical resistance for battery, ESC, wires, and connectors |
| `PropellerSpec` | Propeller geometry passed to the solver |
| `OperatingPoint` | Solved RPM, thrust, torque, power, current, voltage, efficiency, and feasibility state |
| `OperatingPoint` | Solved RPM, thrust, torque, motor/battery current, voltage, efficiency, and feasibility state |

## Propulsion Solver

Expand All @@ -35,6 +52,7 @@ Use `get_no_load_current(rpm)` and `get_winding_resistance(current_a)` when eval
point = solver.solve_operating_point(
motor=motor,
battery=battery,
battery_state=state, # required for RateMapBattery
system=system,
propeller=propeller,
prop_entry=prop_entry,
Expand All @@ -44,6 +62,9 @@ point = solver.solve_operating_point(
)
```

`battery_state` may be omitted for `FixedVoltageBattery`. It is required for
dynamic battery models such as `RateMapBattery`.

`SolverConfig` controls numerical behavior:

| Field | Default | Description |
Expand All @@ -54,6 +75,17 @@ point = solver.solve_operating_point(
| `eps_v` | `1e-8` | Voltage residual tolerance |
| `max_iter` | `100` | Maximum root-finder iterations |

`OperatingPoint` includes propulsion outputs such as `rpm`, `thrust_n`,
`torque_nm`, `motor_current_a`, and `motor_voltage_v`, plus battery outputs:

| Field | Description |
|---|---|
| `battery_power_w` | Battery-side power draw |
| `battery_voltage_v` | Battery terminal pack voltage |
| `battery_current_a` | Battery DC current draw |
| `battery_c_rate` | Cell C-rate for rate-map batteries, or `0.0` for fixed-voltage batteries |
| `battery_efficiency` | Battery model discharge efficiency at the solved point |

## Propeller Database

`PropellerDatabase` loads JSON metadata and CSV performance tables:
Expand Down Expand Up @@ -129,4 +161,4 @@ system = result.to_system_spec()

`pythrust.openmdao.PropulsionComponent` wraps `PropulsionSolver` as an `ExplicitComponent` for optimization models.

Inputs include motor parameters, battery voltage, system resistance, propeller diameter, throttle, density, and airspeed. Outputs include RPM, thrust, torque, battery current, battery power, motor current, motor voltage, and feasibility.
Inputs include motor parameters, fixed battery voltage, system resistance, propeller diameter, throttle, density, and airspeed. Outputs include RPM, thrust, torque, battery current, battery power, motor current, motor voltage, and feasibility.
268 changes: 268 additions & 0 deletions docs/battery_model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
# Battery Model

Status: implemented model note for issue #3.

PyThrust supports a fixed-voltage battery model and a lightweight rate-map
battery model. The fixed-voltage path is useful for quick propulsion sizing,
but it hides two effects that matter for electric aircraft performance studies:

- The terminal voltage drops with load.
- The usable energy depends on discharge rate and state of charge.

This page defines the rate-map model now implemented in PyThrust. The model is
inspired by Robert A. McDonald's
`bat-perf` model and the paper "Battery Knockdown Factors for Conceptual
Design".

!!! abstract "Model scope"
PyThrust uses a compact equivalent-circuit battery model for sizing and optimization. It is intended to capture voltage sag and usable energy trends without introducing electrochemical simulation inputs.

## Goals

The model is intended to:

- Stay fast enough for sizing sweeps, optimizers, and OpenMDAO workflows.
- Use manufacturer-accessible data such as capacity, voltage limits, current
limits, discharge curves, and C-rate maps.
- Support point-performance analysis at a specified state of charge.
- Support mission integration by advancing battery state through time.
- Preserve a simple fixed-voltage battery path for examples and low-fidelity
studies.

The model does not try to replace electrochemical simulation tools. PyThrust
does not need electrode-level parameters, diffusion constants, thermal cell
models, or PyBaMM-style electrochemistry for this feature.

## Core Equations

The paper treats battery state with two governing equations: conservation of
charge and an algebraic terminal-voltage equation.

Depth of discharge is:

$$
x = 1 - z
$$

where $z$ is state of charge. With discharge current $I$ taken as positive,
charge conservation is:

$$
\frac{dx}{dt} = \frac{I}{Q}
$$

where $Q$ is rated cell capacity in ampere-seconds.

The static equivalent circuit is an open-circuit voltage source in series with
an internal resistance:

$$
V(x, I) = OCV(x) - R(x)I
$$

where:

- $V$ is terminal cell voltage.
- $OCV(x)$ is open-circuit cell voltage as a function of depth of discharge.
- $R(x)$ is effective cell resistance as a function of depth of discharge.
- $I$ is cell current, positive during discharge.

For a pack with cells in series and parallel:

$$
V_{pack} = N_s V_{cell}
$$

$$
I_{cell} = \frac{I_{pack}}{N_p}
$$

$$
Q_{pack} = N_p Q_{cell}
$$

## bat-perf Mapping

The `bat-perf` MATLAB model uses the same core variables:

| PyThrust concept | bat-perf name | Meaning |
|---|---|---|
| `dod` | `dod` | Depth of discharge, from 0 to 1 |
| `capacity_as` | `Qmax` | Cell capacity in ampere-seconds |
| `rated_current_a` | `irated` | 1C current |
| `ocv(dod)` | `OCVfun(dod)` | Open-circuit cell voltage |
| `resistance(dod)` | `Rssfun(dod)` | Steady-state internal resistance |
| `cutoff_voltage_v` | `Vcutoff` | Minimum terminal cell voltage |
| `charge_voltage_v` | `Vcharge` | Maximum terminal cell voltage |

The most important point-state functions are:

| Mode | bat-perf function | Equation |
|---|---|---|
| Specified current | `cellStateI` | $V = OCV - RI$ |
| Specified C-rate | `cellStateC` | $I = C Q_{Ah}$, then $V = OCV - RI$ |
| Specified voltage | `cellStateV` | $I = (OCV - V) / R$ |
| Specified power | `cellStateP` | solve $P = I(OCV - RI)$ |
| Specified load resistance | `cellStateR` | $I = OCV / (R + R_{load})$ |

For specified power, the current is obtained from the quadratic:

$$
RI^2 - OCV I + P = 0
$$

using the lower-current discharge root:

$$
I = \frac{OCV - \sqrt{OCV^2 - 4RP}}{2R}
$$

The maximum deliverable power at a given DOD occurs when the discriminant is
zero:

$$
P_{max}(x) = \frac{OCV(x)^2}{4R(x)}
$$

The implementation reports infeasible states when requested power exceeds this
limit, when current exceeds the configured limit, or when terminal voltage falls
below cutoff.

## Python API

Use explicit names for the two battery fidelities:

=== "Fixed voltage"

```python
from pythrust.battery import FixedVoltageBattery

battery = FixedVoltageBattery(voltage_v=14.8)
```

=== "Rate map"

```python
from pythrust.battery import BatteryState, RateMapBattery

state = BatteryState(soc=1.0)
battery = RateMapBattery.from_json(
"data/batteries/example_liion_cell.json",
series=4,
parallel=2,
)
```

!!! tip "Choosing a battery model"
Use `FixedVoltageBattery` for early propulsion sizing and simple examples. Use `RateMapBattery` when load-dependent voltage, C-rate limits, or state of charge affect the result.

`BatterySpec` is too general once multiple battery models exist. It remains as
a compatibility alias:

```python
BatterySpec = FixedVoltageBattery
```

The alias keeps old code working during the transition, but new code and
documentation should use `FixedVoltageBattery`.

The common battery behavior is intentionally small:

```python
voltage = battery.terminal_voltage(current_a=current, state=state)
power = battery.terminal_power(current_a=current, state=state)
```

`RateMapBattery` also supports state advancement:

```python
next_state = battery.step_power(power_w=power, dt_s=dt, state=state)
```

For the fixed-voltage model, `terminal_voltage` returns the configured voltage.
For the rate-map model, it evaluates the cell/pack equivalent circuit.

## Dataset Shape

The JSON dataset describes one cell. Pack topology is passed when loading the
dataset:

```json
{
"name": "Example Li-ion Cell",
"source": "Synthetic example data for PyThrust tests and examples",
"cell": {
"capacity_ah": 4.2,
"cutoff_voltage_v": 2.5,
"charge_voltage_v": 4.2,
"max_current_a": 20.0
},
"curves": {
"dod": [0.0, 0.1, 0.2, 0.4, 0.6, 0.8, 1.0],
"ocv_v": [4.20, 4.08, 3.98, 3.83, 3.70, 3.48, 3.20],
"resistance_ohm": [0.020, 0.021, 0.022, 0.026, 0.031, 0.039, 0.055]
}
}
```

```python
battery = RateMapBattery.from_json(cell_path, series=4, parallel=2)
```

The current implementation interpolates `OCV(dod)` and `R(dod)` directly. A
later calibration utility can derive these curves from manufacturer C-rate
discharge maps. Manufacturer discharge curves are usually terminal voltage
under load, so real datasets should document how `OCV(dod)` and `R(dod)` were
derived.

## Solver Integration

The propulsion solver starts from the PWM voltage relation:

$$
V_{applied} = throttle \times V_{pack}
$$

For a rate-map battery, `V_pack` depends on current and state:

$$
V_{applied} = throttle \times V_{pack}(x, I)
$$

The root equation therefore becomes:

$$
F(RPM) =
V_{back}(RPM)
+ I(RPM)R_{motor}
+ I(RPM)R_{system}
- throttle \times V_{pack}(x, I(RPM))
$$

This keeps the propeller/motor equilibrium as a one-dimensional root solve
because current remains a function of RPM through the propeller torque demand.

For mission simulation, evaluate each time step with the current state, compute
pack current/power, then advance DOD:

$$
x_{next} = x + \frac{I_{cell}}{Q_{cell}} \Delta t
$$

## Implementation Status

The initial implementation includes:

- `pythrust.battery.FixedVoltageBattery`
- `pythrust.battery.RateMapBattery`
- `pythrust.battery.BatteryState` and `BatteryPoint`
- JSON cell datasets with explicit series and parallel counts at load time
- Solver integration through `solve_operating_point(..., battery_state=...)`
- `OperatingPoint` battery outputs for voltage, current, C-rate, and efficiency
- A runnable rate-map mission example

## References

- Robert A. McDonald, "Battery Knockdown Factors for Conceptual Design",
AIAA Aviation Forum, 2024, DOI: `10.2514/6.2024-3903`.
- `ramcdona/bat-perf`: <https://github.com/ramcdona/bat-perf>
Loading
Loading