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

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": "<stevedore plugin name>",         # owning step (resolution handle)
    "options": { ... full get_model_chemistry_options() dict ... },
}

LAMMPS (Phase C) then:

  1. reads _model_chemistry;

  2. checks options["mdi_capable"] and options["periodic_mdi"] and errors helpfully if the selection cannot be driven via MDI;

  3. resolves the owner: step = self.flowchart.plugin_manager.manager[mc["step"]].obj;

  4. 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#

  1. 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.

  2. 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.

  3. 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.

  4. 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)#

  1. Restructure to a simple step (D1): trim model_chemistry.py, tk_model_chemistry.py, drop substep.py.

  2. Real metadata.py and a model_chemistry parameter (dynamic enumeration populated from discovery).

  3. Discovery helper + run() that builds and stores _model_chemistry.

  4. GUI combobox populated from discovery.

  5. 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).