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
60 changes: 58 additions & 2 deletions docs/src/manuals/user_guide/geometries.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The currently supported geometry families are:

- `P2D`
- `P4D Pouch`
- `P4D Prismatic`
- `P4D Cylindrical`

## Overview
Expand All @@ -14,6 +15,7 @@ The currently supported geometry families are:
| --- | --- | --- | --- |
| `P2D` | Through-thickness cell model | 1D across the stack, with radial particle diffusion | Fast cell-level studies, parameter sweeps, cycling analysis |
| `P4D Pouch` | Pouch cell | 3D current-collector / electrode geometry combined with particle diffusion | Tab placement effects, current distribution, pouch thermal/electrical nonuniformity |
| `P4D Prismatic` | Wound prismatic cell | 3D jelly-roll geometry combined with particle diffusion | Wound prismatic tab placement studies and current distribution |
| `P4D Cylindrical` | Cylindrical / jelly-roll cell | Cylindrical wound-cell geometry combined with particle diffusion | Radial and axial heterogeneity in cylindrical cells |

In all three cases, BattMo uses a pseudo-dimensional formulation:
Expand Down Expand Up @@ -140,6 +142,58 @@ See also:

- [3D Pouch example](../../examples/example_3D_pouch.md)

## P4D Prismatic

`P4D Prismatic` targets wound prismatic cells. It uses the same jelly-roll style internal geometry as the cylindrical model, but exposes it through a dedicated framework for prismatic cells packaged in a rectangular can.

This geometry is useful when:

- modelling wound prismatic commercial cells
- studying tab placement and collector-current distribution in a prismatic form factor
- comparing cylindrical and prismatic packaging while keeping the same wound internal structure

Outputs are provided component-wise in the same way as for the cylindrical geometry.

### Main Cell Parameters

| Input path | Meaning |
| --- | --- |
| `["Cell"]["ElectrodeWidth"]` | Axial height of the jelly roll. |
| `["Cell"]["ElectrodeLength"]` | Winding length of the electrode stack used to determine the wound extent. |
| `["Cell"]["InnerRadius"]` | Inner jelly-roll radius. |
| `["Cell"]["CaseWidth"]` | Outer width of the invisible rectangular case used to flatten the wound cross-section. |
| `["Cell"]["CaseThickness"]` | Outer thickness of the invisible rectangular case used to flatten the wound cross-section. |
| `["Cell"]["OuterRadius"]` | Optional fallback outer radius if no winding length is provided. |
| `["NegativeElectrode"]["CurrentCollector"]["TabFractions"]` | Tab placement along the negative current collector as fractions of total spiral length. |
| `["PositiveElectrode"]["CurrentCollector"]["TabFractions"]` | Tab placement along the positive current collector as fractions of total spiral length. |
| `["NegativeElectrode"]["CurrentCollector"]["TabWidth"]` | Width of the negative current-collector tab. |
| `["PositiveElectrode"]["CurrentCollector"]["TabWidth"]` | Width of the positive current-collector tab. |
| `["NegativeElectrode"]["Coating"]["Thickness"]` | Negative electrode coating thickness. |
| `["PositiveElectrode"]["Coating"]["Thickness"]` | Positive electrode coating thickness. |
| `["Separator"]["Thickness"]` | Separator thickness. |
| `["NegativeElectrode"]["CurrentCollector"]["Thickness"]` | Negative current collector thickness. |
| `["PositiveElectrode"]["CurrentCollector"]["Thickness"]` | Positive current collector thickness. |

### Main Simulation Settings

| Setting | Meaning |
| --- | --- |
| `HeightGridPoints` | Number of grid cells along the prismatic-cell height. |
| `AngularGridPoints` | Number of angular sectors used around the wound internal geometry. |
| `NegativeElectrodeCoatingGridPoints` | Number of cells through the negative electrode coating thickness. |
| `PositiveElectrodeCoatingGridPoints` | Number of cells through the positive electrode coating thickness. |
| `SeparatorGridPoints` | Number of cells through the separator thickness. |
| `NegativeElectrodeParticleGridPoints` | Radial resolution inside negative active-material particles. |
| `PositiveElectrodeParticleGridPoints` | Radial resolution inside positive active-material particles. |
| `NegativeElectrodeCurrentCollectorGridPoints` | Number of cells through the negative current collector thickness. |
| `PositiveElectrodeCurrentCollectorGridPoints` | Number of cells through the positive current collector thickness. |
| `NegativeElectrodeCurrentCollectorTabWidthGridPoints` | Resolution across the negative tab face. |
| `PositiveElectrodeCurrentCollectorTabWidthGridPoints` | Resolution across the positive tab face. |

