surface_builder

Configuration & data classes

class biochar.surface_builder.SurfaceConfig(target_num_carbons=50, H_C_ratio=0.3, O_C_ratio=0.05, functional_groups=None, aromaticity_percent=95.0, pore_type='slit', num_sheets=2, pore_diameter=10.0, max_attempts=500, min_separation=3.0, sheet_overrides=None, box_padding_xy=1.0, box_padding_z=1.0, system_name='SLIT', sheet_base_name='SHT', seed=None, defect_fraction=0.0, strict=True)[source]

Bases: object

Configuration for SurfaceBuilder.

Parameters:
target_num_carbons

Carbon atoms per sheet (passed to BiocharGenerator).

H_C_ratio

Target H/C ratio for each sheet.

O_C_ratio

Target O/C ratio (used when functional_groups is None).

functional_groups

Functional groups applied to every sheet, e.g. {"phenolic": 2, "ether": 1}. Overridden per-sheet via sheet_overrides.

aromaticity_percent

Target aromatic carbon fraction (percentage).

pore_type

Pore geometry. "slit" stacks parallel sheets along z; "amorphous" performs random rigid-body placement with steric rejection (each sheet independently rotated/translated).

num_sheets

Number of parallel sheets. Must be ≥ 2.

pore_diameter

Gap between the inner van-der-Waals surfaces of adjacent sheets, in Ångströms. The centre-to-centre sheet separation is pore_diameter + 3.4 Å (slit pore only).

max_attempts

Maximum random placement attempts per sheet for pore_type="amorphous" before raising RuntimeError.

min_separation

Minimum allowed inter-sheet atom-atom distance (Ångströms) used as the steric-rejection criterion for pore_type="amorphous".

sheet_overrides

Per-sheet configuration overrides. If provided, must be a list of dicts (one per sheet) with keys matching GeneratorConfig fields. If None, all sheets are chemically identical (one .itp with count = num_sheets).

box_padding_xy

Padding added to each side of the bounding box in x and y, in nm.

box_padding_z

Padding added to each side of the bounding box in z, in nm.

system_name

Name written to the [ system ] section of the .top file.

sheet_base_name

Residue name prefix for sheet molecules. Must be ≤ 3 characters so that the full name (prefix + digit) fits within GROMACS’ 5-character residue-name limit.

seed

Random seed passed to each BiocharGenerator.

defect_fraction

Pentagon insertion probability for each sheet (see defect_fraction).

class biochar.surface_builder.SheetResult(mol, coords, composition, atom_types, charges, molecule_name)[source]

Bases: object

Result of generating and positioning a single sheet.

Parameters:
mol

RDKit molecule with 3-D conformer and OPLS-AA types.

coords

Atomic coordinates in Ångströms, shape (N, 3). Updated in-place by SurfaceBuilder.build() to world coordinates (translated along z after flattening).

composition

Atom counts and ratios (CompositionInfo).

atom_types

Mapping of atom index → OPLS-AA type string.

charges

Mapping of atom index → partial charge (electrons).

molecule_name

Residue name used in .gro and .itp files. Identical for all sheets when sheet_overrides is None; indexed (e.g. SHT1, SHT2) for distinct sheets.

Builder

class biochar.surface_builder.SurfaceBuilder(config)[source]

Bases: object

Build slit-pore surface systems from parallel biochar sheets.

The builder orchestrates a three-phase pipeline:

  1. Sheet generation — creates each sheet via BiocharGenerator, assigns OPLS-AA types and charges, then flattens the sheet to the xy plane using SVD.

  2. Positioning — translates sheet i to z = i × (pore_diameter + 3.4 Å) so consecutive sheets are separated by the requested pore gap.

  3. Box computation — wraps all sheets in an orthogonal periodic box padded by SurfaceConfig.box_padding_xy (nm) in x/y and SurfaceConfig.box_padding_z (nm) in z, then centres the system inside the box.

When SurfaceConfig.sheet_overrides is None all sheets are chemically identical: only the first sheet is generated; the rest are deep-copied. A single .itp is produced and the .top lists it with count = num_sheets.

Use generate_surface() for a one-call convenience wrapper.

Examples:

config = SurfaceConfig(
    target_num_carbons=60,
    functional_groups={"phenolic": 2},
    pore_diameter=12.0,
    num_sheets=2,
    seed=42,
)
builder = SurfaceBuilder(config)
sheets, box_nm = builder.build()
gro, top, itps = builder.export_gromacs("output", "slit_pore")
Parameters:

config (SurfaceConfig)

build()[source]

Generate all sheets and compute their positioned coordinates.

Return type:

Tuple[List[SheetResult], ndarray]

Returns:

(list_of_SheetResult, box_vectors_nm)

Each SheetResult.coords is updated in place to world coordinates (translated along z so sheets are stacked with the requested pore gap).

export_gromacs(output_directory='.', basename='surface')[source]

Export the positioned surface system to GROMACS files.

Return type:

Tuple[Path, Path, List[Path]]

Returns:

(gro_path, top_path, list_of_itp_paths)

Parameters:
  • output_directory (str)

  • basename (str)