Source code for mdadash.backend.analyses.com_distance

"""
Distance between two center-of-masses
"""

import logging
from collections import deque

import matplotlib.pyplot as plt
from IPython.display import display
from MDAnalysis.exceptions import NoDataError
from MDAnalysis.lib.distances import calc_bonds

from mdadash.backend.widgets.base import WidgetBase

logger = logging.getLogger(__name__)


[docs] class COMDistance(WidgetBase): """COM Distance Distance between two center-of-masses (COMs) """ name = "COMDistance" description = "Distance between two COMs" _inputs = [ { "attribute": "selection1", "name": "Selection 1", "description": "First MDAnalysis selection phrase", "type": "str", "validations": ["required"], }, { "attribute": "selection2", "name": "Selection 2", "description": "Second MDAnalysis selection phrase", "type": "str", "validations": ["required"], }, { "attribute": "periodic", "name": "Periodic", "description": "Select with periodic boundary conditions", "type": "bool", }, { "attribute": "updating", "name": "Updating", "description": "Update selection during each timestep", "type": "bool", }, { "attribute": "custom_title", "name": "Custom title", "description": "Custom title for the plot", "type": "str", }, { "attribute": "maxlen", "name": "Max values", "description": "Max values to show in plot", "type": "int", }, { "attribute": "max_distance", "name": "Max distance", "description": "Max distance for alert check", "type": "float", }, { "attribute": "max_distance_alert", "name": "Alert if distance > 'Max distance'", "type": "bool", }, { "attribute": "max_distance_pause", "name": "Pause simulation if distance > 'Max distance'", "type": "bool", }, { "attribute": "x_type", "name": "X-axis", "type": "toggle", "options": [ {"name": "Time", "value": "time"}, {"name": "Step", "value": "step"}, ], }, ] def __init__(self): super().__init__() self.selection1 = "protein" self.selection2 = "resid 1" self.periodic = True self.updating = False self.ag1 = None self.ag2 = None self.max_distance = 50.0 self.max_distance_alert = False self.max_distance_pause = False self.title = "Distance between COMs" self.custom_title = None self.default_maxlen = 100 self.maxlen = self.default_maxlen self.x_type = "time" self.x_values = None self._setup_plot() self._reset_plot_values() def _setup_plot(self): """Setup matplotlib plot""" self.fig, self.ax = plt.subplots() (self.plot,) = self.ax.plot([], []) self.ax.set_ylabel("Distance (Å)") self.ax.grid(True) self._set_title() def _reset_plot_values(self): """Reset plot values""" self.steps = deque(maxlen=self.maxlen) self.times = deque(maxlen=self.maxlen) self.y_values = deque(maxlen=self.maxlen) self._set_x_values() def _set_title(self): """Set plot title""" self.ax.set_title(self.custom_title if self.custom_title else self.title) def _set_x_values(self): """Set the values for the x-axis""" if self.x_type == "step": x_label = "Step" self.x_values = self.steps else: x_label = "Time (ps)" self.x_values = self.times self.ax.set_xlabel(x_label) def _update_selections(self): """Update atom groups when selection phrases change""" self.ag1 = self.u.select_atoms( self.selection1, periodic=self.periodic, updating=self.updating ) self.ag2 = self.u.select_atoms( self.selection2, periodic=self.periodic, updating=self.updating ) self.title = f"{self.selection1} <---> {self.selection2}" self._set_title()
[docs] def on_post_create(self): """on_post_create handler""" self._set_title() self._reset_plot_values()
[docs] def on_post_connect(self): """on_post_connect handler""" self._update_selections()
[docs] def on_input_change(self, attribute, _old_value, new_value): """on_input_change handler""" reset_plot = False if attribute == "maxlen": if new_value < 0: self.maxlen = self.default_maxlen reset_plot = True elif attribute == "x_type": self._set_x_values() elif attribute == "custom_title": self._set_title() elif attribute == "selection1": self._update_selections() reset_plot = True elif attribute == "selection2": self._update_selections() reset_plot = True elif attribute in ("periodic", "updating"): self._update_selections() if reset_plot: self._reset_plot_values()
[docs] def run_every_frame(self): """every-frame run handler""" try: com1 = self.ag1.center_of_mass(unwrap=True) com2 = self.ag2.center_of_mass(unwrap=True) except NoDataError: # pragma: no cover # unwrap can fail if there is no bonds info com1 = self.ag1.center_of_mass() com2 = self.ag2.center_of_mass() dist = calc_bonds(com1, com2, box=self.u.dimensions) self.y_values.append(dist) self.steps.append(self.u.trajectory.ts.data["step"]) self.times.append(self.u.trajectory.ts.data["time"]) # update plot self.plot.set_data(self.x_values, self.y_values) self.ax.relim() self.ax.autoscale_view() self.fig.canvas.draw() display(self.fig) if self.max_distance_pause and dist > self.max_distance: self.pause_simulation()