fela-pde / README.md
itstheraj's picture
initial commit
cdcc0fd
|
Raw
History Blame Contribute Delete
10.3 kB
---
license: other
license_name: lowdown-labs-lovely-license-1.0
license_link: LICENSE
tags:
- fela
- fourier-neural-operator
- fno
- cpu
- on-device
- pde-surrogate
- thermal-simulation
- battery
library_name: transformers
pipeline_tag: image-to-image
---
# DISCLAIMER
This model is a research preview. Lowdown Labs has put together
this model in the interest of advancing public science.
# FELA PDE: on device 2D thermal field surrogate for battery packs
Give FELA PDE the layout of a battery pack, its heat load, and how it is being cooled, and it
tells you where the pack runs hot. It returns the full steady state temperature map in one fast
pass, standing in for a slower finite volume solve. It runs on a plain CPU with no GPU, so it can
sit inside a battery management tool, a design loop, or an on premises engineering app and flag
hot spots without a cloud round trip.
What ships in this repo is the small web lite version: 892,545 parameters, about 7.1 MB in fp32.
The larger validated teacher is a separate line and is not shipped here.
# What goes in, what comes out
- Input: an 8 channel physics field on a 96x96 grid, shape `(1, 8, 96, 96)`. The channels, in
order, are `mask` (pack solid region), `q_source` (volumetric heat source, the hot spot),
`k_field` (thermal conductivity), `h_conv` (convective heat transfer coefficient), `T_amb`
(ambient temperature), `x_coord`, `y_coord` (normalized 0..1 coordinates), and
`log_domain_L` (log of the physical domain size). Each channel is standardized with the
training statistics that ship in `config.json`; `modeling.preprocess` does this for you.
- Output: a 1 channel normalized temperature field, shape `(1, 1, 96, 96)`.
`modeling.denormalize` converts it to degrees Celsius using the training y statistics
(`Y_degC = Ynorm * y_std + y_mean`).
- In plain terms: give it the pack geometry, the heat load, and the cooling conditions, and it
returns the predicted temperature map so an engineer can see where the pack runs hot.
# Building an input (for battery and BMS engineers)
You do not hand build the 8 channel tensor. `input_builder.py` (an add on shipped in this repo)
builds it from ordinary pack parameters, matching the exact encoding the model was trained on
(the coordinate planes, `log_domain_L`, and the per channel standardization from `config.json`).
Two of the channels and `log_domain_L` are model conventions, not physics you supply. The
physical channels and their units are:
| Channel | Meaning | Units | Typical range |
|---|---|---|---|
| mask | 1 inside a cell, 0 in the coolant | none | 0 or 1 |
| q_source | heat source density in the cells | W/m3 | derived from current, SoC, R0 |
| k_field | thermal conductivity | W/(m K) | cell 1 to 30, coolant 0.1 to 1.5 |
| h_conv | convective heat transfer coefficient | W/(m2 K) | 5 to 200 |
| T_amb | ambient temperature | degC | 15 to 40 |
| x_coord | normalized column position (the builder sets this) | none | 0 to 1 |
| y_coord | normalized row position (the builder sets this) | none | 0 to 1 |
| log_domain_L | natural log of the physical pack size (the builder sets this) | ln(m) | pack 0.02 to 0.12 m |
The model was trained on this distribution; inputs well outside these ranges are not characterized.
## The BMS path: from_pack
Give it a cell layout and pack parameters. It computes the heat source
(`P = current^2 * R0 * (1 + beta * (1 - SoC)^2)`, spread over the cell area), the conductivity map,
and the rest, then returns a ready to run `(1, 8, 96, 96)` tensor:
```python
import torch
from input_builder import from_pack, cylinder_mask
from modeling import load_model, denormalize
model = load_model(".")
mask = cylinder_mask(rows=3, cols=4, radius_frac=0.4) # a 3 by 4 cylindrical cell pack
x = from_pack(
mask,
current_A=40.0, soc=0.3, R0_ohm=0.02,
k_cell_W_mK=20.0, k_coolant_W_mK=0.6,
h_conv_W_m2K=80.0, T_amb_degC=25.0, domain_L_m=0.08,
)
with torch.no_grad():
T = denormalize(model(x))[0, 0] # a 96 by 96 temperature map in degC
print("peak", float(T.max()), "degC")
```
`cylinder_mask(rows, cols, radius_frac)` and `rect_mask(aspect, fill)` build the geometry mask.
`example.py` runs this end to end and prints the peak temperature and hottest cell.
## The field path: from_fields
If you already have physical field maps (say from your own thermal model), pass them directly
instead of pack parameters:
```python
from input_builder import from_fields
x = from_fields(mask, q_source_W_m3, k_field_W_mK, h_conv_W_m2K, T_amb_degC, domain_L_m)
```
Each argument is a 96 by 96 array or a scalar (scalars are broadcast). The builder grids each to
96 by 96, adds the coordinate and size channels, standardizes, and returns the model ready tensor.
It is verified to reproduce the training encoding exactly.
## From a file: from_csv and from_json
If your pack parameters live in a file, point the builder at it. `pack.csv` (a header row plus one
values row) or `pack.json` (a flat object) use the same field names as `from_pack`, plus a geometry
(`rows`, `cols`, `radius_frac` for a cylindrical pack, or `aspect`, `fill` for a prismatic block):
```python
import torch
from input_builder import from_csv
from modeling import load_model, denormalize
x = from_csv("pack.csv") # from_json("pack.json") works the same way
with torch.no_grad():
T = denormalize(load_model(".")(x))[0, 0]
```
Example `pack.csv` and `pack.json` ship in this repo.
NB - real battery data comes in many shapes this repo does not read yet, such as
vendor spreadsheets with their own columns, CAD geometry like STEP or STL, and simulation exports
from tools like COMSOL or ANSYS. `from_csv` and `from_json` handle the flat parameter case, which
is the common one. If you already have a geometry or a field as numbers, load it into a 96 by 96
array yourself and pass it to `from_fields`.
# Why we built it this way
A temperature field is smooth and slowly varying, so we mix information in the frequency domain
rather than pixel by pixel. That is what a Fourier Neural Operator does, it learns filters that
act on the field's frequencies (an FFT, a learned filter, an inverse FFT), which suits a smooth
solution field well. The model is small and has no all pairs attention. One forward pass produces
the whole 96x96 map on a plain CPU, far faster than solving the same field directly with a
finite volume method.
# Architecture
- 2D FNO: a lifting `Conv2d(8 -> 32, 1x1)`, then 3 spectral plus pointwise residual blocks
(`SpectralConv2d` keeping 12x12 low and high Fourier modes plus a `Conv2d(32,32,1x1)` skip, GELU
residual), then a projection head `Conv2d(32 -> 128, 1x1) -> GELU -> Conv2d(128 -> 1, 1x1)`.
- 892,545 parameters. The full architecture is in `modeling.py`; the arch dims and
normalization statistics are in `config.json`.
# Training data
- Self generated. Every training, validation, and test sample is produced on CPU by a
deterministic steady state heat equation finite volume solver (pure NumPy and SciPy). No external
dataset is downloaded, scraped, or redistributed. The PDE, the geometry parameterization, and
the input and target encoding are reproduced in `train.py` (`--smoke` regenerates the split and
asserts the sizes). Full details, seeds, and the split are in `train.py`.
- License: none required. The generator is our own code; the finite volume method and the
Fourier Neural Operator (Li et al., ICLR 2021) are published methods, not licensed data.
Commercially clean.
The shipped `train.py` reproduces the primary battery 2D training recipe (a larger `FNO2dV32`
teacher with a length scale channel and an energy balance peak prior). The weights shipped here
are the distilled web lite student described above.
# How to run it
```python
from huggingface_hub import hf_hub_download
import modeling
path = hf_hub_download("lowdown-labs/fela-pde", "model.safetensors")
model = modeling.load_model(path) # or load_model("/path/to/weights_dir")
# raw_field: an (8, 96, 96) physics field in physical units
x = modeling.preprocess(raw_field) # standardizes and validates shape
import torch
with torch.no_grad():
y_norm = model(x) # (1, 1, 96, 96) normalized temperature
y_degc = modeling.denormalize(y_norm) # degrees Celsius
```
`verify.py` runs a fixed sample input and checks the output shape and a verification value.
# Serving artifacts
- `model.safetensors` plus `config.json` for the safetensors load path (fp32).
- `verify.py` runs a fixed sample input and checks the output shape and a verification value.
# Intended use, limitations, and safety
- This is a surrogate, not a certified thermal safety tool. Use its output as a fast screening
and design aid, not as the sole basis for a safety critical decision. Validate against a full
solver on your own configurations before relying on it.
- Trained and evaluated only on the self generated battery thermal distribution described above.
Geometries, materials, and boundary conditions outside that distribution are not characterized
here.
- This is the distilled web lite student. The larger validated checkpoints (battery and heatsink,
2D and 3D) are a separate line.
# Acknowledgements and references
- Fourier Neural Operator: Li, Z., Kovachki, N., Azizzadenesheli, K., et al. (2021). Fourier
Neural Operator for Parametric Partial Differential Equations. ICLR.
https://arxiv.org/abs/2010.08895
- Finite volume heat transfer: Patankar, S. V. (1980). Numerical Heat Transfer and Fluid Flow.
- SciPy: Virtanen, P., et al. (2020). Nature Methods 17, 261-272.
- PyTorch: Paszke, A., et al. (2019). NeurIPS. https://arxiv.org/abs/1912.01703
# Model family
This is part of the FELA family from Lowdown Labs: one Fourier Neural Operator architecture
across many modalities, all CPU native and subquadratic. Sibling repos are independently
trained per modality and share no weights, so none carries a `base_model` link.
# License
Released under the Lowdown Labs Lovely License 1.0 (CC BY-NC 4.0 plus Hippocratic License 3.0). See LICENSE. For most LL models, a commercial license may be available; contact Lowdown Labs.