Jump to Section
Pack Envelope & Constants
Fixed battery compartment dimensions for the SakuMoto 2W EV moped platform
PACK_L
363 mm
Pack length (internal cavity dimension)
PACK_W
209 mm
Pack width (internal cavity dimension)
PACK_H
149 mm
Pack height (internal cavity dimension)
PACK_VOL
11,285,427 mm³
= 363 × 209 × 149 mm³ = 11.285 liters
CU_COST_PER_M2
$5.40 / m²
Cu foil anode CC cost — derived: 8 µm × 8960 kg/m³ × $75.34/kg
AL_COST_PER_M2
$2.40 / m²
Al foil cathode CC cost — derived: 15 µm × 2700 kg/m³ × $59.26/kg
PCC_COST_PER_M2
$0.89 / m²
PCC cost per m²
LAYER_THICKNESS
0.185 mm
Electrode bilayer stack thickness (cathode 50µm + anode 90µm + sep 20µm + Al 15µm + Cu 10µm)
AL_DENSITY
2.70 g/cm³
Aluminum density (for casing weight calculation)
Cell Chemistry Parameters
Electrochemical properties by chemistry selection
| Parameter | NMC (default) | LFP | Notes |
|---|---|---|---|
| Nominal Cell Voltage | 3.70 V | 3.20 V | Average discharge voltage |
| Specific Capacity | 220 mAh/g | 170 mAh/g | Active cathode material, gravimetric |
| Cathode Density | 2.50 g/cm³ | 2.30 g/cm³ | Used in PCC weight model for cathode layer |
| Series range | ⌈51/3.7⌉–⌊58/3.7⌋ = 14–15S | ⌈51/3.2⌉–⌊58/3.2⌋ = 16–18S | Target pack voltage 51–58 V |
Cell Packing Algorithms
How cells are arranged to fill the fixed pack volume
Prismatic — Rectangular Grid Packing with Optimal Orientation
// All 6 permutations of [L, W, H] are tried; the one maximising totalFit is used.
// For each permutation [cL, cW, cH] of [cell_L, cell_W, cell_H]:
nL = ⌊(PACK_L + gap) / (cL + gap)⌋
nW = ⌊(PACK_W + gap) / (cW + gap)⌋
nH = ⌊(PACK_H + gap) / (cH + gap)⌋
totalFit = nL × nW × nH
// Best result across all 6 permutations is selected automatically.
| Variable | Unit | Description |
|---|---|---|
| cell_L, cell_W, cell_H | mm | Cell length, width, height (user input) |
| cL, cW, cH | mm | Cell dims as assigned to pack axes for each permutation |
| gap | mm | Inter-cell clearance (default: 2 mm; thermal + tolerance) |
| PACK_L, PACK_W, PACK_H | mm | Fixed pack envelope dimensions |
| totalFit | cells | Physical maximum cells that fit for the best orientation (pre S×P selection) |
All 6 rotational permutations are evaluated exhaustively. The orientation that maximises
totalFit is automatically selected. Gap accounts for thermal expansion clearance, vibration isolation, and manufacturing tolerance (default 2 mm).Cylindrical — Hexagonal Close Packing (Cross-Section)
// Try all 3 axis orientations, pick best (most cells)
For each orientation (axis ∥ L, axis ∥ W, axis ∥ H):
nAlong = ⌊axis_length / (cell_H + endGap)⌋
inCross = hexPackInArea(cross_A, cross_B, d)
total = nAlong × inCross
// hexPackInArea algorithm (hex close packing):
rowSpacing = d × √3 / 2 // vertical row offset
nEvenCols = ⌊(cross_A + ε) / d⌋ // cells in even rows
nOddCols = ⌊(cross_A - d/2 + ε) / d⌋ // cells in odd rows (offset by d/2)
Count rows until yPos > cross_B - d/2:
add nEvenCols or nOddCols (alternating)
yPos += rowSpacing
Best orientation = orientation with max(total)
| Variable | Unit | Description |
|---|---|---|
| d | mm | Cell diameter (user input, default 21 mm) |
| cell_H | mm | Cell height (user input, default 70 mm) |
| endGap | mm | Axial end gap (default 1 mm) |
| rowSpacing | mm | Hex row center distance = d × √3/2 ≈ 0.866 × d |
Hex packing yields theoretical max density of π/(2√3) ≈ 90.7% vs rectangular packing at π/4 ≈ 78.5%. The algorithm tries all 3 axis orientations because cylindrical cells can be oriented perpendicular to any face of the pack envelope.
Series / Parallel Cell Configuration
Optimal S×P selection from available cells to hit target voltage window
getBestSeries — Voltage-Constrained S×P Optimizer
// Voltage constraint from target pack range (51–58 V)
minS = ⌈minV / cellV⌉ // e.g. ⌈51/3.7⌉ = 14 for NMC
maxS = ⌊maxV / cellV⌋ // e.g. ⌊58/3.7⌋ = 15 for NMC
// For each candidate S in [minS, maxS]:
// p = ⌊totalFit / S⌋ (parallel strings)
// score = p × S (used cells)
// Select S that maximizes used cells
usedCells = S × P
packV = S × cellV
packAh = P × cellAh
| Variable | Unit | Description |
|---|---|---|
| minV, maxV | V | Target pack voltage window (default: 51–58 V) |
| cellV | V | Nominal cell voltage (chemistry-dependent) |
| S | — | Cells in series (determines pack voltage) |
| P | — | Parallel strings (determines pack capacity) |
| totalFit | cells | Physical cells that fit in envelope |
| usedCells | cells | S × P (may be ≤ totalFit due to integer rounding) |
The optimizer exhaustively checks all valid S values (typically only 1–3 candidates) and picks the one that utilizes the most cells from totalFit. Any unused cells (totalFit − usedCells) are left out to maintain voltage constraint.
Electrical Output Calculations
Pack-level energy, voltage, and capacity
Pack Voltage, Capacity, & Energy
Pack Voltage (V) = S × cellV
Pack Capacity (Ah) = P × cellAh
Pack Energy (Wh) = Pack Voltage × Pack Capacity
= (S × cellV) × (P × cellAh)
Pack Energy (kWh) = Pack Energy (Wh) / 1000
| Variable | Unit | Description |
|---|---|---|
| S | — | Cells in series |
| P | — | Parallel strings |
| cellV | V | Nominal cell voltage |
| cellAh | Ah | Cell capacity (user input) |
Volumetric & Gravimetric Energy Density (Incl. / Excl. Overhead)
// Gravimetric density (unchanged):
Gravimetric Density (Wh/kg) = Pack Energy (Wh) / Pack Weight (kg)
// Pack volume:
PACK_VOL_L = PACK_VOL / 1,000,000 // mm³ → L
// Overhead factors:
overhead = 0.15 (prismatic) | 0.20 (cylindrical) | 0.08 (PCC)
// Wh/L — two variants shown in Live Metrics:
Wh/L (incl. overhead) = energyWh / PACK_VOL_L
Wh/L (excl. overhead) = energyWh / (PACK_VOL_L × (1 − overhead))
// Cell geometric volume:
cellVol = cell_L × cell_W × cell_H (prismatic, mm³)
cellVol = π × (d/2)² × cellH (cylindrical, mm³)
// Volume Utilisation — two variants shown in Live Metrics:
Vol Util (incl. overhead) (%) = (usedCells × cellVol) / PACK_VOL × 100
Vol Util (excl. overhead) (%) = (usedCells × cellVol) / (PACK_VOL × (1 − overhead)) × 100
| Variable | Unit | Description |
|---|---|---|
| PACK_VOL_L | L | Pack internal volume in litres (11.285 L) |
| overhead | — | Fraction reserved for BMS, busbars, thermal: 15% prismatic, 20% cylindrical, 8% PCC |
| cellVol | mm³ | Outer geometric volume of one cell |
| energyWh | Wh | Pack energy = S × P × cellV × cellAh |
Incl. overhead is the conservative pack-level figure (denominator = full pack envelope) — this matches how OEMs compare battery pack solutions. Excl. overhead uses only the usable cell space as the denominator, isolating the cell stack's intrinsic density from system overhead. Both values are displayed in the Live Metrics table.
Active Volume Fraction — Incl. & Excl. Overhead
// Active (internal) volume per cell — casing walls subtracted:
activeVol_per_cell = activeL × activeW × activeH (prismatic)
activeVol_per_cell = π × (activeD/2)² × activeH (cylindrical)
// overhead = 0.15 prismatic | 0.20 cylindrical | 0.08 PCC
// Active volume fraction including overhead (vs. total pack):
AVF_incl (%) = (usedCells × activeVol_per_cell) / PACK_VOL × 100
// Active volume fraction excluding overhead (vs. usable cell space):
AVF_excl (%) = (usedCells × activeVol_per_cell) / (PACK_VOL × (1 − overhead)) × 100
| Variable | Unit | Description |
|---|---|---|
| activeVol_per_cell | mm³ | Internal electrode volume (outer cell vol minus casing walls) |
| usedCells | — | S × P (cells used in the pack) |
| PACK_VOL | mm³ | 11,285,427 mm³ |
| overhead | — | 0.15 prismatic / 0.20 cylindrical / 0.08 PCC |
| AVF_incl | % | Active material volume as % of total pack cavity |
| AVF_excl | % | Active material volume as % of space available for cells |
Both AVF values are shown in the Live Metrics table. The excl. overhead figure isolates electrode stack efficiency from system overhead, making it useful when benchmarking cell designs rather than complete pack architectures.
Weight Calculations
Pack mass from cell weight (prismatic & cylindrical)
Pack Weight (Prismatic & Cylindrical)
Pack Weight (kg) = usedCells × cellWeight_g / 1000
// cellWeight = user-supplied cell mass including:
// - Electrodes (cathode, anode, separator)
// - Electrolyte
// - Current collectors
// - Casing (can/pouch)
// - Tabs and termination hardware
| Variable | Unit | Description |
|---|---|---|
| cellWeight_g | g | Per-cell mass (user input; defaults: Prismatic 450 g, Cylindrical 70 g) |
| usedCells | — | S × P (actual cells in pack) |
Pack weight here is cell-only mass. Real pack weight adds ~15–25% for enclosure, BMS, busbars, cooling, and connectors — these overhead factors are not included in this calculator's scope.
Casing Wall Model
Aluminum enclosure thickness used to compute active internal volume and casing mass per cell
Prismatic Casing (Aluminum Shell)
// Wall thickness rule (by cell size):
wallT = 1.0 mm if (cell_L ≥ 100 mm OR cell_H ≥ 100 mm) // large format
wallT = 0.5 mm otherwise // small format
// Active internal volume (subtract wall from all 6 faces):
activeL = cell_L - 2 × wallT
activeW = cell_W - 2 × wallT
activeH = cell_H - 2 × wallT
activeVol = activeL × activeW × activeH
// Active volume fraction (%):
activeVolFrac = (activeVol / (cell_L × cell_W × cell_H)) × 100
// Casing mass per cell:
casingVol_cm³ = (cell_L × cell_W × cell_H - activeVol) / 1000
casingWeight_g = casingVol_cm³ × AL_DENSITY // = casingVol_cm³ × 2.7
| Variable | Unit | Description |
|---|---|---|
| wallT | mm | Aluminum wall thickness (size-dependent; 0.5–1.0 mm) |
| AL_DENSITY | g/cm³ | Aluminum density = 2.70 g/cm³ |
| activeVolFrac | % | Fraction of cell volume available for active materials |
Cylindrical Casing (Can + Cap)
// Wall thickness by cell format:
canWall = 0.25 mm, capThick = 0.50 mm if d ≤ 22 mm (18650 / 21700 format)
canWall = 0.40 mm, capThick = 1.00 mm if d ≥ 40 mm (4680 format)
canWall = 0.30 mm, capThick = 0.75 mm otherwise (mid-size)
// Active inner dimensions:
activeD = d - 2 × canWall
activeH = cell_H - 2 × capThick
// Volumes:
outerVol = π × (d/2)² × cell_H
innerVol = π × (activeD/2)² × activeH
// Active volume fraction:
activeVolFrac = (innerVol / outerVol) × 100
// Casing mass per cell:
casingVol_cm³ = (outerVol - innerVol) / 1000
casingWeight_g = casingVol_cm³ × AL_DENSITY
| Variable | Unit | Description |
|---|---|---|
| canWall | mm | Radial wall thickness of cylindrical can |
| capThick | mm | End cap thickness (applied to both ends) |
| activeD | mm | Effective inner diameter for electrode winding |
Wall thickness tiers reflect commercial cell design norms: larger format cells require thicker cans to withstand swelling pressure; small 18650/21700 cells are well-characterized at 0.25 mm wall.
Current Collector Cost (Prismatic & Cylindrical)
Copper foil area per cell estimated from jelly-roll / electrode stack geometry
Prismatic — Unit Cell Stacking, Capacity & CC Cost
// ── Step 1: Unit cell thickness ──────────────────────────────────────
// One unit cell = cathode layer + Al CC + separator + anode layer + Cu CC + separator
UNIT_CELL_MM = 0.185 // mm = 185 µm total per unit cell
// (cathode 50µm + Al CC 15µm + sep 10µm + anode 90µm + Cu CC 10µm + sep 10µm)
// ── Step 2: How many unit cells fit in the case? ──────────────────────
// W (cell width) is the stack direction; subtract Al casing wall on both sides.
activeW = cell_W - 2 × wallT
nUnitCells = floor(activeW / UNIT_CELL_MM)
// ── Step 3: Electrode face area ───────────────────────────────────────
activeL = cell_L - 2 × wallT
activeH = cell_H - 2 × wallT
electrodeArea_cm² = (activeL × activeH) / 100 // mm² → cm²
// ── Step 4: Cell capacity (derived from stacked layers) ───────────────
// arealCap = user input (mAh/cm²); typical NMC = 3.0, LFP = 2.5–3.0
singleUnitCellAh = (arealCap_mAhcm2 × electrodeArea_cm²) / 1000 // mAh → Ah
cellAh = nUnitCells × singleUnitCellAh
// ── Step 5: Pack capacity ─────────────────────────────────────────────
packAh = p × cellAh // p = parallel cell count in pack
// ── Step 6: Current collector cost — Cu (anode) + Al (cathode) ────────
ccArea_m² = (activeL × activeH / 1,000,000) × 2 × nUnitCells × usedCells
cuCost = (ccArea_m² / 2) × CU_COST_PER_M2 // Cu foil anode — half the area
alCost = (ccArea_m² / 2) × AL_COST_PER_M2 // Al foil cathode — half the area
ccCost = cuCost + alCost // total CC cost (Cu + Al)
| Variable | Unit | Description |
|---|---|---|
| UNIT_CELL_MM | mm | 0.185 mm per unit cell: cathode(50) + AlCC(15) + sep(10) + anode(90) + CuCC(10) + sep(10) = 185 µm |
| activeW | mm | Internal stack width = cell_W − 2 × wallT |
| nUnitCells | — | Number of unit cells stacked across cell width; drives BOTH capacity and CC area |
| arealCap | mAh/cm² | User-specified electrode areal capacity (typical NMC: 3.0 mAh/cm²) |
| singleUnitCellAh | Ah | Capacity of a single stacked unit cell = arealCap × electrode face area |
| cellAh | Ah | Total prismatic cell capacity = nUnitCells × singleUnitCellAh (derived, not input) |
| activeL × activeH | mm² | Electrode face area per unit cell (large face of the prismatic cell) |
| × 2 | — | Both sides of copper foil (cathode-side Al CC + anode-side Cu CC per unit cell) |
Key correction vs. single-cell assumption: A 26 mm wide cell with 0.5 mm Al walls has activeW = 25 mm → floor(25 / 0.185) = 135 stacked unit cells. Modeling this as 1 cell understates capacity by 135×. Cell capacity is now fully derived from electrode geometry — not a user input.
CC cost basis: Cu foil $5.40/m² (8 µm, 8960 kg/m³, $75.34/kg) + Al foil $2.40/m² (15 µm, 2700 kg/m³, $59.26/kg). Both anode (Cu) and cathode (Al) current collectors are included.
CC cost basis: Cu foil $5.40/m² (8 µm, 8960 kg/m³, $75.34/kg) + Al foil $2.40/m² (15 µm, 2700 kg/m³, $59.26/kg). Both anode (Cu) and cathode (Al) current collectors are included.
Cylindrical — Wound Jelly Roll Area
// Jelly-roll bore volume (active winding space):
jrVol_mm³ = π × (activeD/2)² × activeH
// Unrolled electrode area from bore volume and layer thickness:
ccArea_m² = (jrVol_mm³ / LAYER_THICKNESS / 1,000,000) × 2 × usedCells
= (jrVol_mm³ / 0.185 / 1e6) × 2 × usedCells
// Cost — Cu (anode) + Al (cathode):
cuCost = (ccArea_m² / 2) × $5.40 // Cu foil anode side
alCost = (ccArea_m² / 2) × $2.40 // Al foil cathode side
ccCost = cuCost + alCost
| Variable | Unit | Description |
|---|---|---|
| jrVol_mm³ | mm³ | Effective jelly-roll volume inside the cylindrical can |
| activeD, activeH | mm | Inner active dimensions (subtracts can wall and cap) |
The wound jelly-roll is approximated as a solid cylinder of active volume divided by layer thickness — this gives the electrode ribbon length, and thus foil area. Real cells have a hollow core mandrel (typically 2–4 mm dia), so actual area is slightly lower; the approximation is conservative by ~2–5%.
PCC / Kavian Bipolar Model
Polaris Composite Cell — bipolar stacked architecture replacing metal current collectors
Mono-Cell Thickness (Single Bipolar Layer)
monoCellT (µm) = cathodeT + sepT + anodeT + pccT
monoCellT (mm) = monoCellT (µm) / 1000
// Default values:
// cathodeT = 50 µm (NMC cathode coating)
// sepT = 10 µm (separator, per side)
// anodeT = 90 µm (graphite anode coating)
// pccT = 10 µm (Polaris Composite Cell layer)
| Variable | Unit | Description |
|---|---|---|
| cathodeT | µm | Cathode coating thickness per side (user input, default 50 µm) |
| sepT | µm | Separator thickness (user input, default 10 µm/side) |
| anodeT | µm | Anode coating thickness per side (user input, default 90 µm) |
| pccT | µm | PCC layer thickness (user input, default 10 µm) |
In a bipolar stack, the PCC layer serves as both the cathode current collector for one cell and the anode current collector for the adjacent cell — eliminating one metal foil layer per interface.
Sub-Stack Configuration (Series × Parallel)
// Voltage from series layers (same logic as getBestSeries):
minS = ⌈minV / cellV⌉
maxS = ⌊maxV / cellV⌋
nSeries = round((minV + maxV) / 2 / cellV), clamped to [minS, maxS]
// Sub-stack dimensions:
subStackT = nSeries × monoCellT_mm // thickness
subStackX = activeX + 2 × sealW // length (active area + sealing margin)
subStackY = activeY + 2 × sealW // width (active area + sealing margin)
subStackV = nSeries × cellV // voltage
// Number of parallel sub-stacks (geometric packing — actual physical fit):
nParallel_geo = ⌊PACK_L/subStackX⌋ × ⌊PACK_W/subStackY⌋ × ⌊PACK_H/subStackT⌋
nParallel = ⌊nParallel_geo × (1 − OVERHEAD_PCC)⌋ // reserve BMS/thermal overhead
| Variable | Unit | Description |
|---|---|---|
| activeX, activeY | mm | Active electrode face dimensions (user input; default 280 × 180 mm) |
| sealW | mm | Sealing/bonding margin around active area (default 1 mm) |
| OVERHEAD_PCC | — | 8% reserved for BMS, bus bars, and thermal management (same overhead mechanism as prismatic/cylindrical) |
| subStackVol | mm³ | = subStackX × subStackY × subStackT |
PCC packing is calculated the same way as prismatic and cylindrical: count how many sub-stacks physically fit in the available pack volume based on actual cell x-y dimensions and layer thickness. No arbitrary fill efficiency multiplier is applied — nParallel_geo is the true physical count. The 8% OVERHEAD_PCC reserve (BMS, bus bars, thermal mgmt) is then applied to the geometric count.
PCC Pack Capacity & Energy
// Areal capacity → per-cell capacity:
activeArea_cm² = (activeX × activeY) / 100 // mm² → cm²
monoAh = (arealCap_mAhcm² × activeArea_cm²) / 1000 // mAh → Ah
// Pack totals:
packAh = nParallel × monoAh // Parallel stacks in parallel
packV = nSeries × cellV // Series layers in each stack
packWh = packV × packAh
packKwh = packWh / 1000
| Variable | Unit | Description |
|---|---|---|
| arealCap | mAh/cm² | Electrode areal capacity loading (user input; default 3.0 mAh/cm²) |
| activeArea_cm² | cm² | Single-layer electrode face area |
| monoAh | Ah | Capacity of one mono-cell (one bipolar layer) |
PCC Weight Model (Layer-by-Layer)
// Material densities:
ρ_cathode = 2.50 g/cm³ (NMC) or 2.30 g/cm³ (LFP)
ρ_anode = 1.60 g/cm³ (graphite composite)
ρ_sep = 1.00 g/cm³ (separator membrane)
ρ_pcc = 1.05 g/cm³ (polymer composite)
// Weight per cm² of active area per mono-cell:
w_cathode = ρ_cathode × (cathodeT_µm / 10000) g/cm²
w_anode = ρ_anode × (anodeT_µm / 10000) g/cm²
w_sep = ρ_sep × (sepT_µm / 10000) g/cm²
w_pcc = ρ_pcc × (pccT_µm / 10000) g/cm²
monoWeightPerCm² = w_cathode + w_anode + w_sep + w_pcc
// Sub-stack weight:
subStackWeight_g = nSeries × monoWeightPerCm² × activeArea_cm²
// Pack weight:
packWeight_kg = (nParallel × subStackWeight_g) / 1000
| Variable | Unit | Description |
|---|---|---|
| ρ_cathode | g/cm³ | NMC cathode coating density (chemistry-dependent) |
| ρ_anode | g/cm³ | Graphite anode composite density (fixed) |
| ρ_sep | g/cm³ | Separator membrane density (fixed) |
| ρ_pcc | g/cm³ | Polaris Composite Cell polymer density (fixed) |
Key advantage: No metal current collectors means zero copper (~9 g/m²) and zero aluminum foil (~4 g/m²) weight. PCC polymer at 1.05 g/cm³ is ~90% lighter than copper (8.96 g/cm³) and ~60% lighter than aluminum (2.70 g/cm³) on a per-volume basis.
PCC vs Metal Current Collector Cost Comparison
// Total current collector area in pack:
ccArea_m² = nParallel × nSeries × (activeArea_cm² / 10000)
// Traditional CC baseline: Cu (anode) + Al (cathode) — full area each
ccCostTraditional = ccArea_m² × CU_COST_PER_M2 + ccArea_m² × AL_COST_PER_M2
= ccArea_m² × ($5.40 + $2.40) = ccArea_m² × $7.80
// PCC alternative cost (replaces both Cu + Al):
pccCostTotal = ccArea_m² × $0.89
// Savings:
savings = ccCostTraditional - pccCostTotal
savings% = (savings / ccCostTraditional) × 100
≈ (7.80 - 0.89) / 7.80 × 100 ≈ 88.6%
| Variable | Unit | Description |
|---|---|---|
| CU_COST_PER_M2 | $/m² | $5.40/m² — Cu anode foil (8 µm × 8960 kg/m³ × $75.34/kg) |
| AL_COST_PER_M2 | $/m² | $2.40/m² — Al cathode foil (15 µm × 2700 kg/m³ × $59.26/kg) |
| PCC_COST_PER_M2 | $/m² | $0.89/m² — PCC (target production cost, replaces both Cu + Al) |
| savings% | % | Cost reduction from replacing Cu + Al foil with PCC |
Result: ~88.6% reduction in current collector cost. PCC at $0.89/m² replaces Cu ($5.40/m²) + Al ($2.40/m²) = $7.80/m² combined. For a typical SakuMoto pack with ~3–5 m² of electrode area, this represents $21–35 per pack savings — a meaningful figure at scale.
Jump to Section
Smart Initial Cell Dimensions Patch 1.0A
Default cell dimensions chosen to maximise volume utilisation at startup
V1.0 — Hardcoded Prismatic Defaults
// V1.0 ships with fixed default cell dimensions that achieve ~89% volume utilisation
// against the SakuMoto pack envelope (363 × 209 × 149 mm):
L = 180 mm
W = 100 mm
H = 140 mm
// Approximate pack-level volume utilisation at these defaults:
volUtil ≈ (totalFit × L × W × H) / PACK_VOL × 100 ≈ 89%
| Variable | Unit | Description |
|---|---|---|
| L | mm | Cell length default (180 mm) |
| W | mm | Cell width default (100 mm) |
| H | mm | Cell height default (140 mm) |
| PACK_VOL | mm³ | 363 × 209 × 149 = 11,285,427 mm³ (SakuMoto pack envelope) |
V1.1 / V2.1 difference: V1.1 and V2.1 replace the hardcoded defaults with a grid-search optimisation that sweeps candidate L/W/H values and selects the combination that maximises total cell count within the pack envelope. The V1.0 fixed defaults are a manually tuned approximation of that optimum for the SakuMoto form factor.
Optimal Cell Orientation Patch 1.0A
Tries all 6 rotational permutations of cell dimensions to find the arrangement that fits the most cells
Prismatic — Exhaustive 6-Permutation Orientation Search
// All 6 unique permutations of [L, W, H] mapped to [PACK_L, PACK_W, PACK_H]:
permutations = [
[L,W,H], [L,H,W],
[W,L,H], [W,H,L],
[H,L,W], [H,W,L]
]
// For each permutation [cL, cW, cH]:
nL = ⌊(PACK_L + gap) / (cL + gap)⌋
nW = ⌊(PACK_W + gap) / (cW + gap)⌋
nH = ⌊(PACK_H + gap) / (cH + gap)⌋
totalFit = nL × nW × nH
// Select the permutation with the maximum totalFit:
bestOrientation = permutation where totalFit is maximised
| Variable | Unit | Description |
|---|---|---|
| L, W, H | mm | User-supplied cell dimensions (length, width, height) |
| cL, cW, cH | mm | Cell dimensions as mapped to pack axes for this permutation |
| PACK_L, PACK_W, PACK_H | mm | Fixed pack envelope dimensions (363 × 209 × 149 mm) |
| gap | mm | Inter-cell clearance (default: 2 mm) |
| nL, nW, nH | — | Cell count along each pack axis for this permutation |
| totalFit | cells | Total cells that physically fit for this orientation |
| bestOrientation | — | The [cL, cW, cH] permutation that yields max totalFit |
Why this matters: Non-cubic prismatic cells often fit significantly more instances in one orientation than another. For example, a 180 × 100 × 140 mm cell may yield a different count when the 140 mm axis is aligned to PACK_L (363 mm) vs PACK_H (149 mm). Exhaustive enumeration of all 6 permutations guarantees the globally optimal orientation is always selected — no manual orientation tuning required.
Dual Active Volume Fraction Patch 1.0A
Live Metrics table now reports active volume fraction both including and excluding pack overhead
Active Volume Fraction — Incl. & Excl. Overhead
// overhead by cell format:
overhead = 0.15 // 15% for prismatic
overhead = 0.20 // 20% for cylindrical
overhead = 0.08 // 8% for PCC
// Per-cell active volume:
activeVol_per_cell = activeL × activeW × activeH (prismatic)
activeVol_per_cell = π × (activeD/2)² × activeH (cylindrical)
// Including overhead (against full pack volume):
AVF_incl (%) = (usedCells × activeVol_per_cell) / PACK_VOL × 100
// Excluding overhead (against usable pack volume only):
AVF_excl (%) = (usedCells × activeVol_per_cell) / (PACK_VOL × (1 − overhead)) × 100
| Variable | Unit | Description |
|---|---|---|
| activeVol_per_cell | mm³ | Volume of active electrode material inside one cell (subtracts casing walls) |
| usedCells | — | S × P (cells actually used in the pack configuration) |
| PACK_VOL | mm³ | Total internal pack cavity volume (11,285,427 mm³) |
| overhead | — | Fraction reserved for BMS, busbars, thermal management (15% / 20% / 8%) |
| AVF_incl | % | Active volume as % of total pack volume |
| AVF_excl | % | Active volume as % of usable pack volume (overhead subtracted) |
Interpretation: AVF_incl is the conservative, pack-level figure an OEM would report. AVF_excl reflects how efficiently the available cell space (after reserving room for BMS, cooling, and busbars) is filled with active electrode material — useful for benchmarking cell stack design independent of system overhead.
Dual Volumetric Density & Volume Utilisation Patch 1.0A
Live Metrics table now shows both incl. and excl. overhead variants for Wh/L and Vol Utilisation
Volumetric Energy Density — Incl. & Excl. Overhead
// Pack volume in litres:
PACK_VOL_L = PACK_VOL / 1,000,000 // mm³ → L
// Wh/L including overhead (against full pack volume):
WhPerL_incl = energyWh / PACK_VOL_L
// Wh/L excluding overhead (against usable cell volume only):
WhPerL_excl = energyWh / (PACK_VOL_L × (1 − overhead))
| Variable | Unit | Description |
|---|---|---|
| energyWh | Wh | Pack energy = S × P × cellV × cellAh |
| PACK_VOL_L | L | Pack internal volume in litres (11.285 L) |
| overhead | — | 0.15 prismatic / 0.20 cylindrical / 0.08 PCC |
| WhPerL_incl | Wh/L | Volumetric density vs. total pack envelope |
| WhPerL_excl | Wh/L | Volumetric density vs. usable cell space (overhead excluded) |
Volume Utilisation — Incl. & Excl. Overhead
// Per-cell geometric volume:
cellVol = cell_L × cell_W × cell_H (prismatic, mm³)
cellVol = π × (d/2)² × cellH (cylindrical, mm³)
// Volume utilisation including overhead (cells vs. total pack):
VolUtil_incl (%) = (usedCells × cellVol) / PACK_VOL × 100
// Volume utilisation excluding overhead (cells vs. usable pack space):
VolUtil_excl (%) = (usedCells × cellVol) / (PACK_VOL × (1 − overhead)) × 100
| Variable | Unit | Description |
|---|---|---|
| cellVol | mm³ | Outer geometric volume of one cell (includes casing) |
| usedCells | — | S × P |
| PACK_VOL | mm³ | 11,285,427 mm³ |
| overhead | — | 0.15 prismatic / 0.20 cylindrical / 0.08 PCC |
| VolUtil_incl | % | Cell volume as % of total pack envelope |
| VolUtil_excl | % | Cell volume as % of usable pack space (overhead excluded) |
Incl. vs. Excl. overhead — when to use which: Use VolUtil_incl / WhPerL_incl for pack-level comparison against other battery systems (matches how OEMs report energy density). Use VolUtil_excl / WhPerL_excl to evaluate how well the cell geometry exploits the space actually available for cells, independently of how much the BMS/thermal system consumes.
Jump to Section
Pack Envelope & Material Constants
Fixed battery compartment dimensions and material property constants for V2.1
PACK_L
363 mm
Pack internal cavity length
PACK_W
209 mm
Pack internal cavity width
PACK_H
149 mm
Pack internal cavity height
PACK_VOL
11,285,427 mm³
= 363 × 209 × 149 mm³ = 11.285 L
CU_FOIL_THICKNESS
8 µm
Copper anode current collector foil thickness
CU_DENSITY
8,960 kg/m³
Copper density
CU_COST_PER_KG
$75.34/kg
Battery-grade rolled Cu foil
CU_COST_PER_M2
≈ $5.40/m²
= 8×10⁻⁶ m × 8960 kg/m³ × $75.34/kg
AL_FOIL_THICKNESS
15 µm
Aluminum cathode current collector foil thickness
AL_CC_DENSITY
2,700 kg/m³
Aluminum density (current collector foil)
AL_COST_PER_KG
$59.26/kg
Battery-grade rolled Al foil
AL_COST_PER_M2
≈ $2.40/m²
= 15×10⁻⁶ m × 2700 kg/m³ × $59.26/kg
BEYOND_COST_PER_M2
$0.89/m²
PCC polymer bipolar layer (replaces both Cu + Al)
AL_DENSITY_G_CM3
2.70 g/cm³
Aluminum density for casing weight calculation
OVERHEAD_PRISMATIC
15% (default)
User-configurable; bus bars, BMS, thermal management
OVERHEAD_CYLINDRICAL
20% (default)
User-configurable; wiring, bus bars, thermal management
OVERHEAD_BEYOND
8% (default)
User-configurable; minimal thermal mgmt (inherently safe bipolar chemistry)
UNIT_CELL_MM
0.185 mm
Prismatic bilayer stack pitch: cathode(50)+AlCC(15)+sep(10)+anode(90)+CuCC(10)+sep(10) = 185 µm
Overhead Volume Model V2.1
User-configurable overhead %; placed as a slab along the longest pack axis
Overhead Slab Placement — getOverheadDims(overheadFrac)
// Overhead is placed as a slab at the END of the pack volume
// along the LONGEST pack dimension. The slab spans the full
// cross-section of the two shorter dimensions.
longestAxis = max(PACK_L, PACK_W, PACK_H) // e.g. PACK_L = 363 mm
overheadMm = longestAxis × overheadFrac // slab thickness in mm
// Available volume for cells (longest dim is reduced by overheadMm):
availL = PACK_L - overheadMm (if longestAxis = PACK_L, else PACK_L)
availW = PACK_W - overheadMm (if longestAxis = PACK_W, else PACK_W)
availH = PACK_H - overheadMm (if longestAxis = PACK_H, else PACK_H)
// Available volume (mm³):
availVol_mm3 = availL × availW × availH
// Equivalently:
availVol_mm3 = PACK_VOL × (1 − overheadFrac)
// Example (prismatic defaults: PACK_L=363, overhead=15%):
overheadMm = 363 × 0.15 ≈ 54.5 mm
availL = 363 − 54.5 = 308.5 mm
availW = 209 mm (unchanged — not the longest axis)
availH = 149 mm (unchanged — not the longest axis)
availVol ≈ 308.5 × 209 × 149 ≈ 9,590,000 mm³ (≈ 85% of PACK_VOL)
| Variable | Unit | Description |
|---|---|---|
| overheadFrac | — | User-editable overhead fraction (OVERHEAD_PRISMATIC / CYLINDRICAL / BEYOND, default 0.15/0.20/0.08) |
| longestAxis | mm | The largest of PACK_L, PACK_W, PACK_H — overhead slab is placed here |
| overheadMm | mm | Physical depth of overhead slab along the longest axis |
| availL, availW, availH | mm | Available pack dimensions for cell packing after overhead slab is subtracted |
| availVol_mm3 | mm³ | = PACK_VOL × (1 − overheadFrac) — volume available for cell stacking |
Key principle: The overhead slab is always placed along the longest dimension, not distributed across all axes. This reflects physical reality: the BMS, thermal management components, and bus bars are typically consolidated at one end of the pack. Changing the overhead % directly changes how many cells fit — the cell optimizer then works exclusively within
availVol.Cell Packing Algorithms
How cells are arranged within the available volume (after overhead slab subtraction)
Prismatic — Exhaustive 6-Permutation Orientation Search V2.1
// All 6 permutations of [cell_L, cell_W, cell_H] are tried
// against available pack dims [availL, availW, availH].
// The permutation that maximises totalFit is selected.
permutations = [
[L,W,H], [L,H,W],
[W,L,H], [W,H,L],
[H,L,W], [H,W,L]
]
// For each permutation [cL, cW, cH] mapped to [availL, availW, availH]:
nL = ⌊(availL + gap) / (cL + gap)⌋
nW = ⌊(availW + gap) / (cW + gap)⌋
nH = ⌊(availH + gap) / (cH + gap)⌋
totalFit = nL × nW × nH
// Best permutation = argmax(totalFit)
// gap = 2 mm default (thermal clearance + manufacturing tolerance)
| Variable | Unit | Description |
|---|---|---|
| availL, availW, availH | mm | Available pack dims after overhead slab subtraction (from getOverheadDims) |
| cell_L, cell_W, cell_H | mm | Prismatic cell dimensions (user input) |
| gap | mm | Inter-cell clearance (default 2 mm) |
| totalFit | cells | Max cells that physically fit for this orientation; best across all 6 permutations is used |
V2.1 change from V1.1: The optimizer now uses
availL/availW/availH (post-overhead-slab dims) instead of full PACK_L/W/H. This means overhead % directly reduces available space, and changing overhead % triggers a fresh orientation search over the smaller volume.Cylindrical — Hexagonal Close Packing (Best Axis Orientation)
// Try all 3 axis orientations (cylinder axis ∥ L, ∥ W, or ∥ H).
// Pick the orientation with the highest total cell count.
For each orientation (axis aligned to dim X, cross-section Y × Z):
nAlong = ⌊(X + endGap) / (cellH + endGap)⌋
inCross = hexPackInArea(Y, Z, d)
total = nAlong × inCross
// hexPackInArea algorithm (hex close-packing):
rowSpacing = d × √3 / 2 // vertical spacing between row centers
nEvenCols = ⌊(Y + ε) / d⌋ // cells per even row
nOddCols = ⌊(Y − d/2 + ε) / d⌋ // cells per odd row (offset by d/2)
Count rows until yPos > Z − d/2:
add nEvenCols or nOddCols (alternating)
yPos += rowSpacing
Best orientation = orientation with max(total)
// (all dimensions Y, Z, X are from availL/availW/availH)
| Variable | Unit | Description |
|---|---|---|
| d | mm | Cell diameter (user input) |
| cellH | mm | Cell height / length (user input) |
| endGap | mm | Axial end gap (1 mm default) |
| rowSpacing | mm | = d × √3/2 ≈ 0.866 × d (hex row center-to-center) |
Hex packing theoretical max density: π/(2√3) ≈ 90.7%. Rectangular packing: π/4 ≈ 78.5%. Hex packing accounts for this geometry difference in the Active Volume Fraction calculation.
Series / Parallel Cell Configuration
getBestSeries — voltage-constrained S×P selection from available cells
getBestSeries — Optimal S×P Selection
// Target pack voltage window: 51–58 V (user-adjustable)
minS = ⌈minV / cellV⌉ // min series cells to reach lower voltage bound
maxS = ⌊maxV / cellV⌋ // max series cells before exceeding upper voltage bound
// For each candidate S in [minS .. maxS]:
// P = ⌊totalFit / S⌋
// score = P × S (used cells)
// Select S that maximises score (= used cells)
usedCells = S × P
packV = S × cellV
packAh = P × cellAh
| Variable | Unit | Description |
|---|---|---|
| minV, maxV | V | Target pack voltage window (default 51–58 V) |
| cellV | V | Nominal cell voltage (NMC 3.70 V / LFP 3.20 V) |
| S | — | Cells in series (fixes pack voltage) |
| P | — | Parallel strings (fixes pack capacity) |
| totalFit | cells | Physical cells that fit in available volume |
Electrical Output & Energy Density
Pack voltage, capacity, energy, and volumetric/gravimetric density
Pack Voltage, Capacity & Energy
packV = S × cellV
packAh = P × cellAh
packWh = packV × packAh = S × P × cellV × cellAh
packKwh = packWh / 1000
| Variable | Unit | Description |
|---|---|---|
| S | — | Cells in series (from getBestSeries) |
| P | — | Parallel strings |
| cellV | V | Nominal cell voltage |
| cellAh | Ah | Cell capacity (derived from electrode geometry for prismatic; user input for cylindrical) |
Volumetric Energy Density & Volume Utilisation
PACK_VOL_L = PACK_VOL / 1,000,000 // mm³ → L
// Incl. overhead = against total pack envelope (OEM-comparable):
WhPerL_incl = packWh / PACK_VOL_L
VolUtil_incl = (usedCells × cellVol) / PACK_VOL × 100
// Excl. overhead = against usable cell space only:
WhPerL_excl = packWh / (PACK_VOL_L × (1 − overheadFrac))
VolUtil_excl = (usedCells × cellVol) / availVol_mm3 × 100
// Gravimetric density:
WhKg = packWh / packWeightKg
| Variable | Unit | Description |
|---|---|---|
| cellVol | mm³ | Outer geometric volume of one cell |
| availVol_mm3 | mm³ | = PACK_VOL × (1 − overheadFrac) — space after overhead slab |
| overheadFrac | — | User-set overhead % / 100 (e.g. 0.15 for 15%) |
Two variants: incl. overhead uses the full pack envelope as denominator — matches how OEMs compare battery systems. Excl. overhead uses only the usable cell space, isolating cell-stack efficiency from system overhead. Both variants are shown in the Live Metrics table.
Active Volume Fraction V2.1
Volume of active electrode material (all cathodes + anodes + separators) relative to the cell-stack volume
Definition — Active Volume Fraction
// Active Volume Fraction (AVF) = volume of active electrode material
// (cathodes + anodes + separators of all cells in the pack)
// divided by the volume occupied by the full cell stack
// (= usable pack volume after overhead is excluded).
// Per-cell active volume:
activeVol_per_cell = activeL × activeW × activeH (prismatic — subtracts Al casing walls)
activeVol_per_cell = π × (activeD/2)² × activeH (cylindrical — subtracts can wall + caps)
// AVF excl. overhead (primary metric; denominator = cell-stack zone only):
AVF_excl (%) = (usedCells × activeVol_per_cell) / availVol_mm3 × 100
= (usedCells × activeVol_per_cell) / (PACK_VOL × (1 − overheadFrac)) × 100
// AVF incl. overhead (conservative pack-level figure):
AVF_incl (%) = (usedCells × activeVol_per_cell) / PACK_VOL × 100
| Variable | Unit | Description |
|---|---|---|
| activeVol_per_cell | mm³ | Internal electrode volume of one cell (outer cell volume minus casing walls) |
| usedCells | — | S × P (actual cells in pack) |
| availVol_mm3 | mm³ | = PACK_VOL × (1 − overheadFrac) — excludes overhead slab volume |
| AVF_excl | % | Active electrode volume as % of cell-stack zone (primary metric shown in comparison table) |
| AVF_incl | % | Active electrode volume as % of total pack cavity |
Cylindrical note: For cylindrical cells,
activeVol_per_cell = π × (activeD/2)² × activeH. This is the actual cylinder bore volume — it inherently accounts for hex packing geometry because the per-cell area used in hex packing is the bounding-box cell footprint (d × rowSpacing), not the bore circle. The comparison table displays AVF_excl for all three cell types.Weight Model V2.1
First-principles cell mass breakdown for prismatic; full pack weight including overhead components
Prismatic — First-Principles Cell Weight (Component Breakdown)
// Material densities:
ρ_cathode = 2.50 g/cm³ (NMC) | 2.30 g/cm³ (LFP)
ρ_anode = 1.60 g/cm³ — graphite composite
ρ_separator = 1.00 g/cm³ — PE/PP separator membrane
ρ_electrolyte = 1.20 g/cm³ — liquid electrolyte
ρ_Cu = 8.96 g/cm³ — copper (anode-side CC foil, 8 µm)
ρ_Al_CC = 2.70 g/cm³ — aluminum (cathode-side CC foil, 15 µm)
ρ_Al_casing = 2.70 g/cm³ — aluminum cell can
// Per unit-cell per cm² of electrode face area:
w_cathode = ρ_cathode × (cathodeT_µm / 10000) g/cm²
w_anode = ρ_anode × (anodeT_µm / 10000) g/cm²
w_separator = ρ_separator × (sepT_µm / 10000) g/cm²
w_Cu_CC = (CU_DENSITY_KG_M3/1000) × (CU_FOIL_UM / 10000) // ≈ 0.0072 g/cm²
w_Al_CC = (AL_CC_DENSITY_KG_M3/1000) × (AL_FOIL_UM / 10000) // ≈ 0.0041 g/cm²
w_electrolyte = ρ_electrolyte × (
0.30 × (cathodeT_µm / 10000) + // 30% cathode porosity
0.35 × (anodeT_µm / 10000) + // 35% anode porosity
0.40 × (sepT_µm / 10000) // 40% separator porosity
)
unitCellWtPerCm2 = w_cathode + w_anode + w_separator + w_Cu_CC + w_Al_CC + w_electrolyte
// Electrode stack mass:
electrodeAreaCm2 = (activeL × activeH) / 100 // mm² → cm²
nUnitCells = ⌊activeW / UNIT_CELL_MM⌋
electrodeStackWtG = nUnitCells × unitCellWtPerCm2 × electrodeAreaCm2
// Casing mass (Al shell):
casingWtG = (cellVol_mm3 − activeVol_mm3) / 1000 × AL_DENSITY_G_CM3
// Tab / terminal hardware:
tabWtG = clamp(cell_L × cell_H / 5000, 3, 15) // 3–15 g, scaled by cell face area
// Per-cell mass:
cellWeightG = electrodeStackWtG + casingWtG + tabWtG
| Variable | Unit | Description |
|---|---|---|
| cathodeT, anodeT, sepT | µm | Layer thicknesses (cathode 50 µm, anode 90 µm, separator 10 µm — user-adjustable in PCC tab) |
| nUnitCells | — | Unit cells stacked across cell width: ⌊activeW / 0.185 mm⌋ |
| electrodeAreaCm2 | cm² | Large face of the prismatic cell (activeL × activeH converted to cm²) |
| casingWtG | g | Aluminum shell mass = displaced volume × 2.70 g/cm³ |
| tabWtG | g | Tab + terminal hardware, 3–15 g range scaled by face area |
Pack-Level Weight — Cells + Overhead Components
// Cell stack mass:
cellsMassKg = usedCells × cellWeightG / 1000
// Pack-level overhead mass (BMS, bus bars, thermal system, structural housing):
// Overhead components fill the overhead slab volume at an average density of 2.0 g/cm³
OVERHEAD_COMPONENT_DENSITY = 2.0 // g/cm³ avg for BMS/busbars/thermal/housing mix
overheadVolCm3 = (PACK_VOL × overheadFrac) / 1000 // mm³ → cm³
packOverheadMassKg = overheadVolCm3 × OVERHEAD_COMPONENT_DENSITY / 1000
// Total pack weight:
packWeightKg = cellsMassKg + packOverheadMassKg
| Variable | Unit | Description |
|---|---|---|
| cellsMassKg | kg | Total mass of all S×P cells |
| overheadVolCm3 | cm³ | Volume of overhead slab = PACK_VOL × overheadFrac (converted mm³→cm³) |
| packOverheadMassKg | kg | Estimated mass of BMS, bus bars, thermal management, and structural housing filling the overhead volume |
| packWeightKg | kg | Total pack mass = cell stack + overhead components |
Overhead component density: 2.0 g/cm³ is a blended average reflecting aluminum busbars (~2.7 g/cm³), BMS PCBs (~1.8 g/cm³), thermal pads/foam (~0.1–0.5 g/cm³), and structural brackets (~2.7 g/cm³). Cylindrical uses a simpler model: user-supplied per-cell weight × usedCells (no first-principles calculation).
Current Collector Cost V2.1
Both copper (anode) and aluminum (cathode) current collector foils — separate material cost calculations
Prismatic — Dual-Metal CC Cost (Cu Anode + Al Cathode)
// CC area: both sides of each unit cell, across all used cells
// Each unit cell has 1 Cu CC (anode side) + 1 Al CC (cathode side)
// Total CC area = electrode face area × 2 (both sides) × nUnitCells × usedCells
ccAreaM2 = (electrodeAreaCm2 / 10000) × 2 × nUnitCells × usedCells
// Split evenly between Cu (anode side) and Al (cathode side):
cuCost = (ccAreaM2 / 2) × CU_COST_PER_M2 // ≈ $5.40/m²
alCost = (ccAreaM2 / 2) × AL_COST_PER_M2 // ≈ $2.40/m²
totalCcCost = cuCost + alCost
// Cost derivation:
CU_COST_PER_M2 = 8×10⁻⁶ m × 8960 kg/m³ × $75.34/kg ≈ $5.40/m²
AL_COST_PER_M2 = 15×10⁻⁶ m × 2700 kg/m³ × $59.26/kg ≈ $2.40/m²
| Variable | Unit | Description |
|---|---|---|
| CU_COST_PER_M2 | $/m² | Copper foil cost: thickness(8µm) × density(8960 kg/m³) × price($75.34/kg) ≈ $5.40/m² |
| AL_COST_PER_M2 | $/m² | Aluminum foil cost: thickness(15µm) × density(2700 kg/m³) × price($59.26/kg) ≈ $2.40/m² |
| ccAreaM2 | m² | Total CC foil area across all unit cells in the pack (Cu + Al combined) |
| nUnitCells | — | Unit cells stacked per prismatic cell = ⌊activeW / 0.185 mm⌋ |
| × 2 | — | One Cu CC (anode face) + one Al CC (cathode face) per unit cell; each spans the full electrode area |
Dual-metal calculation: V2.1 models Cu and Al foils as separate materials with independent thicknesses, densities, and prices. Cu (8 µm) costs ~$5.40/m² and Al (15 µm) costs ~$2.40/m², together ~$7.80/m² combined — which is the baseline the PCC cost comparison uses.
Cylindrical — Wound Jelly Roll CC Area & Cost
// Active bore volume:
jrVolMm3 = π × (activeD/2)² × activeH
// Unrolled electrode area from bore volume and layer pitch:
CYL_LAYER_MM = 0.185 // mm — same bilayer pitch as prismatic
ccAreaM2 = (jrVolMm3 / CYL_LAYER_MM / 1e6) × 2 × usedCells
// Dual-metal cost:
cuCost = (ccAreaM2 / 2) × CU_COST_PER_M2 // Cu anode side ≈ $5.40/m²
alCost = (ccAreaM2 / 2) × AL_COST_PER_M2 // Al cathode side ≈ $2.40/m²
totalCcCost = cuCost + alCost
| Variable | Unit | Description |
|---|---|---|
| jrVolMm3 | mm³ | Effective jelly-roll bore volume (after subtracting can wall and end caps) |
| CYL_LAYER_MM | mm | 0.185 mm per wound bilayer — same pitch as prismatic stack |
The wound jelly roll is approximated as a solid cylinder of active volume divided by layer pitch — giving total electrode ribbon length and therefore foil area. Real cells have a hollow mandrel core (~2–4 mm dia), so actual area is slightly lower; this approximation is conservative by ~2–5%.
PCC (Kavian) Bipolar Stack Model V2.1
Bipolar stacked architecture — PCC polymer layer replaces conventional metal current collectors
Mono-Cell Layer Thickness & Sub-Stack Geometry
// One mono-cell = one bipolar layer:
// cathode coating + separator + anode coating + PCC CC layer
monoCellT_µm = cathodeT + sepT + anodeT + beyondT
monoCellT_mm = monoCellT_µm / 1000
// Default thicknesses (user-adjustable):
// cathodeT = 50 µm, sepT = 10 µm, anodeT = 90 µm, beyondT = 10 µm
// → monoCellT_mm = 160 µm = 0.160 mm
// Sub-stack (nSeries bipolar layers stacked in series):
nSeries = round((minV + maxV) / 2 / cellV), clamped to [minS, maxS]
subStackT = nSeries × monoCellT_mm // total thickness
subStackX = activeX + 2 × sealW // sealed length
subStackY = activeY + 2 × sealW // sealed width
(sealW = 1 mm sealing margin)
// Packing — same overhead mechanism as prismatic/cylindrical:
// Overhead slab placed along longest pack axis (getOverheadDims)
// PCC avail dims = availL, availW, availH
nParallel = ⌊availL / subStackX⌋ × ⌊availW / subStackY⌋ × ⌊availH / subStackT⌋
× (1 − OVERHEAD_BEYOND) // fractional reduction via geometric count × (1-overhead)
→ ⌊nParallel_geo × (1 − OVERHEAD_BEYOND)⌋
| Variable | Unit | Description |
|---|---|---|
| cathodeT, anodeT, sepT, beyondT | µm | User-adjustable layer thicknesses (defaults 50/90/10/10 µm) |
| nSeries | — | Bipolar layers in series to hit target voltage; selected from voltage range midpoint |
| activeX, activeY | mm | Active electrode face dimensions (user input; default 280 × 180 mm) |
| sealW | mm | Sealing margin around active area (1 mm per side) |
| nParallel | — | Parallel sub-stacks that physically fit within available volume (after overhead fraction applied) |
In a bipolar stack the PCC layer serves as both the cathode-side CC for one cell and the anode-side CC for the adjacent cell — eliminating one metal foil layer per interface. This is why the PCC weight model omits Cu and Al CC contributions entirely.
PCC Pack Energy & Weight
// Areal capacity → per-mono-cell capacity:
activeAreaCm2 = (activeX × activeY) / 100 // mm² → cm²
monoAh = (arealCap_mAhcm2 × activeAreaCm2) / 1000
// Pack totals:
packAh = nParallel × monoAh
packV = nSeries × cellV
packWh = packV × packAh
// Weight model (layer-by-layer, no Cu or Al foil):
ρ_cathode = 2.50 g/cm³ (NMC) | 2.30 g/cm³ (LFP)
ρ_anode = 1.60 g/cm³
ρ_sep = 1.00 g/cm³
ρ_beyond = 1.05 g/cm³
// Per mono-cell per cm² active area:
monoWeightPerCm2 = ρ_cathode×(cathodeT/10000) + ρ_anode×(anodeT/10000)
+ ρ_sep×(sepT/10000) + ρ_beyond×(beyondT/10000)
subStackWeightG = nSeries × monoWeightPerCm2 × activeAreaCm2
packWeightKg = (nParallel × subStackWeightG) / 1000
| Variable | Unit | Description |
|---|---|---|
| arealCap | mAh/cm² | Electrode areal capacity (user input; default 3.0 mAh/cm²) |
| monoAh | Ah | Capacity of one mono-cell = one bipolar active layer |
| ρ_beyond | g/cm³ | PCC polymer bipolar layer density (1.05 g/cm³ — ~88% lighter than Cu on a volume basis) |
Weight advantage: No copper (8.96 g/cm³) and no aluminum (2.70 g/cm³) CC foil in the stack. PCC at 1.05 g/cm³ is ~88% lighter than Cu and ~61% lighter than Al on a per-volume basis. At $0.89/m² vs. Cu+Al combined $7.80/m², PCC also delivers ~88.6% CC cost reduction.
PCC vs. Traditional CC Cost Comparison
// Total CC area across all sub-stacks:
ccAreaPerSubStack_m2 = nSeries × (activeAreaCm2 / 10000)
totalCcArea_m2 = nParallel × ccAreaPerSubStack_m2
// Traditional baseline — full-area Cu (anode) + full-area Al (cathode) per interface:
ccCuCost = totalCcArea_m2 × CU_COST_PER_M2 // ≈$5.40/m²
ccAlCost = totalCcArea_m2 × AL_COST_PER_M2 // ≈$2.40/m²
ccCostTraditional = ccCuCost + ccAlCost // ≈$7.80/m²
// PCC replacement cost:
pccCostTotal = totalCcArea_m2 × BEYOND_COST_PER_M2 // $0.89/m²
// Savings:
savings = ccCostTraditional − pccCostTotal
savingsPct = savings / ccCostTraditional × 100 // ≈ 88.6%
| Variable | Unit | Description |
|---|---|---|
| CU_COST_PER_M2 | $/m² | ≈ $5.40/m² — Cu anode foil (8µm × 8960 kg/m³ × $75.34/kg) |
| AL_COST_PER_M2 | $/m² | ≈ $2.40/m² — Al cathode foil (15µm × 2700 kg/m³ × $59.26/kg) |
| BEYOND_COST_PER_M2 | $/m² | $0.89/m² — PCC bipolar layer (target production cost, replaces both Cu + Al) |
| savingsPct | % | ≈ 88.6% CC cost reduction from PCC vs. Cu+Al foil |
Recommended Dimensions Algorithm V2.1
Grid-search optimizer that finds cell dimensions maximising pack energy for the current overhead % and chemistry
Prismatic — Energy-Maximising Grid Search
// Objective: find (L, W, H) that maximises pack energy (Wh)
// subject to the current overhead % and voltage constraints.
pOh = getOverheadDims(OVERHEAD_PRISMATIC)
aL = pOh.availL, aW = pOh.availW, aH = pOh.availH
for L in [30 .. aL] step 8:
for W in [10 .. aW] step 5:
for H in [20 .. aH] step 5:
// Cell count:
nL = ⌊(aL + gap) / (L + gap)⌋
nW = ⌊(aW + gap) / (W + gap)⌋
nH = ⌊(aH + gap) / (H + gap)⌋
totalFit = nL × nW × nH
if totalFit < minS: skip // can't even reach min voltage
// Best S×P:
{ s, p } = getBestSeries(totalFit, cellV, minV, maxV)
if p == 0: skip
// Estimate energy using cell-level energy density:
cellVol_L = L × W × H / 1e6
cellAh = eDens_WhL × cellVol_L / cellV // eDens ≈ 290 Wh/L NMC, 200 Wh/L LFP
energyWh = s × p × cellV × cellAh
if energyWh > bestWh: record as best
// Output: best (L, W, H) — shown as suggested dims in the UI
| Variable | Unit | Description |
|---|---|---|
| aL, aW, aH | mm | Available dims after current overhead slab; algorithm re-runs whenever overhead % changes |
| eDens_WhL | Wh/L | Cell energy density proxy: 290 Wh/L (NMC) / 200 Wh/L (LFP) — used for grid-search estimation only |
| gap | mm | Inter-cell clearance (2 mm); same value used in actual pack calculation |
What it optimises for: Maximum pack energy (Wh), not maximum cell count. A smaller cell that allows more parallel strings may yield higher total energy than a larger cell at the same S. The algorithm honours the current overhead %, so increasing overhead % reduces
availVol and may shift the optimal cell size. The "Reset to Recommended" button applies this result to all three dimension inputs.Cylindrical — Best Commercial Format Selection
// Only evaluates the 4 established commercial formats:
formats = [
{ name: "18650", d: 18, cellH: 65 },
{ name: "21700", d: 21, cellH: 70 },
{ name: "46800", d: 46, cellH: 80 },
{ name: "46900", d: 46, cellH: 90 }
]
// For each format × 3 axis orientations:
// 1. Hex pack into available dims → usedCells
// 2. getBestSeries → S, P
// 3. Derive cellAh from jelly-roll geometry + areal capacity
// 4. energyWh = S × P × cellV × cellAh
// Return format with highest energyWh
| Variable | Unit | Description |
|---|---|---|
| formats | — | 18650 / 21700 / 4680 / 4690 — only commercially validated geometries are evaluated |
| cellAh (cylindrical) | Ah | Derived from jelly-roll bore volume, layer thickness, and areal capacity — not user-supplied in the recommended search |
PCC — Active Area Sweep
// Sweep electrode face dimensions (X, Y) to maximise pack energy.
pOh = getOverheadDims(OVERHEAD_BEYOND)
aL = pOh.availL, aW = pOh.availW, aH = pOh.availH
for X in [50 .. aL] step 10:
for Y in [50 .. aH] step 10: // note: Y sweeps across aH (height axis)
subStackX = X + 2 × sealW // add sealing margin
subStackY = Y + 2 × sealW
nSeries = round((minV + maxV) / 2 / cellV), clamped to [minS, maxS]
subStackT = nSeries × monoCellT_mm
nParallel = ⌊aL/subStackX⌋ × ⌊aW/subStackY⌋ × ⌊aH/subStackT⌋
monoAh = arealCap × (X × Y / 100) / 1000 // cm² conversion
energyWh = nSeries × cellV × nParallel × monoAh
if energyWh > bestWh: record as best (X, Y)
// Output: best (activeX, activeY) — displayed in the UI recommendation chip
| Variable | Unit | Description |
|---|---|---|
| X, Y | mm | Active electrode face dimensions being swept (step 10 mm) |
| sealW | mm | 1 mm sealing margin added to both X and Y |
| monoCellT_mm | mm | (cathodeT + sepT + anodeT + beyondT) / 1000 — from current layer thickness inputs |
All three recommended-dimension searches re-run automatically whenever overhead % changes (via a 50 ms debounce after
onOverheadChange()). This keeps the recommendations consistent with the available volume at the current settings.