def find_configurations(self): """ Finds the partitions as required by the Hot-Spot method and saves each partition as a system object. Saves all systems in the dictionary self.systems Parameters ---------- qm_center : list atoms that define the qm center, default is None """ qm = System(qm_indices=self.qm_atoms, qm_residues=self.qm_residues, run_ID=self.run_ID, partition_ID='qm') qm.buffer_groups = self.buffer_groups # the following only runs if there are groups in the buffer zone if self.buffer_groups: qm.buffer_groups = deepcopy(self.buffer_groups) for i, buf in self.buffer_groups.items(): qm.qm_residues.append(i) for idx in buf.atoms: qm.qm_atoms.append(idx) self.systems[self.run_ID] = {} self.systems[self.run_ID][qm.partition_ID] = qm
def run_qmmm(self, main_info, wrapper_type): """ Drives QM/MM computation. Updates the positions and topology given in main_info, and determines the QM/MM energy and gradients Parameters ---------- main_info : dict Contains the energy, forces, topology, and position information for the whole system wrapper_type : str Defines the program used to obtain main_info """ t0 = time.time() self.update_traj(main_info['positions'], main_info['topology'], wrapper_type) system = System(qm_indices=self.qm_atoms, qm_residues=None, run_ID=self.run_ID) if self.embedding_method == 'Mechanical': t_all = self.mechanical(system, main_info) elif self.embedding_method == 'Electrostatic': t_all = self.electrostatic(system, main_info) else: print( 'only mechanical and electrostatic embedding schemes implemented at this time' ) self.systems[self.run_ID] = {} self.systems[self.run_ID][system.partition_ID] = system self.systems[self.run_ID]['qmmm_forces'] = system.qmmm_forces self.systems[self.run_ID]['qmmm_energy'] = system.qmmm_energy #self.systems[self.run_ID]['kinetic_energy'] = main_info['kinetic'] self.systems[ self.run_ID]['kinetic_energy'] = system.entire_sys['kinetic'] #if self.run_ID % 10 == 0: #print('!', self.run_ID, self.systems[self.run_ID]['qmmm_energy'] + self.systems[self.run_ID]['kinetic_energy']) print('! run {} total energy: {}'.format( self.run_ID + 1, (self.systems[self.run_ID]['qmmm_energy'] + self.systems[self.run_ID]['kinetic_energy']))) print('! run {} total potential energy: {}'.format( self.run_ID + 1, self.systems[self.run_ID]['qmmm_energy'])) # add kinetic in total qmmm_energy # updates current step count self.run_ID += 1 # delete the information of 2 runs before, only save current run and previous run information at a time if self.run_ID > 1: del self.systems[self.run_ID - 2] t_end = time.time() print('qmmm overhead took {}s'.format(t_end - t0 - t_all)) return t_all
def prepare_link_atom(self): """ Identifies where to put link atom. Saves the qm and mm atom associated with the bond being cut across the QM/MM boundary and computes the g scaling factor for the link atom. """ self.link_atoms = {} self.link_atoms['all_mm'] = [] self.link_atoms['all_outer_bonds'] = [] for i, bond in enumerate(self.qmmm_boundary_bonds): qm = bond[0] mm = bond[1] self.link_atoms[i] = {} self.link_atoms[i]['qm_atom'] = qm self.link_atoms[i]['mm_atom'] = mm self.link_atoms['all_mm'].append(mm.index) self.link_atoms[i]['link_atom'] = self.link_atom_element g = System.compute_scale_factor_g(qm.element.symbol, mm.element.symbol, self.link_atom_element) self.link_atoms[i]['scale_factor'] = g # this is in nm self.link_atoms[i]['link_positions'] = (1 - g) * self.positions[ qm.index] + g * self.positions[mm.index] if self.boundary_treatment == 'RC' or self.boundary_treatment == 'RCD': bonds = [] # find index of atoms bonded to mm atom for bond in self.topology.bonds: if bond[0] == mm or bond[1] == mm: if bond[0] != qm and bond[1] != qm: if bond[0] != mm: bonds.append(bond[0].index) elif bond[1] != mm: bonds.append(bond[1].index) self.link_atoms[i]['bonds_to_mm'] = bonds self.link_atoms['all_outer_bonds'].append(bonds)
def find_configurations(self): """ Finds the QM/MM configurations as required by the PAP method and saves each partition as a system object. Saves all systems in the dictionary self.systems Parameters ---------- qm_center : list atoms that define the qm center, default is None """ qm = System(qm_indices=self.qm_atoms, qm_residues=self.qm_residues, run_ID=self.run_ID, partition_ID='qm') qm.buffer_groups = self.buffer_groups self.systems[self.run_ID] = {} self.systems[self.run_ID][qm.partition_ID] = qm # the following only runs if there are groups in the buffer zone if self.buffer_groups: self.partitions, sigmas = self.get_combos(list(self.buffer_groups)) print('partitions', self.partitions) for i, part in enumerate(self.partitions): sys = System(qm_indices=self.qm_atoms, qm_residues=self.qm_residues, run_ID=self.run_ID, partition_ID=i) sys.sigma = sigmas[i] for group in part: sys.qm_residues.append(group) for idx in self.buffer_groups[group].atoms: sys.qm_atoms.append(idx) # each partition has a copy of its buffer groups - # don't know if this is actually needed sys.buffer_groups = {k: self.buffer_groups[k] for k in part} self.systems[self.run_ID][sys.partition_ID] = sys
def find_configurations(self): """ Finds the partitions as required by the PAP method and saves each partition as a system object. Saves all systems in the dictionary self.systems Parameters ---------- qm_center : list atoms that define the qm center, default is None """ qm = System(qm_indices=self.qm_atoms, qm_residues=self.qm_residues, run_ID=self.run_ID, partition_ID='qm') # qm partition contains the original partition information qm.buffer_groups = self.buffer_groups self.systems[self.run_ID] = {} self.systems[self.run_ID][qm.partition_ID] = qm # the following only runs if there are groups in the buffer zone if self.buffer_groups: self.partitions = self.get_combos(list(self.buffer_groups)) for i, part in enumerate(self.partitions): sys = System(qm_indices=self.qm_atoms, qm_residues=self.qm_residues, run_ID=self.run_ID, partition_ID=i) for group in part: sys.qm_residues.append(group) for idx in self.buffer_groups[group].atoms: sys.qm_atoms.append(idx) # each partition has a copy of its buffer groups - sys.buffer_groups = {k: self.buffer_groups[k] for k in part} self.systems[self.run_ID][sys.partition_ID] = sys
def run_aqmmm(self): """ Interpolates the energy and gradients from each partition according to the PAP method """ qm = self.systems[self.run_ID]['qm'] if not self.buffer_groups: self.systems[self.run_ID]['qmmm_energy'] = qm.qmmm_energy self.systems[self.run_ID]['qmmm_forces'] = qm.qmmm_forces else: # getting first term of ap energy and forces (w/o gradient of switching function) qm.aqmmm_energy = deepcopy(qm.qmmm_energy) qm.aqmmm_forces = deepcopy(qm.qmmm_forces) dis = sorted(self.buffer_distance, key=self.buffer_distance.get) sigma = self.buffer_groups[dis[0]].s_i qm.aqmmm_energy *= sigma qm.aqmmm_forces.update( (x, y * sigma) for x, y in qm.aqmmm_forces.items()) energy = deepcopy(qm.aqmmm_energy) qmmm_forces = deepcopy(qm.aqmmm_forces) # getting rest of the terms of ap energy and forces (w/o gradient of switching function) for i, part in enumerate(self.partitions): sys = self.systems[self.run_ID][i] sys.aqmmm_energy = sys.qmmm_energy * sys.sigma sys.aqmmm_forces = deepcopy(sys.qmmm_forces) sys.aqmmm_forces.update( (x, y * sys.sigma) for x, y in sys.aqmmm_forces.items()) energy += sys.aqmmm_energy # combining all forces for i, part in enumerate(self.partitions): forces = self.systems[self.run_ID][i].aqmmm_forces for j, force in forces.items(): if j in qmmm_forces: qmmm_forces[j] += force else: qmmm_forces[j] = force # need to deal with bookkeeping term energy_bk = 0.0 systems_f = [] systems_match = [] if self.run_ID != 0: for sys_f in self.systems[self.run_ID]: if isinstance(sys_f, System()): systems_f.append(Counter(sys_f.qm_residues)) for sys_i in self.systems[self.run_ID - 1]: if isinstance(sys_i, System()): if Counter(sys_f.qm_residues) == Counter( sys_i.qm_residues): systems_match.append( Counter(sys_f.qm_residues)) energy_bk += sys_f.aqmmm_energy * ( sys_f.sigma - sys_i.sigma) for sys in systems_f: if sys not in systems_match: energy_bk += sys_f.aqmmm_energy * sys_f.sigma energy -= energy_bk self.systems[self.run_ID]['qmmm_energy'] = energy self.systems[self.run_ID]['qmmm_forces'] = qmmm_forces