See also:

- [3D prismatic example](../../examples/example_3d_prismatic.md)

## P4D Cylindrical

`P4D Cylindrical` targets cylindrical cells with a jelly-roll type internal structure. It extends the pseudo-dimensional electrochemistry to a cylindrical geometry so that spatial variations around the wound cell can be represented.
Expand All @@ -157,9 +211,10 @@ As for the pouch case, outputs are provided component-wise for the resolved doma

| Input path | Meaning |
| --- | --- |
| `["Cell"]["ElectrodeWidth"]` | Axial height of the jelly roll. |
| `["Cell"]["ElectrodeLength"]` | Winding length of the electrode stack used to determine the wound extent. |
| `["Cell"]["InnerRadius"]` | Inner jelly-roll radius. |
| `["Cell"]["OuterRadius"]` | Outer jelly-roll radius. |
| `["Cell"]["Height"]` | Axial height of the cylindrical cell. |
| `["Cell"]["OuterRadius"]` | Optional fallback outer radius if no winding length is provided. |
| `["NegativeElectrode"]["CurrentCollector"]["TabFractions"]` | Angular or spiral placement of one or more negative tabs as fractions of total spiral length. |
| `["PositiveElectrode"]["CurrentCollector"]["TabFractions"]` | Angular or spiral placement of one or more positive tabs as fractions of total spiral length. |
| `["NegativeElectrode"]["CurrentCollector"]["TabWidth"]` | Width of the negative current-collector tab. |
Expand Down Expand Up @@ -198,6 +253,7 @@ As a rule of thumb:

- use `P2D` when speed and robust cell-scale trends are most important
- use `P4D Pouch` for pouch cells with spatially resolved collector/electrode effects
- use `P4D Prismatic` for wound prismatic cells with jelly-roll 3D effects
- use `P4D Cylindrical` for cylindrical cells where wound-cell geometry matters

You select the geometry through the model settings:
Expand Down
3 changes: 2 additions & 1 deletion docs/src/manuals/user_guide/pxd_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
On this page, we describe the different models available in BattMo for simulation a lithium ion battery cell. We have models available for simulating 1D and 3D geometries:
- P2D: Pseudo-two-dimensional model
- P4D Pouch: Pseudo-four-dimensional model for pouch cells
- P4D Prismatic: Pseudo-four-dimensional model for prismatic cells
- P4D Cylindrical: Pseudo-four-dimensional model for cylindrical cells

## P2D model
Expand Down Expand Up @@ -185,4 +186,4 @@ This table lists all required parameters from the DFN model used in BattMo.



## P4D Cylindrical
## P4D Cylindrical
4 changes: 2 additions & 2 deletions docs/src/manuals/user_guide/sub_models.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

| ModelSetting | Sub-model(s) | Description |
|-----------------------------------|-------------------------------------------|-----------------------------------------------------------------------|
| `"ModelFrameWork"` | "P2D", "P4D Pouch", "P4D Cylindrical" | [See the PXD section](../user_guide/pxd_model.md) |
| `"ModelFrameWork"` | "P2D", "P4D Pouch", "P4D Prismatic", "P4D Cylindrical" | [See the PXD section](../user_guide/pxd_model.md) |
| `"TransportInSolid"` | "FullDiffusion" | - |
| `"RampUp"` | "Sinusoidal" | [See the Ramp Up section](../user_guide/ramp_up.md) |
| `"ButlerVolmer"` | "Standard", "Chayambuka" | [See the Sodium ion section](../user_guide/sodium_ion_model.md) |
Expand Down Expand Up @@ -43,4 +43,4 @@ The model settings can then be pased to the battery model struct that you'd like

