Porous Surface Generation
The surface builder creates slit-pore systems consisting of multiple parallel graphene-like sheets separated by a controllable pore gap and exports them as a single GROMACS simulation box.
SurfaceConfig
│
▼
Generate N sheets (each via single-molecule pipeline)
│ Identical sheets: generate once, deep-copy remainder
│ Distinct sheets: generate each independently
▼
Flatten each sheet to xy plane (SVD best-fit rotation)
│
▼
Stack along z: sheet i at z = i × (pore_diameter + 3.4 Å)
│
▼
Compute periodic box (bounding box + padding)
│
▼
Centre system in box
│
▼
GROMACS export
│ .gro — all N sheets, contiguous atom numbering
│ .itp — one file (identical) or one per unique sheet
└─ .top — forcefield + itp includes + [ molecules ] count
Symmetric slit pore
Two chemically identical sheets — only one sheet is generated and
deep-copied. A single .itp is produced with count = 2 in the
.top.
from biochar import generate_surface
sheets, gro, top, itps = generate_surface(
target_num_carbons=50,
H_C_ratio=0.3,
O_C_ratio=0.05,
functional_groups={"phenolic": 2},
pore_diameter=10.0, # Å — gap between vdW surfaces
num_sheets=2,
output_directory="output",
basename="slit_pore",
seed=42,
)
The pore diameter is the free gap between the inner van-der-Waals
surfaces of the two sheets. The centre-to-centre separation is
pore_diameter + 3.4 Å (graphite interlayer spacing).
Asymmetric pore
Use sheet_overrides to give each wall different chemistry. One
.itp is written per unique sheet.
sheets, gro, top, itps = generate_surface(
pore_diameter=8.0,
num_sheets=2,
sheet_overrides=[
# Wall 1: phenolic-rich, smaller sheet
{"target_num_carbons": 40, "functional_groups": {"phenolic": 3}},
# Wall 2: carboxyl-rich, larger sheet
{"target_num_carbons": 60, "functional_groups": {"carboxyl": 2}},
],
output_directory="output",
basename="asymmetric_pore",
)
Multi-sheet stacking
More than two sheets create multiple consecutive pores with equal gaps:
sheets, gro, top, itps = generate_surface(
target_num_carbons=50,
pore_diameter=12.0,
num_sheets=4, # three pore gaps
output_directory="output",
basename="multilayer",
seed=7,
)
Using the class API
For programmatic control, use SurfaceBuilder
directly:
from biochar import SurfaceBuilder, SurfaceConfig
config = SurfaceConfig(
target_num_carbons=60,
functional_groups={"phenolic": 2, "ether": 1},
pore_diameter=10.0,
num_sheets=2,
box_padding_xy=1.5, # nm
box_padding_z=1.0, # nm
seed=42,
)
builder = SurfaceBuilder(config)
sheets, box_nm = builder.build()
print(f"Box: {box_nm} nm")
for i, sheet in enumerate(sheets):
z_vals = sheet.coords[:, 2]
print(f"Sheet {i+1}: z = {z_vals.mean():.2f} Å")
gro, top, itps = builder.export_gromacs("output", "slit_pore")
Output files
File |
Contents |
|---|---|
|
All sheets; residue numbers 1, 2, … N |
|
Force field include, itp includes, [molecules] |
|
Identical-sheet topology (count = N in .top) |
|
Sheet 1 topology (distinct-sheet case) |
|
Sheet 2 topology (distinct-sheet case) |