def find_closest_o_to_ostar(water_oxys, o_star, hydronium, box, h30_atom_type): """ Calculate the minimum distance between a water oxygen and the protonatable oxygen atom closest to the excess proton @param water_oxys: @param o_star: the protonatable oxygen atom closest to the excess proton @param hydronium: the list of atoms in the hydronium, if there is a hydronium @param box: the dimensions of the periodic box (assumed 90 degree angles) @param h30_atom_type: (int) lammps type for h30 oxygen atom @return: the water oxygen (or hydronium oxygen) closest to the o_star, and the distance between them """ # initialize smallest distance to the maximum distance in a periodic box min_dist = np.linalg.norm(np.divide(box, 2)) closest_o = None if not hydronium: # initialize minimum distance to maximum distance allowed in the PBC for wat_o in water_oxys: dist = pbc_dist(np.asarray(wat_o[XYZ_COORDS]), np.asarray(o_star[XYZ_COORDS]), box) if dist < min_dist: min_dist = dist closest_o = wat_o else: for atom in hydronium: if atom[ATOM_TYPE] == h30_atom_type: min_dist = pbc_dist(np.asarray(atom[XYZ_COORDS]), np.asarray(o_star[XYZ_COORDS]), box) closest_o = atom return closest_o, min_dist
def find_closest_o_to_ostar(water_oxys, o_star, hydronium, box, h3o_oxy_atom_type): """ Calculate the minimum distance between a water oxygen and the protonatable oxygen atom closest to the excess proton @param water_oxys: list of atom dicts @param o_star: the protonatable oxygen atom closest to the excess proton @param hydronium: the list of atoms in the hydronium, if there is a hydronium @param box: the dimensions of the periodic box (assumed 90 degree angles) @param h3o_oxy_atom_type: (int) lammps type for h30 oxygen atom @return: the water oxygen (or hydronium oxygen) closest to the o_star, and the distance between them """ # initialize smallest distance to the maximum distance in a periodic box min_dist = np.linalg.norm(np.divide(box, 2)) closest_o = None o_star_coord = np.asarray(o_star[XYZ_COORDS]) if not hydronium: # initialize minimum distance to maximum distance allowed in the PBC for wat_o in water_oxys: dist = pbc_dist(np.asarray(wat_o[XYZ_COORDS]), o_star_coord, box) if dist < min_dist: min_dist = dist closest_o = wat_o else: for atom in hydronium: if atom[ATOM_TYPE] == h3o_oxy_atom_type: min_dist = pbc_dist(np.asarray(atom[XYZ_COORDS]), o_star_coord, box) closest_o = atom return closest_o, min_dist
def find_closest_excess_proton(carboxyl_oxys, prot_h, hydronium, box, cfg): # initialize minimum distance to maximum distance possible in the periodic box (assume 90 degree corners) min_dist = np.full(len(carboxyl_oxys), np.linalg.norm(np.divide(box, 2))) closest_h = {} alt_dist = np.nan for index, oxy in enumerate(carboxyl_oxys): if prot_h is None: for atom in hydronium: if atom[ATOM_TYPE] == cfg[H3O_H_TYPE]: dist = pbc_dist(np.asarray(atom[XYZ_COORDS]), np.asarray(oxy[XYZ_COORDS]), box) if dist < min_dist[index]: min_dist[index] = dist closest_h[index] = atom else: min_dist[index] = pbc_dist(np.asarray(prot_h[XYZ_COORDS]), np.asarray(oxy[XYZ_COORDS]), box) closest_h[index] = prot_h index_min = np.argmin(min_dist) min_min = min_dist[index_min] o_star = carboxyl_oxys[index_min] excess_proton = closest_h[index_min] # The distance calculated again below because this will ensure that the 2nd return distance uses the same excess # proton for index, oxy in enumerate(carboxyl_oxys): if index != index_min: alt_dist = pbc_dist(np.asarray(excess_proton[XYZ_COORDS]), np.asarray(oxy[XYZ_COORDS]), box) alt_min = np.amin(alt_dist) return excess_proton, o_star, {OH_MIN: min_min, OH_MAX: alt_min, OH_DIFF: alt_min - min_min}
def atom_distances(rst, atom_pairs): """Finds the distance between the each of the atom pairs in the given LAMMPS dump file. :param rst: A file in the LAMMPS dump format. :param atom_pairs: Zero or more pairs of atom IDs to compare. :returns: Nested dicts keyed by time step, then pair, with the distance as the value. """ results = OrderedDict() flat_ids = set(itertools.chain.from_iterable(atom_pairs)) tstep_atoms, tstep_box = find_atom_data(rst, flat_ids) for tstep, atoms in tstep_atoms.items(): pair_dist = OrderedDict({FILENAME: os.path.basename(rst)}) for pair in atom_pairs: try: row1 = atoms[pair[0]] row2 = atoms[pair[1]] pair_dist[pair] = pbc_dist(row1[-3:], row2[-3:], tstep_box[tstep]) except KeyError as e: warning(MISSING_TSTEP_ATOM_MSG.format(rst, tstep, e)) return results[tstep] = pair_dist return results
def calc_pair_dists(atom_a_list, atom_b_list, box): dist = np.zeros(len(atom_a_list) * len(atom_b_list)) dist_index = 0 for atom_a in atom_a_list: for atom_b in atom_b_list: dist[dist_index] = pbc_dist(np.asarray(atom_a[XYZ_COORDS]), np.asarray(atom_b[XYZ_COORDS]), box) dist_index += 1 return dist
def find_closest_wat_o_to_hyd_o(water_oxys, hydronium, box, h3o_oxy_atom_type): """ Calculate the minimum distance between a water oxygen a hydronium oxygen @param water_oxys: list of atom dicts @param hydronium: the list of atoms in the hydronium, if there is a hydronium @param box: the dimensions of the periodic box (assumed 90 degree angles) @param h3o_oxy_atom_type: (int) lammps type for h30 oxygen atom @return: the water oxygen closest to the hydronium oxygen and the distance between them; the hydronium oxygen; the hydronium hydrogen closes to the water oxygen and the distance between it and the hydronium oxygen """ min_oo_dist = np.linalg.norm(np.divide(box, 2)) min_oh_dist = copy.copy(min_oo_dist) wat_o_hstar_dist = copy.copy(min_oo_dist) close_wat_o_to_hyd = None hyd_o = None hyd_h = [] close_hyd_h_to_wat = None for atom in hydronium: if atom[ATOM_TYPE] == h3o_oxy_atom_type: hyd_o = atom else: hyd_h.append(atom) hyd_o_coords = np.asarray(hyd_o[XYZ_COORDS]) for wat_o in water_oxys: dist = pbc_dist(np.asarray(wat_o[XYZ_COORDS]), hyd_o_coords, box) if dist < min_oo_dist: min_oo_dist = dist close_wat_o_to_hyd = wat_o close_wat_o_coords = np.asarray(close_wat_o_to_hyd[XYZ_COORDS]) for h_atom in hyd_h: # noinspection PyTypeChecker h_atom_coords = np.asarray(h_atom[XYZ_COORDS]) dist = pbc_dist(h_atom_coords, close_wat_o_coords, box) if dist < wat_o_hstar_dist: wat_o_hstar_dist = dist min_oh_dist = pbc_dist(h_atom_coords, hyd_o_coords, box) close_hyd_h_to_wat = h_atom hyd_wat_dict = { R_OO_HYD_WAT: min_oo_dist, R_OH_HYD: min_oh_dist, R_OH_WAT_HYD: wat_o_hstar_dist } return hyd_wat_dict, hyd_o, close_hyd_h_to_wat, close_wat_o_to_hyd
def calc_q_arq(r_ao, r_do, r_h, box, r0_sc, r0_da_q, lambda_q): """ Calculates the 3-body term, keeping the pbc in mind, per Maupin et al. 2006, http://pubs.acs.org/doi/pdf/10.1021/jp053596r, equations 7-8 @param r_ao: x,y,z position of the acceptor oxygen (closest water O) @param r_do: x,y,z position of the donor oxygen (always using the glu oxygen, for now) @param r_h: x,y,z position of the reactive H @param box: the dimensions of the periodic box (assumed 90 degree angles) @param r0_sc: parameter for calc @param r0_da_q: parameter for calc @param lambda_q: parameters for calc @return: the dot-product of the vector q (not the norm, as we need it squared for the next step) """ da_dist = pbc_dist(r_do, r_ao, box) r_sc = r0_sc - lambda_q * (da_dist - r0_da_q) r_dh = pbc_calc_vector(r_h, r_do, box) r_da = pbc_calc_vector(r_ao, r_do, box) q_vec = np.subtract(r_dh, r_sc * r_da / 2.0) return da_dist, np.dot(q_vec, q_vec)
def deprotonate(cfg, protonatable_res, excess_proton, dump_h3o_mol, water_mol_dict, box, tpl_data): """ Deprotonate a the residue and assign the proton to the closest water so that the output data matches with the template. """ # Convert excess proton to a hydronium proton excess_proton[1] = tpl_data[H3O_MOL][0][1] # molecule number excess_proton[2] = cfg[H3O_H_TYPE] # type excess_proton[3] = tpl_data[H3O_H_CHARGE] # charge dump_h3o_mol.append(excess_proton) min_dist_id = None min_dist = np.linalg.norm(box) for mol_id, molecule in water_mol_dict.items(): for atom in molecule: if atom[2] == cfg[WAT_O_TYPE]: dist = pbc_dist(np.asarray(excess_proton[4:7]), np.asarray(atom[4:7]), box) if dist < min_dist: min_dist_id = mol_id min_dist = dist logger.debug('Deprotonated residue: the molecule ID of the closest water ' '(to become a hydronium) is {}.'.format(min_dist_id)) # Now that have the closest water, add its atoms to the hydronium list for atom in water_mol_dict[min_dist_id]: dump_h3o_mol.append(atom) # Remove the closest water from the dictionary of water molecules, and convert it to a hydronium del water_mol_dict[min_dist_id] for atom in dump_h3o_mol: if atom[2] == cfg[WAT_O_TYPE]: atom[2] = cfg[H3O_O_TYPE] atom[3] = tpl_data[H3O_O_CHARGE] elif atom[2] == cfg[WAT_H_TYPE]: atom[2] = cfg[H3O_H_TYPE] atom[3] = tpl_data[H3O_H_CHARGE] # Make the atom type and charge of the protonatable residue the same as for the template file (switching # from protonated to deprotonated residue) if len(tpl_data[PROT_RES_MOL]) != len(protonatable_res): raise InvalidDataError( 'Encountered dump file in which the number of atoms in the ' 'protonatable residue does not equal the number of atoms in the template data file.' )
def deprotonate(cfg, protonatable_res, excess_proton, dump_h3o_mol, water_mol_dict, box, tpl_data): """ Deprotonate a the residue and assign the proton to the closest water so that the output data matches with the template. """ # Convert excess proton to a hydronium proton excess_proton[1] = tpl_data[H3O_MOL][0][1] # molecule number excess_proton[2] = cfg[H3O_H_TYPE] # type excess_proton[3] = tpl_data[H3O_H_CHARGE] # charge dump_h3o_mol.append(excess_proton) min_dist_id = None min_dist = np.linalg.norm(box) for mol_id, molecule in water_mol_dict.items(): for atom in molecule: if atom[2] == cfg[WAT_O_TYPE]: dist = pbc_dist(np.asarray(excess_proton[4:7]), np.asarray(atom[4:7]), box) if dist < min_dist: min_dist_id = mol_id min_dist = dist logger.debug('Deprotonated residue: the molecule ID of the closest water ' '(to become a hydronium) is {}.'.format(min_dist_id)) # Now that have the closest water, add its atoms to the hydronium list for atom in water_mol_dict[min_dist_id]: dump_h3o_mol.append(atom) # Remove the closest water from the dictionary of water molecules, and convert it to a hydronium del water_mol_dict[min_dist_id] for atom in dump_h3o_mol: if atom[2] == cfg[WAT_O_TYPE]: atom[2] = cfg[H3O_O_TYPE] atom[3] = tpl_data[H3O_O_CHARGE] elif atom[2] == cfg[WAT_H_TYPE]: atom[2] = cfg[H3O_H_TYPE] atom[3] = tpl_data[H3O_H_CHARGE] # Make the atom type and charge of the protonatable residue the same as for the template file (switching # from protonated to deprotonated residue) if len(tpl_data[PROT_RES_MOL]) != len(protonatable_res): raise InvalidDataError('Encountered dump file in which the number of atoms in the ' 'protonatable residue does not equal the number of atoms in the template data file.')
def calc_more_ocoh_props(box, carboxyl_c_xyz, o_star_xyz, alt_o_xyz, excess_h_xyz): """ If requested and there are xyz coords, @param box: xyz box size @param carboxyl_c_xyz: @param o_star_xyz: @param alt_o_xyz: @param excess_h_xyz: @return: a dict of additional calculated properties """ if carboxyl_c_xyz is not None: # TODO: test that accounts for the case when carboxyl group is broken over the PBC # does not have be be shown: move c_carbon to the origin; we know the results of moving it and multiplying # the resulting xyz coords (0, 0, 0) by the COM_WEIGHT_C c_o_star_vec = pbc_calc_vector(o_star_xyz, carboxyl_c_xyz, box) c_alt_o_vec = pbc_calc_vector(alt_o_xyz, carboxyl_c_xyz, box) c_h_star_vec = pbc_calc_vector(excess_h_xyz, carboxyl_c_xyz, box) o_star_h_vec = pbc_calc_vector(excess_h_xyz, o_star_xyz, box) com_xyz = COM_WEIGHT_PER_O * c_o_star_vec com_xyz += COM_WEIGHT_PER_O * c_alt_o_vec oh_prop_dict = { COM_H_DIST: pbc_dist(com_xyz, c_h_star_vec, box), OCO_ANGLE: vec_angle(c_o_star_vec, c_alt_o_vec), OCOH_DIH: vec_dihedral(c_alt_o_vec, c_o_star_vec, o_star_h_vec), COM_XYZ: carboxyl_c_xyz + com_xyz } else: oh_prop_dict = { COM_H_DIST: np.nan, OCO_ANGLE: np.nan, OCOH_DIH: np.nan, COM_XYZ: np.nan } return oh_prop_dict
def find_closest_excess_proton(carboxyl_oxys, prot_h, hydronium, box, cfg, carboxyl_carbon, cec_xyz): # initialize minimum distance to maximum distance possible in the periodic box (assume 90 degree corners) oxy_h_min_dists = np.full(len(carboxyl_oxys), np.linalg.norm(np.divide(box, 2))) # excess proton will become None if prot_h is none, then calculated excess_proton = prot_h for index, oxy in enumerate(carboxyl_oxys): if prot_h is None: for atom in hydronium: if atom[ATOM_TYPE] == cfg[H3O_H_TYPE]: dist = pbc_dist(np.asarray(atom[XYZ_COORDS]), np.asarray(oxy[XYZ_COORDS]), box) if dist < oxy_h_min_dists[index]: oxy_h_min_dists[index] = dist excess_proton = atom else: oxy_h_min_dists[index] = pbc_dist(np.asarray(prot_h[XYZ_COORDS]), np.asarray(oxy[XYZ_COORDS]), box) index_min = np.argmin(oxy_h_min_dists) o_star = carboxyl_oxys[index_min] min_oh_dist = oxy_h_min_dists[index_min] oh_prop_dict = {OH_MIN: min_oh_dist} if cfg[CALC_OCOH_PROPS] or cfg[CALC_CEC_DIST]: # the following depends on there being exactly 2 carboxylic oxygen atoms index_max = abs(index_min - 1) o_star_xyz = np.asarray(o_star[XYZ_COORDS]) excess_proton_xyz = np.asarray(excess_proton[XYZ_COORDS]) alt_o_xyz = np.asarray(carboxyl_oxys[index_max][XYZ_COORDS]) # distance calculated again to ensure that the 2nd return distance uses the same excess proton alt_oh_dist = pbc_dist(excess_proton_xyz, alt_o_xyz, box) oh_prop_dict.update({ OH_MAX: alt_oh_dist, OH_DIFF: alt_oh_dist - min_oh_dist, }) if carboxyl_carbon is not None: carboxyl_c_xyz = np.asarray(carboxyl_carbon[XYZ_COORDS]) oh_prop_dict.update( calc_more_ocoh_props(box, carboxyl_c_xyz, o_star_xyz, alt_o_xyz, excess_proton_xyz)) if cfg[CALC_CEC_DIST] and cec_xyz is not None: cec_o_dists = np.array([ pbc_dist(cec_xyz, o_star_xyz, box), pbc_dist(cec_xyz, alt_o_xyz, box) ]) oh_prop_dict[CEC_O_MIN] = cec_o_dists.min() oh_prop_dict[CEC_O_MAX] = cec_o_dists.max() oh_prop_dict[ CEC_O_DIFF] = oh_prop_dict[CEC_O_MAX] - oh_prop_dict[CEC_O_MIN] oh_prop_dict[CEC_H_DIST] = pbc_dist(cec_xyz, excess_proton_xyz, box) oh_prop_dict[CEC_O_MIN_DIST] = calc_min_dist( np.array([oh_prop_dict[CEC_O_MIN], oh_prop_dict[CEC_O_MAX]]), cfg[MIN_DIST_BETA]) if carboxyl_carbon is not None: oh_prop_dict[CEC_COM_DIST] = pbc_dist(cec_xyz, oh_prop_dict[COM_XYZ], box) return excess_proton, o_star, oh_prop_dict