```
model = LithiumIonBattery(;model_settings = model_settings)
```
```
12 changes: 9 additions & 3 deletions examples/example_3D_cylindrical.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ nothing #hide
# ## Review and modify the cell parameters
# We go through some of the geometrical and discretization parameters. We modify some of them to obtain a cell where the different components are easier to visualize

# The cell geometry is determined by the inner and outer radius and the height. We reduce the outer radius
cell_parameters["Cell"]["OuterRadius"] = 0.01
# The wound geometry is determined primarily by the inner radius, the winding
# length and the jelly-roll height. We shorten the winding length here to make
# the geometry more compact.
cell_parameters["Cell"]["ElectrodeWidth"] = 0.065
cell_parameters["Cell"]["ElectrodeLength"] = 1.6
cell_parameters["Cell"]["InnerRadius"] = 2e-3
nothing #hide

# We modify the current collector thicknesses, for visualization purpose
Expand Down Expand Up @@ -125,7 +129,9 @@ simulation_settings = load_simulation_settings(; from_default_set = "p4d_cylindr

# We adjust the parameters so that the simulation in this example is not too long (around a couple of minutes)

cell_parameters["Cell"]["OuterRadius"] = 0.004
cell_parameters["Cell"]["ElectrodeWidth"] = 0.065
cell_parameters["Cell"]["ElectrodeLength"] = 0.55
cell_parameters["Cell"]["InnerRadius"] = 2e-3
cell_parameters["NegativeElectrode"]["CurrentCollector"]["TabFractions"] = [0.5]
cell_parameters["PositiveElectrode"]["CurrentCollector"]["TabFractions"] = [0.5]
cell_parameters["NegativeElectrode"]["CurrentCollector"]["TabWidth"] = 0.002
Expand Down
94 changes: 94 additions & 0 deletions examples/example_3d_prismatic.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# # 3D wound prismatic cell geometry and plotting
#
# This example demonstrates how to set up, run and visualize a wound prismatic
# battery model. The internal geometry is a jelly roll, like the cylindrical
# model, but it is exposed through the `P4D Prismatic` framework so the setup
# can represent a wound cell packaged in a prismatic form factor.

using BattMo, Jutul, GLMakie

# ## Load parameter sets

cell_parameters = load_cell_parameters(; from_default_set = "chen_2020")
cycling_protocol = load_cycling_protocol(; from_default_set = "cc_discharge")
model_settings = load_model_settings(; from_default_set = "p4d_prismatic")
simulation_settings = load_simulation_settings(; from_default_set = "p4d_prismatic")
nothing #hide

# ## Adjust the geometry

# A more compact winding length and thicker current collectors make the wound
# structure easier to inspect visually.
cell_parameters["Cell"]["Case"] = "Prismatic"
cell_parameters["Cell"]["ElectrodeWidth"] = 0.065
cell_parameters["Cell"]["ElectrodeLength"] = 0.5
cell_parameters["Cell"]["InnerRadius"] = 2e-3
cell_parameters["Cell"]["CaseWidth"] = 0.03
cell_parameters["Cell"]["CaseThickness"] = 0.010
cell_parameters["NegativeElectrode"]["CurrentCollector"]["Thickness"] = 50.0e-6
cell_parameters["PositiveElectrode"]["CurrentCollector"]["Thickness"] = 50.0e-6

# Tabs are specified as fractions of the total spiral length of each current
# collector, which is the natural coordinate for wound-cell tab placement.
cell_parameters["NegativeElectrode"]["CurrentCollector"]["TabFractions"] = [0.2, 0.5, 0.8]
cell_parameters["PositiveElectrode"]["CurrentCollector"]["TabFractions"] = [0.2, 0.5, 0.8]
cell_parameters["NegativeElectrode"]["CurrentCollector"]["TabWidth"] = 0.002
cell_parameters["PositiveElectrode"]["CurrentCollector"]["TabWidth"] = 0.002

simulation_settings["AngularGridPoints"] = 30
nothing #hide

# ## Create the simulation object

model = LithiumIonBattery(; model_settings)
sim = Simulation(model, cell_parameters, cycling_protocol; simulation_settings)

grids = sim.grids
couplings = sim.couplings
nothing #hide

# ## Visualize the wound component meshes

components = ["NegativeElectrode", "PositiveElectrode", "NegativeCurrentCollector", "PositiveCurrentCollector"]
colors = [:gray, :green, :blue, :black]
nothing #hide

for (i, component) in enumerate(components)
if i == 1
global fig, ax = plot_mesh(grids[component], color = colors[i])
else
plot_mesh!(ax, grids[component], color = colors[i])
end
plot_mesh_edges!(ax, grids[component], color = :black, linewidth = 0.6)
end
ax.aspect = :data
ax.azimuth[] = 5.0
ax.elevation[] = 0.35
display(GLMakie.Screen(), fig)
fig #hide

# ## Highlight the tab couplings

for component in ["NegativeCurrentCollector", "PositiveCurrentCollector"]
plot_mesh!(
ax, grids[component];
boundaryfaces = couplings[component]["External"]["boundaryfaces"],
color = :red,
)
end

ax.aspect = :data
ax.azimuth[] = 5.0
ax.elevation[] = 0.35
display(GLMakie.Screen(), fig)
fig #hide

# # ## Run a compact simulation
# output = solve(sim; info_level = -1)
# nothing #hide

# # ## Visualize the simulation output

# plot_dashboard(output; plot_type = "simple")

# plot_interactive_3d(output)
1 change: 1 addition & 0 deletions src/BattMo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ include("grid/grid_conversion.jl")
include("grid/grid_utils.jl")
include("grid/geometries/1d.jl")
include("grid/geometries/pouch.jl")
include("grid/geometries/prismatic.jl")
include("grid/geometries/jelly_roll.jl")

include("solver/solver_as_preconditioner_system.jl")
Expand Down
76 changes: 72 additions & 4 deletions src/grid/geometries/jelly_roll.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,73 @@ export jelly_roll_grid
# jelly roll grid setup #
#########################

function get_jelly_roll_stack_thickness(cell_parameters)
dxs = [
cell_parameters["PositiveElectrode"]["Coating"]["Thickness"],
cell_parameters["PositiveElectrode"]["CurrentCollector"]["Thickness"],
cell_parameters["PositiveElectrode"]["Coating"]["Thickness"],
cell_parameters["Separator"]["Thickness"],
cell_parameters["NegativeElectrode"]["Coating"]["Thickness"],
cell_parameters["NegativeElectrode"]["CurrentCollector"]["Thickness"],
cell_parameters["NegativeElectrode"]["Coating"]["Thickness"],
cell_parameters["Separator"]["Thickness"],
]
return sum(dxs)
end

function jelly_roll_arc_length(outer_radius, inner_radius, stack_thickness)
A = stack_thickness / (2 * pi)
u0 = inner_radius
u1 = outer_radius
F(u) = u * sqrt(u^2 + A^2) + A^2 * asinh(u / A)
return (F(u1) - F(u0)) / (2 * A)
end

function outer_radius_from_winding_length(winding_length, inner_radius, stack_thickness)
@assert winding_length > 0.0 "ElectrodeLength must be positive."
@assert inner_radius > 0.0 "InnerRadius must be positive."
@assert stack_thickness > 0.0 "Cell stack thickness must be positive."

lo = inner_radius
hi = inner_radius + stack_thickness
while jelly_roll_arc_length(hi, inner_radius, stack_thickness) < winding_length
hi *= 2
end

for _ in 1:80
mid = 0.5 * (lo + hi)
if jelly_roll_arc_length(mid, inner_radius, stack_thickness) < winding_length
lo = mid
else
hi = mid
end
end
return hi
end

function get_jelly_roll_dimensions(cell_parameters)
cell = cell_parameters["Cell"]
inner_radius = cell["InnerRadius"]
height = get(cell, "ElectrodeWidth", get(cell, "Height", nothing))
if isnothing(height)
error("Jelly-roll geometries require Cell/ElectrodeWidth, or Cell/Height as a fallback.")
end

stack_thickness = get_jelly_roll_stack_thickness(cell_parameters)
winding_length = get(cell, "ElectrodeLength", nothing)

outer_radius =
if !isnothing(winding_length)
outer_radius_from_winding_length(winding_length, inner_radius, stack_thickness)
elseif haskey(cell, "OuterRadius")
cell["OuterRadius"]
else
error("Jelly-roll geometries require Cell/ElectrodeLength, or Cell/OuterRadius as a fallback.")
end

return (inner_radius = inner_radius, outer_radius = outer_radius, height = height, stack_thickness = stack_thickness)
end

function jelly_roll_grid(input)

cell_parameters = input.cell_parameters
Expand All @@ -13,9 +80,10 @@ function jelly_roll_grid(input)

nangles = simulation_settings["AngularGridPoints"]
nz = simulation_settings["HeightGridPoints"]
rinner = cell["InnerRadius"]
router = cell["OuterRadius"]
height = cell["Height"]
dims = get_jelly_roll_dimensions(cell_parameters)
rinner = dims.inner_radius
router = dims.outer_radius
height = dims.height

function get_vector(geomparams, fdname)
# double coated electrode
Expand Down Expand Up @@ -57,7 +125,7 @@ function jelly_roll_grid(input)
spacing = [0; cumsum(dx)]
spacing = spacing / spacing[end]

thickness = sum(dxs)
thickness = dims.stack_thickness

depths = [0; cumsum(repeat([height / nz], nz))]

Expand Down
Loading
Loading