Phase A: classical forcefields and ReaxFF (2026-10-12) ====================================================== What was built -------------- * **forcefield_step** (``model_chemistry.py``; classmethods on ``ForcefieldStep``): - ``get_model_chemistry_options()`` offers one model chemistry per ``#define`` in every ``.frc`` file found, with the file's own names: ``FF@`` (``FF@oplsaa+``, ``FF@pcff``; ``dreiding/X6`` is ``FF@dreiding-X6``, the grammar's reserved characters replaced by ``-``) and ``ReaxFF@`` for ``reaxff/``. (Paul, 2026-10-12: the names are the files', not a table of display names such as ``FF@OPLS-AA``.) Files another ``.frc`` file includes (``#include``) are parts of that forcefield and are not offered: e.g. ``local:OPLS-AA/ligpargen.frc`` extends ``oplsaa+``. There is no owner, since LAMMPS evaluates them itself. They are not MDI engines (``mdi_only`` gives nothing). - The files are searched in the same order as data files: the user's (``~/.seamm.d/data/Forcefields``), then the installation's (``/data/Forcefields``, ``~/SEAMM/data/Forcefields``), then the package's. The first forcefield of a name wins, so a user's file shadows a shipped one. - ``forcefield(mc, node)`` builds the ``seamm_ff_util`` forcefield as the Forcefield step does, with the node's references for the citations. - ``model_chemistry_data_files(mc, node)`` returns the ``local:``/``personal:`` file and its includes, for a submitted job. * **model_chemistry_step**: - ``discover_model_chemistries(..., data_path=)`` passes the flowchart's data path (``Node.data_path``: in a JobServer the job's own ``data/`` first) to providers whose ``get_model_chemistry_options`` accepts it. That is how a job finds the user's forcefield sent with it, which is not in the run host's own directories. Providers without the argument are called as before. - ``ModelChemistry.list_data_files()`` asks the offering step for its files. - The dialog has no Program for a level offered without an owner, and composes ``FF@oplsaa``. ``_programs()`` drops ``None``, because ``sorted()`` of a mix raises. * **lammps_step**: ``forcefield_model_chemistry()`` and ``forcefield()``. The forcefield is built through the provider (``plugin_manager.get(mc["step"])``) and cached per level and file. ``ff_form()`` returns the forcefield's form for FF/ReaxFF and ``MDI/QM`` for other model chemistries only. ``initialization`` and ``heat_flux`` call ``parent.forcefield()``. * **packmol_step** types with the forcefield model chemistry when there is one, else with ``_forcefield``. **diffusivity_step** names the model chemistry's level. Verification ------------ Packmol (12 ethanol, periodic) and then LAMMPS Initialization + Energy were run both ways, through a Forcefield step and through a Model Chemistry step, for ``FF@oplsaa+`` (the Forcefield step's default for ``oplsaa.frc``) and ``ReaxFF@CHO_cho_2008``: * ``input.dat`` (and ReaxFF's ``forcefield.dat``) were identical; * ``structure.dat`` was identical apart from the coordinates (Packmol packs randomly): the same atom types, charges, bonded terms and coefficients; * the citations were the same, including the forcefield's (Jorgensen). Notes for later phases ---------------------- * The Forcefield step's default for ``oplsaa.frc`` is its first forcefield, ``oplsaa+``: the converter (phase C) must map it to ``FF@oplsaa+``. * **OpenKIM (IP@) is not done.** The plan offers *installed* potentials, but none are installed in practice: this Mac has none, and ChemAI's LAMMPS envs have only kim-api's examples (``LennardJones_Ar``, ``ex_model_*``). Listing them means running ``kim-api-collections-management list`` in LAMMPS's environment (about 0.3 s). * Releases: forcefield_step and model_chemistry_step first; then lammps_step and packmol_step require them (the released dialog would compose ``None:FF@...``, and the old forcefield_step has no ``forcefield()``).