Implementing Internal Builders#
Per-step work inside BATTER (prepare_equil, prepare_fe, etc.) is handled by
classes under batter/_internal. This section outlines the directory conventions,
registries, and shared ops so you can extend builders without reverse-engineering the
codebase.
Directory Conventions#
Every builder receives a BuildContext
that points at a per-ligand working directory:
simulations/<LIGAND>/
├── q_build_files/ # Shared build artifacts (build.pdb, anchors, dum.*)
├── q_amber_files/ # Amber templates for equilibration
├── q_run_files/ # Job scripts, logs
├── e_build_files/ # Per-component equivalents (e, v, o, z, y, ...)
└── ...
The ctx.build_dir / ctx.amber_dir helpers abstract these paths so ops like
create_box can stage files without duplicating directory logic. Always write
intermediate artifacts into the component’s build directory and keep final AMBER
inputs under ctx.window_dir.
Registry Hooks#
Registries live in batter._internal.builders.fe_registry and map component codes
to functions:
@register_build_complex('e')– adds a factory for the component’s_build_complexhook.@register_create_box('z')– selects the correctcreate_boxhelper.@register_restraints(...),@register_sim_files(...), etc. – route the remaining hooks.
When introducing a new component or overriding an existing hook, register it here so the orchestrator picks it up automatically.
Reusable Ops#
Common tasks are centralised under batter/_internal/ops:
box.py–create_boxhelpers (AMBER tleap scripts, solvation, ion placement).restraints.py– Writers for disang/cv inputs.sim_files.py– Template renderers for MD input decks (equilibration emits the standardeqnpt.in/eqnpt0.inplus the appear/disappear variants used by the run scripts).simprep.py– Build directory initialisation and window copying.
Prefer importing and extending these modules instead of duplicating tleap/parmed code.
Many helpers already expect BuildContext objects and honour the directory layout
described above.
Builder Lifecycle#
Each batter._internal.builders.base.BaseBuilder subclass implements the hook
methods executed by BaseBuilder.build():
_build_complex– Align systems, detect anchors, and populatebuild_dir._create_box– Write solvated/vacuum topologies using the registeredcreate_boxop._restraints– Generate component-specific restraints._pre_sim_files(optional) – Any additional preprocessing before rendering inputs._sim_files– Produce AMBER mdin/mini/eq decks._run_files– Emit SLURM/local job scripts.
Builder Testing#
Builders operate strictly inside the per-ligand working directory, which keeps them easy to reason about. To test a new builder in isolation:
Construct a
BuildContextwith temporary directories forworking_dir,system_root, andparam_dir_dict.Call the individual hook (or
build()) and assert that the expected files appear underctx.working_dir.Re-run
create_boxandsim_filesto ensure idempotency—many ops reuse cached artifacts when files already exist.