Grid Module (softpotato.grid)¶
The softpotato.grid subpackage provides 1D spatial discretization meshes and grid generators for numerical PDE simulations, including uniform, geometrically expanding, and exponential/conformal-mapping meshes, as well as automatic domain sizing from the semi-infinite diffusion layer thickness \(\delta = \text{factor} \cdot \sqrt{D \, t_{\max}}\).
Why Non-Uniform Spatial Discretization?¶
In electrochemical simulations, concentration perturbations and diffusive fluxes are concentrated within a narrow diffusion boundary layer adjacent to the working electrode surface (\(x = x_{\min}\)), while the bulk solution (\(\nabla^2 c \approx 0\)) experiences negligible gradients.
- Uniform grids (
Uniform1DMesh) distribute spatial nodes equally across the entire domain, requiring hundreds or thousands of nodes to resolve steep interfacial gradients \(\left.\partial c / \partial x\right|_{x_{\min}}\) while oversampling the quiescent bulk. - Expanding and stretched grids (
GeometricExpandingMeshandExponentialMesh) cluster fine intervals (\(\Delta x_0 \ll \delta\)) near active boundaries and smoothly coarsen into the bulk solution, achieving 5–20× faster simulation runtimes and 80–90% fewer spatial nodes at equal or superior flux accuracy while preserving second-order spatial convergence (\(\mathcal{O}(\Delta x^2)\)).
Mesh Classes and Generators¶
| Class / Function | Spacing Profile | Key Parameters | Description |
|---|---|---|---|
Mesh1D |
Abstract | nodes, dx |
Abstract base class implementing the 1D mesh interface and NumPy array protocol (__array__, __array_ufunc__, slicing, arithmetic). |
Uniform1DMesh |
\(\Delta x_j = \Delta x\) | x_min, x_max, n_points or dx |
Equidistant 1D spatial mesh with constant node spacing \(\Delta x = (x_{\max} - x_{\min}) / (N - 1)\). |
GeometricExpandingMesh |
\(\Delta x_j = \Delta x_0 \gamma^j\) | x_min, x_max, any two of (n_points, gamma, dx_min), two_sided |
Non-uniform mesh with constant interval expansion ratio \(\gamma > 1\). |
ExponentialMesh |
\(x(\xi)\) via exponential, tanh, or sinh |
x_min, x_max, n_points, beta, two_sided, mapping |
Smooth non-uniform mesh using exponential or hyperbolic coordinate stretching of uniform \(\xi \in [0, 1]\). |
create_diffusion_mesh |
'geometric', 'exponential', or 'uniform' |
D, t_max, kind, factor |
High-level factory constructing a 1D mesh automatically sized to \(\delta = \text{factor} \cdot \sqrt{D \, t_{\max}}\). |
diffusion_layer_thickness |
Scalar \(\delta\) | D, t_max, factor |
Estimates the characteristic semi-infinite diffusion layer thickness \(\delta = \text{factor} \cdot \sqrt{D \, t_{\max}}\). |
Mathematical Formulations¶
1. Physics-Informed Domain Sizing¶
For semi-infinite diffusion over duration \(t_{\max}\) with diffusivity \(D\), the characteristic penetration thickness is:
At the default factor=6.0, the complementary error function decays to \(\operatorname{erfc}(6 / 2) \approx 2.2 \times 10^{-5}\), ensuring outer boundary perturbations remain negligible.
2. Geometrically Expanding Meshes (GeometricExpandingMesh)¶
Consecutive cell intervals expand by a constant geometric factor \(\gamma > 1\):
where the total domain length satisfies the geometric series:
GeometricExpandingMesh supports three flexible parameterizations by specifying any two of n_points (\(N\)), gamma (\(\gamma\)), and dx_min (\(\Delta x_0\)):
1. (n_points, gamma): Solves analytically for the initial boundary step \(\Delta x_0\).
2. (dx_min, gamma): Computes the required node count \(N\) so the boundary step is at most dx_min.
3. (n_points, dx_min): Solves numerically via Brent's root-finding method (scipy.optimize.brentq) for the exact expansion ratio \(\gamma > 1\).
3. Exponential & Conformal-Mapping Meshes (ExponentialMesh)¶
Maps uniform computational coordinates \(\xi_j = j / (N - 1) \in [0, 1]\) to physical coordinates \(x(\xi) \in [x_{\min}, x_{\max}]\) parameterized by a dimensionless stretching factor \(\beta > 0\):
- Exponential mapping (
mapping="exponential", default):
$$ x(\xi) = x_{\min} + (x_{\max} - x_{\min}) \frac{\exp(\beta \xi) - 1}{\exp(\beta) - 1} $$
- Hyperbolic tangent mapping (
mapping="tanh"orExponentialMesh.from_tanh):
$$ x(\xi) = x_{\min} + (x_{\max} - x_{\min}) \left(1 - \frac{\tanh(\beta (1 - \xi))}{\tanh(\beta)}\right) $$
- Hyperbolic sine mapping (
mapping="sinh"orExponentialMesh.from_sinh):
$$ x(\xi) = x_{\min} + (x_{\max} - x_{\min}) \frac{\sinh(\beta \xi)}{\sinh(\beta)} $$
4. Two-Sided Symmetric Clustering (two_sided=True)¶
Both GeometricExpandingMesh and ExponentialMesh support two_sided=True, clustering fine spatial intervals symmetrically at both \(x_{\min}\) and \(x_{\max}\) while coarsening toward the domain midpoint \(x_{\text{mid}} = \frac{1}{2}(x_{\min} + x_{\max})\). This is ideal for thin-layer electrochemical cells, SECM tip–substrate gaps (generation–collection / feedback), and dual-working-electrode (electrodes="both") simulations.
User Guide & Examples¶
1. Automatic Diffusion-Layer Mesh Generation¶
Use create_diffusion_mesh or the .from_diffusion_layer() classmethods to automatically size the domain to \(\delta = 6\sqrt{D t_{\max}}\):
from softpotato.grid import (
ExponentialMesh,
GeometricExpandingMesh,
Uniform1DMesh,
create_diffusion_mesh,
)
D = 1e-9 # m^2/s
t_max = 1.0 # s
# High-level factory (defaults to kind="geometric", factor=6.0)
mesh_geom = create_diffusion_mesh(
D=D, t_max=t_max, kind="geometric", n_points=60, gamma=1.08
)
# Or construct directly via classmethods
mesh_exp = ExponentialMesh.from_diffusion_layer(
D=D, t_max=t_max, n_points=60, beta=3.5, mapping="sinh"
)
mesh_uni = Uniform1DMesh.from_diffusion_layer(D=D, t_max=t_max, n_points=300)
print(f"Domain bounds: {mesh_geom.bounds}")
print(f"Geometric dx_min = {mesh_geom.dx_min:.3e} m, dx_max = {mesh_geom.dx_max:.3e} m")
2. Three Parameterizations of GeometricExpandingMesh¶
Specify any two of n_points, gamma, and dx_min:
from softpotato.grid import GeometricExpandingMesh
# 1. Specify (n_points, gamma) -> computes dx_min
m1 = GeometricExpandingMesh(0.0, 1e-3, n_points=51, gamma=1.08)
# 2. Specify (dx_min, gamma) -> determines required n_points
m2 = GeometricExpandingMesh(0.0, 1e-3, dx_min=1e-6, gamma=1.08)
# 3. Specify (n_points, dx_min) -> solves for gamma > 1
m3 = GeometricExpandingMesh(0.0, 1e-3, n_points=51, dx_min=1e-6)
# Two-sided symmetric mesh for thin-layer / SECM cells
m_thin = GeometricExpandingMesh(0.0, 50e-6, n_points=61, gamma=1.06, two_sided=True)
3. Coupling Expanding Grids with DiffusionProblem and Solvers¶
All Mesh1D subclasses implement the NumPy array protocol and integrate seamlessly with DiffusionProblem and all Soft Potato solvers (scipy_ivp, crank_nicolson, implicit, explicit) across planar, spherical, and cylindrical geometries:
from softpotato.grid import create_diffusion_mesh
from softpotato.solver import DiffusionProblem, DirichletBC, get_solver
D = 1e-9
t_max = 1.0
mesh = create_diffusion_mesh(
D=D, t_max=t_max, kind="geometric", n_points=60, gamma=1.08
)
problem = DiffusionProblem(
grid=mesh,
diffusivity={"Ox": D},
boundary_conditions={"Ox": (DirichletBC(0.0), DirichletBC(1.0))},
initial_conditions={"Ox": 1.0},
geometry="planar",
n_electrons=1,
)
solver = get_solver("scipy_ivp")
result = solver.solve(problem, t_span=(0.0, t_max))
print(result.grid is mesh) # True: preserves Mesh1D reference
print(result.currents["Ox"].shape) # (100,) Faradaic current transient in Amperes
Module Contents¶
grid
¶
Spatial discretization meshes and grid generators for numerical simulation.
Mesh1D
¶
Bases: ABC
Abstract base class representing a 1D spatial discretization mesh.
Encapsulates 1D coordinate points \(x_j\), cell intervals \(\Delta x_j = x_{j+1} - x_j\), spatial domain boundaries, and grid uniformity diagnostics.
Provides full interoperability with the NumPy array protocol (slicing, indexing, mathematical operators, and universal functions).
Attributes:
| Name | Type | Description |
|---|---|---|
nodes |
ndarray
|
1D array of spatial node positions. |
dx |
ndarray
|
Interval spacings between consecutive nodes: \(\Delta x_j = x_{j+1} - x_j\). |
n_points |
int
|
Total number of spatial grid nodes \(N\). |
bounds |
tuple[float, float]
|
Spatial domain boundaries \((x_{\min}, x_{\max})\). |
is_uniform |
bool
|
Whether the grid spacing is uniform within numerical tolerance. |
Source code in src/softpotato/grid/base.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | |
dx
abstractmethod
property
¶
Interval spacings between consecutive nodes: dx[j] = nodes[j+1] - nodes[j].
is_uniform
property
¶
Whether the grid spacing is uniform within numerical tolerance.
__array__
¶
NumPy array protocol support: np.asarray(mesh) yields mesh.nodes.
Source code in src/softpotato/grid/base.py
__len__
¶
__getitem__
¶
__iter__
¶
__array_ufunc__
¶
Delegate NumPy ufunc evaluations to the underlying nodes array.
Source code in src/softpotato/grid/base.py
ExponentialMesh
¶
Bases: Mesh1D
Exponential and conformal-mapping 1D spatial discretization mesh.
Generates a smooth, non-uniform 1D spatial grid across \([x_{\min}, x_{\max}]\) parameterized by a stretching factor \(\beta > 0\) applied to uniform computational coordinates \(\xi \in [0, 1]\).
For the default "exponential" mapping (one-sided):
Also supports conformal / boundary-layer coordinate transformations:
-
"tanh"(hyperbolic tangent coordinate clustering):\[ x(\xi) = x_{\min} + (x_{\max} - x_{\min}) \left(1 - \frac{\tanh(\beta (1 - \xi))}{\tanh(\beta)}\right) \] -
"sinh"(hyperbolic sine conformal stretching):\[ x(\xi) = x_{\min} + (x_{\max} - x_{\min}) \frac{\sinh(\beta \xi)}{\sinh(\beta)} \]
When two_sided=True, nodes are symmetrically clustered near both
boundaries (\(x_{\min}\) and \(x_{\max}\)) while coarsening toward
the domain center.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_min
|
float
|
Lower boundary of the spatial domain (\(x_{\min}\)). |
required |
x_max
|
float
|
Upper boundary of the spatial domain (\(x_{\max}\)).
Must be strictly greater than |
required |
n_points
|
int
|
Total number of grid nodes \(N\). Must be \(\ge 2\) (or \(\ge 3\)
when |
required |
beta
|
float
|
Dimensionless stretching parameter \(\beta > 0\). Larger values concentrate more nodes near the boundary. |
3.0
|
two_sided
|
bool
|
If |
False
|
mapping
|
str
|
Coordinate transformation function: |
"exponential"
|
Attributes:
| Name | Type | Description |
|---|---|---|
nodes |
ndarray
|
1D array of spatial node positions of length \(N\). |
dx |
ndarray
|
Interval spacings between consecutive nodes of length \(N-1\). |
xi |
ndarray
|
Uniform computational coordinates \(\xi \in [0, 1]\) of length \(N\). |
beta |
float
|
Stretching parameter \(\beta > 0\). |
two_sided |
bool
|
Whether the mesh is symmetrically clustered at both boundaries. |
mapping |
str
|
Name of the coordinate mapping transformation. |
dx_min |
float
|
Minimum interval spacing across the mesh. |
dx_max |
float
|
Maximum interval spacing across the mesh. |
n_points |
int
|
Total number of spatial nodes \(N\). |
bounds |
tuple[float, float]
|
Spatial domain boundaries \((x_{\min}, x_{\max})\). |
is_uniform |
bool
|
Always |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If inputs have invalid types. |
Examples:
>>> from softpotato.grid import ExponentialMesh
>>> mesh = ExponentialMesh(0.0, 1.0, n_points=51, beta=3.0)
>>> mesh.bounds
(0.0, 1.0)
>>> mesh.is_uniform
False
Source code in src/softpotato/grid/exponential.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 | |
two_sided
property
¶
Whether the mesh is symmetrically clustered at both boundaries.
is_uniform
property
¶
Whether the grid spacing is uniform within numerical tolerance.
from_tanh
classmethod
¶
from_tanh(x_min: float, x_max: float, n_points: int, beta: float = 2.5, two_sided: bool = False) -> ExponentialMesh
Construct a mesh using hyperbolic tangent (tanh) coordinate stretching.
Source code in src/softpotato/grid/exponential.py
from_sinh
classmethod
¶
from_sinh(x_min: float, x_max: float, n_points: int, beta: float = 3.0, two_sided: bool = False) -> ExponentialMesh
Construct a mesh using hyperbolic sine (sinh) conformal stretching.
Source code in src/softpotato/grid/exponential.py
from_diffusion_layer
classmethod
¶
from_diffusion_layer(D: float, t_max: float, *, x_min: float = 0.0, factor: float = 6.0, n_points: int = 100, beta: float = 3.0, two_sided: bool = False, mapping: str = 'exponential') -> ExponentialMesh
Create an exponential mesh sized to the diffusion layer thickness.
Sets \(x_{\max} = x_{\min} + \text{factor} \cdot \sqrt{D \, t_{\max}}\),
where factor defaults to \(6.0\) (\(\delta \approx 6 \sqrt{D t_{\max}}\)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
D
|
float
|
Diffusion coefficient \(D > 0\) (\(\text{m}^2/\text{s}\)). |
required |
t_max
|
float
|
Maximum characteristic simulation time \(t_{\max} > 0\) (\(\text{s}\)). |
required |
x_min
|
float
|
Electrode surface coordinate \(x_{\min}\). |
0.0
|
factor
|
float
|
Dimensionless multiplier for diffusion layer thickness \(\delta = \text{factor} \cdot \sqrt{D t_{\max}}\). |
6.0
|
n_points
|
int
|
Total number of grid nodes \(N\). |
100
|
beta
|
float
|
Dimensionless stretching parameter \(\beta > 0\). |
3.0
|
two_sided
|
bool
|
Whether to cluster nodes symmetrically at both boundaries. |
False
|
mapping
|
str
|
Coordinate transformation function: |
"exponential"
|
Returns:
| Type | Description |
|---|---|
ExponentialMesh
|
Configured exponential mesh spanning \([x_{\min}, x_{\min} + \delta]\). |
Source code in src/softpotato/grid/exponential.py
GeometricExpandingMesh
¶
Bases: Mesh1D
Geometrically expanding 1D spatial discretization mesh.
Generates a non-uniform spatial grid where consecutive interval steps scale by a constant expansion ratio \(\gamma > 1\):
Supports three construction parameterizations (specify any two of
n_points, gamma, and dx_min):
(x_min, x_max, n_points, gamma): Computes initial step \(\Delta x_0\) so that the geometric series sums to \(x_{\max} - x_{\min}\).(x_min, x_max, dx_min, gamma): Automatically determines the required number of nodes \(N\) so the initial step is at mostdx_min.(x_min, x_max, n_points, dx_min): Numerically solves for the expansion ratio \(\gamma > 1\) that satisfies the domain length anddx_min.
Also supports two-sided symmetric expansion (two_sided=True), providing
fine spatial resolution \(\Delta x_0\) at both boundaries (\(x_{\min}\)
and \(x_{\max}\)) while coarsening toward the domain midpoint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_min
|
float
|
Lower boundary of the spatial domain (\(x_{\min}\)). |
required |
x_max
|
float
|
Upper boundary of the spatial domain (\(x_{\max}\)).
Must be strictly greater than |
required |
n_points
|
int | None
|
Total number of grid nodes \(N\). |
None
|
gamma
|
float | None
|
Geometric expansion ratio \(\gamma > 1\). |
None
|
dx_min
|
float | None
|
Minimum (boundary) interval spacing \(\Delta x_0 > 0\). |
None
|
two_sided
|
bool
|
If |
False
|
Attributes:
| Name | Type | Description |
|---|---|---|
nodes |
ndarray
|
1D array of spatial node positions of length \(N\). |
dx |
ndarray
|
Interval spacings between consecutive nodes of length \(N-1\). |
gamma |
float
|
Geometric expansion ratio \(\gamma > 1\). |
expansion_ratio |
float
|
Alias for |
dx_min |
float
|
Initial (minimum) boundary step size \(\Delta x_0\). |
dx_0 |
float
|
Alias for |
dx_max |
float
|
Maximum interval spacing across the mesh. |
two_sided |
bool
|
Whether the mesh expands symmetrically from both boundaries. |
n_points |
int
|
Total number of spatial nodes \(N\). |
bounds |
tuple[float, float]
|
Spatial domain boundaries \((x_{\min}, x_{\max})\). |
is_uniform |
bool
|
Always |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If parameters have invalid types. |
Examples:
Constructing from n_points and gamma:
>>> from softpotato.grid import GeometricExpandingMesh
>>> mesh = GeometricExpandingMesh(0.0, 1.0, n_points=21, gamma=1.1)
>>> mesh.n_points
21
>>> mesh.bounds
(0.0, 1.0)
>>> mesh.is_uniform
False
Source code in src/softpotato/grid/geometric.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | |
is_uniform
property
¶
Whether the grid spacing is uniform within numerical tolerance.
from_diffusion_layer
classmethod
¶
from_diffusion_layer(D: float, t_max: float, *, x_min: float = 0.0, factor: float = 6.0, n_points: int | None = None, gamma: float | None = None, dx_min: float | None = None, two_sided: bool = False) -> GeometricExpandingMesh
Create a geometric expanding mesh sized to the diffusion layer thickness.
Sets \(x_{\max} = x_{\min} + \text{factor} \cdot \sqrt{D \, t_{\max}}\),
where factor defaults to \(6.0\) (\(\delta \approx 6 \sqrt{D t_{\max}}\)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
D
|
float
|
Diffusion coefficient \(D > 0\) (\(\text{m}^2/\text{s}\)). |
required |
t_max
|
float
|
Maximum characteristic simulation time \(t_{\max} > 0\) (\(\text{s}\)). |
required |
x_min
|
float
|
Electrode surface coordinate \(x_{\min}\). |
0.0
|
factor
|
float
|
Dimensionless multiplier for diffusion layer thickness \(\delta = \text{factor} \cdot \sqrt{D t_{\max}}\). |
6.0
|
n_points
|
int | None
|
Total number of grid nodes \(N\). Defaults to |
None
|
gamma
|
float | None
|
Geometric expansion ratio \(\gamma > 1\). Defaults to |
None
|
dx_min
|
float | None
|
Initial boundary step size \(\Delta x_0 > 0\). |
None
|
two_sided
|
bool
|
Whether to expand symmetrically from both boundaries. |
False
|
Returns:
| Type | Description |
|---|---|
GeometricExpandingMesh
|
Configured geometric mesh spanning \([x_{\min}, x_{\min} + \delta]\). |
Source code in src/softpotato/grid/geometric.py
Uniform1DMesh
¶
Bases: Mesh1D
Uniform 1D spatial discretization mesh with equidistant nodes.
Generates spatial coordinate nodes \(x_j\) with constant spacing:
where \(\Delta x = (x_{\max} - x_{\min}) / (N - 1)\).
Can be constructed either by specifying the total number of points \(N\)
(n_points) or the desired uniform spacing \(\Delta x\) (dx).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_min
|
float
|
Lower boundary of the spatial domain (\(x_{\min}\)). |
required |
x_max
|
float
|
Upper boundary of the spatial domain (\(x_{\max}\)).
Must be strictly greater than |
required |
n_points
|
int | None
|
Total number of grid nodes \(N\). Must be \(\ge 2\).
Exactly one of |
None
|
dx
|
float | None
|
Target uniform node spacing \(\Delta x\). Must be strictly positive
and evenly divide the domain length (\(x_{\max} - x_{\min}\)).
Exactly one of |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
nodes |
ndarray
|
1D array of spatial node positions of length \(N\). |
dx |
ndarray
|
Array of consecutive node intervals of length \(N-1\), where every element equals \(\Delta x\). |
spacing |
float
|
Constant scalar grid spacing \(\Delta x\). |
step |
float
|
Alias for |
n_points |
int
|
Total number of spatial nodes \(N\). |
bounds |
tuple[float, float]
|
Spatial domain boundaries \((x_{\min}, x_{\max})\). |
is_uniform |
bool
|
Always |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If |
Examples:
Constructing with point count:
>>> from softpotato.grid import Uniform1DMesh
>>> mesh = Uniform1DMesh(0.0, 1.0, n_points=5)
>>> mesh.nodes
array([0. , 0.25, 0.5 , 0.75, 1. ])
>>> mesh.spacing
0.25
>>> mesh.is_uniform
True
Constructing with spacing step:
Source code in src/softpotato/grid/uniform.py
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | |
is_uniform
property
¶
Whether the grid spacing is uniform within numerical tolerance.
from_diffusion_layer
classmethod
¶
from_diffusion_layer(D: float, t_max: float, *, x_min: float = 0.0, factor: float = 6.0, n_points: int | None = None, dx: float | None = None) -> Uniform1DMesh
Create a uniform mesh sized to the diffusion layer thickness.
Sets \(x_{\max} = x_{\min} + \text{factor} \cdot \sqrt{D \, t_{\max}}\),
where factor defaults to \(6.0\) (\(\delta \approx 6 \sqrt{D t_{\max}}\)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
D
|
float
|
Diffusion coefficient \(D > 0\) (\(\text{m}^2/\text{s}\)). |
required |
t_max
|
float
|
Maximum characteristic simulation time \(t_{\max} > 0\) (\(\text{s}\)). |
required |
x_min
|
float
|
Electrode surface coordinate \(x_{\min}\). |
0.0
|
factor
|
float
|
Dimensionless multiplier for diffusion layer thickness \(\delta = \text{factor} \cdot \sqrt{D t_{\max}}\). |
6.0
|
n_points
|
int | None
|
Total number of grid nodes \(N\). Defaults to |
None
|
dx
|
float | None
|
Uniform grid spacing \(\Delta x\). |
None
|
Returns:
| Type | Description |
|---|---|
Uniform1DMesh
|
Configured uniform mesh spanning \([x_{\min}, x_{\min} + \delta]\). |
Source code in src/softpotato/grid/uniform.py
diffusion_layer_thickness
¶
Estimate the semi-infinite diffusion layer thickness \(\delta\).
Computes the characteristic domain extent required to simulate semi-infinite diffusion over duration \(t_{\max}\) without outer boundary interference:
At the default factor=6.0, \(\operatorname{erfc}(6 / 2) \approx 2.2 \times 10^{-5}\),
ensuring concentration perturbations at \(x = \delta\) remain negligible.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
D
|
float
|
Species diffusion coefficient \(D > 0\) (\(\text{m}^2/\text{s}\)). |
required |
t_max
|
float
|
Maximum characteristic simulation time \(t_{\max} > 0\) (\(\text{s}\)). |
required |
factor
|
float
|
Dimensionless multiplier \(> 0\) applied to \(\sqrt{D t_{\max}}\). |
6.0
|
Returns:
| Type | Description |
|---|---|
float
|
Estimated diffusion layer thickness \(\delta > 0\). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If inputs are not real numbers. |
Source code in src/softpotato/grid/base.py
create_diffusion_mesh
¶
create_diffusion_mesh(D: float, t_max: float, *, kind: str = 'geometric', x_min: float = 0.0, factor: float = 6.0, n_points: int | None = None, gamma: float | None = None, dx_min: float | None = None, beta: float = 3.0, dx: float | None = None, two_sided: bool = False, mapping: str = 'exponential') -> Mesh1D
Create a 1D spatial mesh sized to the diffusion layer thickness \(\delta\).
Automatically sets the outer domain boundary to
\(x_{\max} = x_{\min} + \text{factor} \cdot \sqrt{D \, t_{\max}}\)
(with default \(\delta = 6 \sqrt{D t_{\max}}\)) and constructs a
GeometricExpandingMesh, ExponentialMesh, or Uniform1DMesh.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
D
|
float
|
Species diffusion coefficient \(D > 0\) (\(\text{m}^2/\text{s}\)). |
required |
t_max
|
float
|
Maximum characteristic simulation time \(t_{\max} > 0\) (\(\text{s}\)). |
required |
kind
|
str
|
Mesh generator type: |
"geometric"
|
x_min
|
float
|
Lower boundary coordinate \(x_{\min}\). |
0.0
|
factor
|
float
|
Dimensionless multiplier for diffusion layer thickness \(\delta = \text{factor} \cdot \sqrt{D t_{\max}}\). |
6.0
|
n_points
|
int | None
|
Total number of spatial nodes \(N\). |
None
|
gamma
|
float | None
|
Expansion ratio \(\gamma > 1\) (for |
None
|
dx_min
|
float | None
|
Minimum boundary step size \(\Delta x_0 > 0\) (for |
None
|
beta
|
float
|
Stretching parameter \(\beta > 0\) (for |
3.0
|
dx
|
float | None
|
Uniform step size \(\Delta x > 0\) (for |
None
|
two_sided
|
bool
|
Whether to cluster nodes symmetrically at both boundaries
(for |
False
|
mapping
|
str
|
Coordinate transformation ( |
"exponential"
|
Returns:
| Type | Description |
|---|---|
Mesh1D
|
Constructed 1D mesh instance spanning \([x_{\min}, x_{\min} + \delta]\). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If |
Source code in src/softpotato/grid/__init__.py
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |