.. _notes-mdi-lammps-phase-b: ========================================================== Phase B -- Model Chemistry Step (selection + storage) ========================================================== :Author: Paul Saxe (with Claude) :Date: 2026-06-23 :Status: In progress -- design agreed except D1-D4 below :Campaign: LAMMPS + MOPAC/xTB QM-MD via MDI .. contents:: Contents :depth: 2 :local: Scope ===== Phase B builds the **minimum Model Chemistry step** needed for the thin line "select MOPAC PM6-ORG, then drive LAMMPS MD with it via MDI". The step lets the user choose a model chemistry and stores it as the workspace variable ``_model_chemistry`` that LAMMPS (Phase C) reads -- mirroring how the Forcefield step provides ``_forcefield``. In scope: * Strip the package from a *subflowchart* plug-in (as generated by the cookiecutter) down to a *simple* step. * The model-chemistry grammar parser/composer (delivered: ``model_chemistry_step/grammar.py`` + ``tests/test_grammar.py``). * Discovery of available model chemistries across installed program steps. * A GUI to select one. * ``run()`` that parses the selection and sets ``_model_chemistry``. * The ``_model_chemistry`` schema (the producer -> consumer contract). Deferred: * Phase C: the LAMMPS side -- read ``_model_chemistry``, generate the launch script (Phase A's ``get_mdi_engine_command`` + a free TCP port), run. * Cascading Program -> Type -> Method GUI (flat combobox for now). * Full generality: basis/cutoff codes, all program types, and replacing the Forcefield step's global-variable approach wholesale. * xTB (revisit once the MOPAC thin line works end to end). Confirmed SEAMM mechanics ========================= **Discovery.** Steps are Stevedore plug-ins in the ``"org.molssi.seamm"`` namespace (``seamm.PluginManager``). Each entry-point object is the step's helper class (e.g. ``MOPACStep``). So enumerating model chemistries means iterating that namespace and calling ``get_model_chemistry_options()`` on every helper that defines it. The owning plug-in's Stevedore *name* is captured at the same time, to serve as the resolution handle later. **Storage.** ``seamm.Node`` provides ``set_variable(name, value)`` / ``get_variable(name)`` / ``variable_exists(name)``, backed by ``seamm.flowchart_variables``. The step does ``self.set_variable("_model_chemistry", {...})``; LAMMPS does ``self.get_variable("_model_chemistry")``. Same mechanism as ``_forcefield``. The ``_model_chemistry`` contract ================================= The step writes this workspace variable:: { "model_chemistry": "MOPAC:SQM@PM6-ORG", # canonical string "program": "MOPAC", "type": "SQM", "method": "PM6-ORG", "basis": None, "cutoff": None, # parsed components "step": "", # owning step (resolution handle) "options": { ... full get_model_chemistry_options() dict ... }, } LAMMPS (Phase C) then: #. reads ``_model_chemistry``; #. checks ``options["mdi_capable"]`` and ``options["periodic_mdi"]`` and errors helpfully if the selection cannot be driven via MDI; #. resolves the owner: ``step = self.flowchart.plugin_manager.manager[mc["step"]].obj``; #. calls ``step.get_mdi_engine_command(self.flowchart.executor, self.global_options, method=mc["method"], port=port, charge=..., multiplicity=...)`` (charge/multiplicity from the configuration object, per SEAMM convention -- never from the model-chemistry step). Carrying the whole ``options`` block means LAMMPS needs nothing from the program step except the launch command. The ``step`` name (captured at discovery) avoids having to map the ``Program:`` token back to an entry point. Open decisions ============== #. **D1 -- Simplify subflowchart -> simple step.** *Proposed: yes.* Remove the ``Substep`` base class, the iterate-over-substeps ``run()``, and the subflowchart GUI scaffolding. Large diff; confirm before restructuring. #. **D2 -- The ``_model_chemistry`` schema above.** *Proposed as written.* Confirm the keys, especially carrying the full ``options`` block and using ``step`` (Stevedore name) as the resolution handle. #. **D3 -- Generality vs. filtering.** *Proposed: keep the step general* (lists all discovered model chemistries) and let LAMMPS validate MDI-capability; add an optional filter toggle later. #. **D4 -- GUI.** *Proposed: flat combobox* of ``Program:Type@Method`` strings; cascading dropdowns deferred. Grammar (delivered) =================== ``Program:Type@Method[/Basis[@Cutoff]]``. ``parse_model_chemistry(text)`` -> components dict; ``compose_model_chemistry(components)`` -> string (inverse). Delimiters ``:``, ``@``, ``/`` are reserved; a ``Cutoff`` requires a ``Basis``. Lives in ``model_chemistry_step/grammar.py`` so both the step and consumers (LAMMPS, program steps) share one definition. Add to ``__init__.py``:: from .grammar import parse_model_chemistry, compose_model_chemistry # noqa: F401 Next steps (pending D1-D4) ========================== #. Restructure to a simple step (D1): trim ``model_chemistry.py``, ``tk_model_chemistry.py``, drop ``substep.py``. #. Real ``metadata.py`` and a ``model_chemistry`` parameter (dynamic enumeration populated from discovery). #. Discovery helper + ``run()`` that builds and stores ``_model_chemistry``. #. GUI combobox populated from discovery. #. Tests for discovery + storage (mock a program-step helper exposing ``get_model_chemistry_options``). References ========== * SEAMM plug-in discovery: ``seamm/plugin_manager.py`` (namespace ``org.molssi.seamm``). * Workspace variables: ``seamm/node.py`` (``set_variable`` / ``get_variable``) and ``seamm.flowchart_variables``. * Phase A engine contract: ``NOTES_A.rst`` (``get_executor_config``, ``get_mdi_engine_command``).