============================== 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 :class:`~batter._internal.builders.interfaces.BuildContext` that points at a per-ligand working directory:: simulations// ├── 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 :mod:`batter._internal.builders.fe_registry` and map component codes to functions: * ``@register_build_complex('e')`` – adds a factory for the component’s ``_build_complex`` hook. * ``@register_create_box('z')`` – selects the correct ``create_box`` helper. * ``@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_box`` helpers (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 standard ``eqnpt.in``/``eqnpt0.in`` plus 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 :class:`batter._internal.builders.base.BaseBuilder` subclass implements the hook methods executed by :meth:`BaseBuilder.build`: 1. ``_build_complex`` – Align systems, detect anchors, and populate ``build_dir``. 2. ``_create_box`` – Write solvated/vacuum topologies using the registered ``create_box`` op. 3. ``_restraints`` – Generate component-specific restraints. 4. ``_pre_sim_files`` (optional) – Any additional preprocessing before rendering inputs. 5. ``_sim_files`` – Produce AMBER mdin/mini/eq decks. 6. ``_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: 1. Construct a :class:`BuildContext` with temporary directories for ``working_dir``, ``system_root``, and ``param_dir_dict``. 2. Call the individual hook (or ``build()``) and assert that the expected files appear under ``ctx.working_dir``. 3. Re-run ``create_box`` and ``sim_files`` to ensure idempotency—many ops reuse cached artifacts when files already exist.