Skip to content

Commit d13e86b

Browse files
committed
Python: add finite-volume derivative matrix builder (build_matrices)
1 parent d566728 commit d13e86b

File tree

4 files changed

+83
-0
lines changed

4 files changed

+83
-0
lines changed

chromosim/__init__.py

Whitespace-only changes.

chromosim/numerics/__init__.py

Whitespace-only changes.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# chromosim/numerics/build_matrices.py
2+
"""
3+
Finite-volume derivative matrices in z for 1D packed-column models.
4+
5+
This is a direct Python/NumPy/SciPy port of the MATLAB build_matrices.m:
6+
7+
- First derivative (D1): backward/upwind stencil, inlet row is zeroed
8+
because the inlet boundary condition is handled explicitly in the ODE.
9+
- Second derivative (D2): central stencil with:
10+
* inlet ghost-cell treatment: D2[0,0] = -2/dz^2 (Dirichlet-type)
11+
* Danckwerts-style outlet: D2[-1,-1] = 1/dz^2
12+
13+
Returns the same 'par' object/dict with .D1 and .D2 as CSR sparse matrices.
14+
"""
15+
16+
from __future__ import annotations
17+
import numpy as np
18+
from scipy.sparse import diags
19+
20+
def _get(par, key):
21+
"""Support both dicts and objects with attributes."""
22+
try:
23+
return getattr(par, key)
24+
except AttributeError:
25+
return par[key]
26+
27+
def _set(par, key, value):
28+
"""Support both dicts and objects with attributes."""
29+
try:
30+
setattr(par, key, value)
31+
except AttributeError:
32+
par[key] = value
33+
34+
def build_matrices(par):
35+
"""
36+
Build derivative matrices and attach them to `par`.
37+
38+
Parameters
39+
----------
40+
par : dict or object with attributes
41+
Required fields:
42+
- N : int number of axial cells
43+
- dz : float cell length (m)
44+
45+
Side effects
46+
------------
47+
Sets:
48+
par.D1 : (N x N) scipy.sparse.csr_matrix
49+
par.D2 : (N x N) scipy.sparse.csr_matrix
50+
51+
Returns
52+
-------
53+
par : same object/dict, for chaining.
54+
"""
55+
N = int(_get(par, "N"))
56+
dz = float(_get(par, "dz"))
57+
58+
e = np.ones(N)
59+
60+
# 1) First derivative: backward/upwind
61+
D1 = diags([-e, e], offsets=[-1, 0], shape=(N, N), format="lil") / dz
62+
D1[0, :] = 0.0 # inlet row is handled via the boundary condition
63+
D1 = D1.tocsr()
64+
65+
# 2) Second derivative: central
66+
D2 = diags([e, -2*e, e], offsets=[-1, 0, 1], shape=(N, N), format="lil") / dz**2
67+
D2[0, 0] = -2.0 / dz**2 # inlet ghost cell (Dirichlet)
68+
D2[-1, -1] = 1.0 / dz**2 # Danckwerts-type outlet
69+
D2 = D2.tocsr()
70+
71+
_set(par, "D1", D1)
72+
_set(par, "D2", D2)
73+
return par
74+
75+
if __name__ == "__main__":
76+
# Minimal smoke test
77+
from types import SimpleNamespace
78+
par = SimpleNamespace(N=5, dz=0.01)
79+
par = build_matrices(par)
80+
print("D1:\n", par.D1.toarray())
81+
print("D2:\n", par.D2.toarray())

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
numpy
2+
scipy

0 commit comments

Comments
 (0)