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:

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:

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

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

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.

Downloads last month
1
Safetensors
Model size
1.78M params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Papers for lowdown-labs/fela-pde