narupa.openmm.imd module

Link Narupa’s user forces to an OpenMM simulation.

The iMD is hooked to the OpenMM simulation in two places. A CustomExternalForce needs to be added in the system; it has the components of the iMD force for each atom as a per-atom parameter. A reporter periodically updates these parameters based on the imd service, and updates the simulation context.

The custom force can be setup using :fun:`create_imd_force` and :fun:`populate_imd_force`, or using :fun:`add_imd_force_to_system` that combines the two previous functions. When a simulation is created using :fun:`narupa.openmm.serializer.deserialize_simulation`, the imd force must be already present, or must be added by passing it with the imd_force parameter.

The reporter is NarupaImdReporter and both sends the frames and receives the interactions. It can be use instead of narupa.openmm.NarupaReporter that only sends the frames.

from narupa.app import NarupaImdApplication
from narupa.openmm.serializer import deserialize_simulation
from narupa.openmm.imd import NarupaImdReporter, create_imd_force

# Setup the Narupa application server
# The server is accessible using autoconnect.
with NarupaImdApplication.basic_server() as app:

    # Create the imd force and a simulation that includes it.
    imd_force = create_imd_force()
    with open('simulation.xml') as infile:
        simulation = deserialize_simulation(infile.read(), imd_force=imd_force)

    # Setup the reporter that does the translation between Narupa and OpenMM
    reporter = NarupaImdReporter(
        frame_interval=5,
        force_interval=10,
        imd_force=imd_force,
        imd_service=app.imd,
        frame_publisher=app.frame_publisher,
    )
    simulation.reporters.append(reporter)

    # Run the simulation
    while True:
        simulation.run(10)
class narupa.openmm.imd.NarupaImdReporter(frame_interval: int, force_interval: int, imd_force: pretends.CustomExternalForce, imd_state: narupa.imd.imd_state.ImdStateWrapper, frame_publisher: narupa.trajectory.frame_publisher.FramePublisher)

Bases: object

describeNextReport(simulation: pretends.Simulation) → Tuple[int, bool, bool, bool, bool, bool]

Called by OpenMM. Indicates when the next report is due and what type of data it requires.

static get_masses(system: pretends.System) → numpy.ndarray

Collect the mass, in Dalton, of each particle in an OpenMM system and return them as a numpy array.

report(simulation: pretends.Simulation, state: pretends.State) → None

Called by OpenMM.

narupa.openmm.imd.add_imd_force_to_system(system: pretends.System) → pretends.CustomExternalForce

Generate an OpenMM force that accepts arbitrary forces per particle.

The force is created, populated, added to the system and returned.

This is the force that is used to communicate the particle interactions from Narupa by NarupaImdReporter.

narupa.openmm.imd.create_imd_force() → pretends.CustomExternalForce

Returns an empty OpenMM force to communicate imd forces.

Each particle in the system has a fx, fy, and fz parameter to provide the arbitrary force components.

The force needs to be populated to include all the particle in the simulation mm.System.

narupa.openmm.imd.get_imd_forces_from_system(system: pretends.Simulation) → List[pretends.CustomExternalForce]

Find the forces that are compatible with an imd force in a given system.

A compatible force has the expected energy expression, and contains as many particles as the system.

All the compatible force objects are returned.

narupa.openmm.imd.populate_imd_force(force: pretends.CustomExternalForce, system: pretends.System) → None

Add all the particles to the iMD force.

The iMD force must be one generated by create_imd_force().