def test_bad_atom_cg_fail(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] prc.charge_groups[1].append("BadAtom") with pytest.raises(QLibError): prc.check_valid()
def test_info_fail(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] prc.info["random_number_generation"] = "true" with pytest.raises(QLibError): prc.check_valid()
def test_net_charge_cg_fail(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] prc.atoms[0].charge -= 1e-7 with pytest.raises(QLibError): prc.check_valid()
def test_bad_atom_imps_fail(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] prc._add_improper("C1", ["C4", "C7", "BadAtom"]) with pytest.raises(QLibError): prc.check_valid()
def test_rescale(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] prc.atoms[0].charge -= 0.5 prc.rescale(prc.charge_groups[0], 1) assert is_close(prc.atoms[0].charge, -0.72561699999)
def test_rescale_over_threshold_fail(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] prc.atoms[0].charge -= 0.6 with pytest.raises(QLibError): prc.rescale(prc.charge_groups[0], 0.3)
def test_bad_atom_bonds_fail(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] prc.bonds.append(["C1", "BadAtom"]) with pytest.raises(QLibError): prc.check_valid()
def test_dupatom_fail(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] prc.atoms.append(prc.atoms[0]) with pytest.raises(QLibError): prc.check_valid()
def test_atom(self): qlib = QLib("amber") qlib.read_lib("data/qamber14.lib") trp_CG = qlib.residue_dict["TRP"].atoms[7] assert trp_CG.name == "CG" assert trp_CG.atom_type == "Cstar" assert is_close(trp_CG.charge, -0.1415) assert trp_CG.residue.name == "TRP"
def test_rounding(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] old_charge = prc.atoms[0].charge prc.atoms[0].charge -= 1e-7 prc.rescale(prc.charge_groups[0], 1) assert is_close(prc.atoms[0].charge, old_charge)
def test_rescale(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] print([a.charge for a in prc.atoms]) prc.atoms[0].charge -= 0.51 prc.rescale(prc.charge_groups[0], 1) print([a.charge for a in prc.atoms]) assert is_close(prc.atoms[0].charge, -0.733591)
def test_residue(self): qlib = QLib("amber") qlib.read_lib("data/qamber14.lib") asp = qlib.residue_dict["ASP"] assert asp.bonds[5] == ("CB", "HB2") assert asp.impropers[0] == ["-C", "N", "CA", "H"] assert asp.connections == ["head N", "tail C"] hoh = qlib.residue_dict["HOH"] assert int(hoh.info["solvent"]) == 1 assert is_close(float(hoh.info["density"]), 0.0335)
def test_read_ffld(self): qlib = QLib("oplsaa") qstruct = QStruct("data/ace_ash_nma.pdb", "pdb") qlib.read_ffld("data/ace_ash_nma.ffld11", qstruct) assert len(qlib.residue_dict) == 3 assert len(qlib.residue_dict["ACE"].atoms) == 6 assert len(qlib.residue_dict["ASH"].atoms) == 13 assert len(qlib.residue_dict["NMA"].atoms) == 6 ash = qlib.residue_dict["ASH"] assert ash.atoms[1].atom_type == "ash.CA" assert is_close(ash.atoms[1].charge, 0.14) assert "tail C" in ash.connections assert "head N" in ash.connections
def test_build_rules_fail(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] prc.build_rules = ["torsion C1 C4 C7 H9 0"] # passes prc.check_valid() prc.build_rules = ["torsion C1 C4 C7 H9"] with pytest.raises(QLibError): prc.check_valid() prc.build_rules = ["bad_build_rule C1 C4 C7 H9 0"] with pytest.raises(QLibError): prc.check_valid() prc.build_rules = ["torsion C1 BadAtom C7 H9 0"] with pytest.raises(QLibError): prc.check_valid()
def test_read_prepin_impropers_fail(self): # no residues in library qlib = QLib("amber") with pytest.raises(QLibError): qlib.read_amber_lib("data/ff-amber14/prep/amino12.in")
if len(args.frcmods) < 1: print("FATAL! At least one frcmod file is required.") sys.exit() for k, v in six.iteritems(vars(args)): if k in ["mol2", "prepi", "parm", "frcmods"]: for fn in v: if not os.path.lexists(fn): print("File '{}' doesn't exist.".format(fn)) sys.exit(1) # # create QLib, QPrm, QStruct and QTopology objects # qlib = QLib("amber", ignore_errors=args.ignore_errors) qprm = QPrm("amber", ignore_errors=args.ignore_errors) try: qlib.read_mol2(args.mol2[0]) except QLibError as e: print("FATAL! Problem with mol2: {}".format(str(e))) sys.exit(1) try: qlib.read_prepin_impropers(args.prepi[0]) except QLibError as e: print("FATAL! Problem with prepi: {}".format(str(e))) sys.exit(1) for parm in args.parms: try: qprm.read_amber_parm(parm)
def test_rescale_badatom_fail(self): qlib = QLib("oplsaa") qlib.read_lib("data/prc.lib") prc = qlib.residue_dict["PRC"] with pytest.raises(QLibError): prc.rescale(prc.charge_groups[0] + ["BadAtom"], 0.5)
def test_read_write_lib(self): qlib = QLib("amber") qlib.read_lib("data/qamber14.lib") qlib.residue_dict.pop("HOH") # different order ql_str2 = qlib.get_string() assert ql_str2 in open("data/qamber14.lib", "r").read()
def test_read_mol2_fail(self): # no residues found qlib = QLib("amber") with pytest.raises(QLibError): qlib.read_amber_lib("data/ff-amber14/parm/parm10.dat")
def read_amber_conversion(self): qlib = QLib("amber") qlib.read_amber_lib("data/ff-amber14/amber12_mod.lib") qlib.read_amber_lib("data/ff-amber14/arn.lib") qlib.read_prepin_impropers("data/ff-amber14/prep/amino12.in") qlib.read_prepin_impropers("data/ff-amber14/arn.prepi") # remove head from ACE and tail from NME cons = qlib.residue_dict["ACE"].connections cons = [con for con in cons if "head" not in con] qlib.residue_dict["ACE"].connections = cons cons = qlib.residue_dict["NME"].connections cons = [con for con in cons if "tail" not in con] qlib.residue_dict["NME"].connections = cons assert qlib.get_string() in open("data/qamber14.lib").read()
def test_read_ffld_wrong_order_fail(self): # see if it fails with PDB with wrong atom-order qlib = QLib("oplsaa") qstruct = QStruct("data/ace_ash_nma_bad.pdb", "pdb") with pytest.raises(QLibError): qlib.read_ffld("data/ace_ash_nma.ffld11", qstruct)
def test_wrong_ff_fail(self): qlib = QLib("amber", ignore_errors=True) with pytest.raises(QLibError): qlib.read_ffld("data/ace_ash_nma.ffld11", None) qlib = QLib("oplsaa", ignore_errors=True) with pytest.raises(QLibError): qlib.read_amber_lib("data/ff-amber14/lib/amino12.lib") with pytest.raises(QLibError): qlib.read_prepin_impropers("data/ff-amber14/lib/amino12.lib") with pytest.raises(QLibError): qlib.read_mol2("data/all_amino_acids.mol2")
def test_read_ffld_fail(self): # no residues found qlib = QLib("oplsaa") qstruct = QStruct("data/ace_ash_nma.pdb", "pdb") with pytest.raises(QLibError): qlib.read_ffld("data/ace_ash_nma.pdb", qstruct)
def log_dups(dups): for dup in dups: logger.info("Overwritten: {}".format(dup)) # # Amber14FF to Qamber14 # # Convert Amber14 lib (+prepin for impropers) and parm+frcmod to Q lib/prm # Load all_amino_acids.pdb and all_amino_acids.mol2 and build topology # separately. # # ignore_errors=True is needed because frcmod.ff14SB overwrites some parameters # qal = QLib("amber") qap = QPrm("amber", ignore_errors=True) qal.read_amber_lib("../Amber/ff-amber14/amber12_mod.lib") qal.read_amber_lib("../Amber/ff-amber14/arn.lib") qal.read_prepin_impropers("../Amber/ff-amber14/prep/amino12.in") qal.read_prepin_impropers("../Amber/ff-amber14/arn.prepi") log_dups(qap.read_amber_parm("../Amber/ff-amber14/parm/parm10.dat")) log_dups(qap.read_amber_parm("../Amber/ff-amber14/parm/parm10.dat")) log_dups(qap.read_amber_frcmod("../Amber/ff-amber14/parm/frcmod.ff14SB")) # add options to parameters for line in """name Q-Amber14SB type AMBER vdw_rule arithmetic !vdW combination rule (geometric or arithmetic) scale_14 0.8333 ! electrostatic 1-4 scaling factor switch_atoms off
def make_fep(qmap_file, pdb_file, forcefield, parm_files, lib_files, ignore_errors=False): """Generate a template FEP file for EVB simulations in Q. Parses a QMAP file (see below), state 1 structure file (PDB) and all states libraries and parameters, and determines the changes in connectivity/charges/parameters that occur between the states. QMAP is a text file that defines mappings of library ids (for each state) to state 1 structure/topology ids, best explained on an example: q 315.O OHH.O OHH.O q 315.H1 OHH.H1 HIP.HE2 q 315.H2 OHH.H2 OHH.H2 q 155.NE2 HID.NE2 HIP.NE2 ... n 155.CA HID.CA HIP.CA The first column defines the atom as being a 'Q' atom or a 'neighbouring' atom. The latter will not be included in the 'Q-region' but will be included in the 'change_bonds/change_angles...' sections in case there is a change in bonding/parameters outside the Q region. Additionally, you can define a 'q' atom with 'q_qcp', which will create an additional section for isotopically clean masses used in QCP calculations. The second column is the PDB ID, comprised of residue index and atom name, separated by a dot. The third column is the library ID of this atom in state 1, comprised of residue name and atom name (should be the same as in the structure). The fourth column is the library ID of this atom in state 2. Additional columns can be added for other states. The returned template string contains several missing parts, denoted with <FIX>, which have to be manually replaced with appropriate values. These include the softpair C parameters, Morse parameters, Hij parameters. Args: qmap_file (string): QMAP file path pdb_file (string): state 1 PDB file path (the one built with qprep) forcefield (string): forcefield type (see SUPPORTED_FF) prms_files (list): Q parameter-file paths libs_files (list): Q library-file paths ignore_errors (boolean, optional): don't fail on certain non critical errors Returns: fepstr (string): fepfile template Raises: QMakeFepError """ if forcefield not in SUPPORTED_FF: raise QMakeFepError("Force field '{}' not supported. Use {}" "".format(forcefield, " or ".join(SUPPORTED_FF))) fep_types = { "atoms": [], "bonds": [], "angles": [], "torsions": [], "impropers": [] } fep_changes = { "atoms": [], "charges": [], "bonds": ODict(), "angles": ODict(), "torsions": ODict(), "impropers": ODict() } fep_qcp_atoms = [] fep_morse_prms = {} fep_reacting_atoms = set() num_evb_states = None # parse the MAP file # pdb_ids_map = [ ('q', [pdbid1_state1,]), # ('q', [pdbid2_state1,]), # ... # ('n', [pdbid11_state1,]), # ... # ] # lib_ids_map = [ [lib_id1_state1, lib_id2_state1...], # [lib_id1_state2, lib_id2_state2...], # ... # ] # lib_ids_map = [] pdb_ids_map = [] with open(qmap_file, 'r') as qatom_map: for i, line in enumerate(qatom_map.readlines()): line = re.split("#|\*|\!", line, 1)[0].strip() # remove comments if line == "": continue c = line.split() atom_type = c[0].lower() pdb_id = c[1] lib_ids = c[2:] if atom_type not in ["q", "n", "q_qcp"]: raise QMakeFepError("Lines in the QMAP file should begin " "with either 'q' (qatom) or 'n' " "(neighboring atom) or 'q_qcp' " "(QPI q atom)") try: resid, name = pdb_id.split(".") if not name or not int(resid): raise ValueError except ValueError: raise QMakeFepError("Invalid PDB ID '{}'. Should be " "RESID.ATOMNAME".format(pdb_id)) tmp = (atom_type, [ pdb_id, ]) if tmp in pdb_ids_map: raise QMakeFepError("Duplicate PDB ID: '{}'".format(pdb_id)) pdb_ids_map.append(tmp) if num_evb_states == None: num_evb_states = len(lib_ids) elif len(lib_ids) != num_evb_states: raise QMakeFepError("Number of states in line '{}' not equal " "to number of PDB files".format(line)) for state, lib_id in enumerate(lib_ids): try: resname, name = lib_id.split(".") if not resname or not name: raise ValueError except ValueError: raise QMakeFepError("Invalid library ID '{}'. Should be " "RESNAME.ATOMNAME".format(lib_id)) try: if lib_id in lib_ids_map[state]: raise QMakeFepError("The library IDs in one EVB state " "should be unique (double '{}'), " "otherwise proper bonding can't " "be determined.".format(lib_id)) except IndexError: lib_ids_map.append([]) lib_ids_map[state].append(lib_id) # load libraries qlib = QLib(forcefield, ignore_errors=ignore_errors) for lib in lib_files: try: qlib.read_lib(lib) except QLibError as e: raise QMakeFepError("Problem parsing lib ({}): {}" "".format(lib, e)) # make dummy structures for other states structures = [None for _ in range(num_evb_states)] structures[0] = pdb_file libid_pdbid_map = [{} for _ in range(num_evb_states)] for state in range(1, num_evb_states): state_structure = [] atom_index = 1 processed_residues = [] for i, (q_or_n, pdb_ids_all_states) in enumerate(pdb_ids_map): lib_id = lib_ids_map[state][i] resname, aname = lib_id.split(".") # add all atoms of current residue to the dummy structure # at the same time, storing the mapping lib_id:pdb_id # in libid_pdbid_map for later if resname not in processed_residues: try: residue_lib = qlib.residue_dict[resname] except KeyError: raise QMakeFepError("Residue '{}' not found in library." "".format(resname)) processed_residues.append(resname) res_index = len(processed_residues) for atom in residue_lib.atoms: lib_id2 = "{}.{}".format(resname, atom.name) pdb_id2 = "{}.{}".format(res_index, atom.name) state_structure.append("{:<6}{:5} {:4} {:3} {:5} " "{:8.3f}{:8.3f}{:8.3f}" "".format("ATOM", atom_index, atom.name, resname, res_index, 0, 0, 0)) atom_index += 1 # map the newly created dummy atom's pdb_id to lib_id libid_pdbid_map[state][lib_id2] = pdb_id2 # add pdb_id of current atom in current (dummy structure) # state to pdb_ids_map (using its lib_id) try: pdb_id_this_state = libid_pdbid_map[state][lib_id] except KeyError: raise QMakeFepError( "Library ID '{}' not valid.".format(lib_id)) pdb_ids_all_states.append(pdb_id_this_state) _, structures[state] = tempfile.mkstemp() open(structures[state], "w").write("\n".join(state_structure)) # DEBUG # print "Dummy PDB for st.{}: {}".format(state + 1, structures[state]) # load parameters qprm = QPrm(forcefield, ignore_errors=ignore_errors) for parm in parm_files: try: qprm.read_prm(parm) except QPrmError as e: raise QMakeFepError("Problem with parm ({}): {}" "".format(parm, e)) # load structures and make topologies topologies = [] for state in range(num_evb_states): try: qstruct = QStruct(structures[state], "pdb", ignore_errors=ignore_errors) except QStructError as e: raise QMakeFepError("Problem parsing PDB file ({}): {} " "".format(structures[state], e)) try: topologies.append(QTopology(qlib, qprm, qstruct)) except QTopologyError as e: raise QMakeFepError("Problem building the topology: {}" "".format(e)) # Make _TopoAtom (atoms in QTopology) maps out of qmap's lists # and extract types, type changes and charge changes # # atom_map = [ [_TopoAtom1_state1, _TopoAtom1_state2, ... ], # [_TopoAtom2_state1, _TopoAtom2_state2, ... ], # [_TopoAtom3_state1, _TopoAtom3_state2, ... ], # ... # ] atom_map = [] for i, (q_or_n, pdb_id_all_states) in enumerate(pdb_ids_map): atom_all_states = [] for state, pdb_id in enumerate(pdb_id_all_states): residue, aname = pdb_id.split(".") try: residue = topologies[state].residues[int(residue) - 1] atom = [a for a in residue.atoms if a.name == aname][0] except (KeyError, IndexError) as e: raise QMakeFepError("Atom '{}' doesn't exist in PDB '{}'" "".format(pdb_id, structures[state])) atom_all_states.append(atom) atom_map.append(atom_all_states) # check for stupidity - lib_id in QMAP state 1 # not matching the structure/topology lib_id_qmap = lib_ids_map[0][i] lib_id = "{}.{}".format(atom_all_states[0].residue.name, atom_all_states[0].name) if lib_id != lib_id_qmap: pdb_id = pdb_ids_map[i][1][0] raise QMakeFepError("QMAP state 1 library ID ({}) of atom '{}' " "doesn't match topology library ID ({})." "".format(lib_id_qmap, pdb_id, lib_id)) # For Q atoms (and not the neighbor atoms): # get FEP atom types, type changes and charge changes if q_or_n in ["q", "q_qcp"]: for atom in atom_all_states: if atom.prm not in fep_types["atoms"]: fep_types["atoms"].append(atom.prm) fep_changes["charges"].append([a.charge for a in atom_all_states]) fep_changes["atoms"].append(atom_all_states) if q_or_n == "q_qcp": fep_qcp_atoms.append(atom_all_states) charge_sums = [] for state in range(num_evb_states): charge_sum = sum([c[state] for c in fep_changes["charges"]]) if abs(round(charge_sum) - charge_sum) > 1e-6: raise_or_log("Net charge in state {} not integer: {}" "".format(state + 1, charge_sum), QMakeFepError, logger, ignore_errors=ignore_errors) charge_sums.append(charge_sum) if any([abs(c - charge_sums[0]) > 1e-6 for c in charge_sums]): logger.warning("Net charge changes between states: {}" "".format(" -> ".join([str(c) for c in charge_sums]))) # get all Bonds, Angles, Torsions and Impropers which include # at least one atom defined in qmap batis = {"bonds": [], "angles": [], "torsions": [], "impropers": []} batis["bonds"] = [set() for _ in range(num_evb_states)] batis["angles"] = [set() for _ in range(num_evb_states)] batis["torsions"] = [set() for _ in range(num_evb_states)] batis["impropers"] = [set() for _ in range(num_evb_states)] for atom_all_states in atom_map: for state, atom in enumerate(atom_all_states): _ = [batis["bonds"][state].add(b) for b in atom.bonds] _ = [batis["angles"][state].add(a) for a in atom.angles] _ = [batis["torsions"][state].add(t) for t in atom.torsions] _ = [batis["impropers"][state].add(i) for i in atom.impropers] # map the bonds,angles,torsions,impropers (bati) in different states # to same key (ordered list of state1 PDB_IDs) # # bati_map = # { "bonds": {state1_bond1_key: [bond1_state1, bond1_state2,...], # state1_bond2_key: [bond2_state1, bond2_state2,...], ...}, # "angles": {state1_angle1_key: [angle1_state1, angle1_state2,...],...} # ... } # # also, include only batis which have all atoms defined in qmap # also, detect inter-residue batis and raies QMakeFepError bati_map = {"bonds": {}, "angles": {}, "torsions": {}, "impropers": {}} for state in range(num_evb_states): atoms_in_state = [a_all_st[state] for a_all_st in atom_map] for bati_type in bati_map: for bati in batis[bati_type][state]: # find the corresponding atoms in state1 try: atoms_st1 = [ atom_map[atoms_in_state.index(a)][0] for a in bati.atoms ] except ValueError: # one of the Atoms is not defined in QMAP continue pdbid_index = [] for atom in atoms_st1: pdbid_index.append( (atom.index, "{}.{}".format(atom.residue.index, atom.name))) # order the pdbids to prevent double entries if bati_type == "bonds": pids = sorted(pdbid_index) elif bati_type == "angles": pids = min(pdbid_index, list(reversed(pdbid_index))) elif bati_type == "torsions": pids = min(pdbid_index, list(reversed(pdbid_index))) elif bati_type == "impropers": # topology order == library order == correct order pids = pdbid_index key = " ".join([p[1] for p in pids]) # check for bonds/angles/torsions/impropers that are # shared between residues residue_ids = set(atom.residue.index for atom in bati.atoms) if len(residue_ids) > 1: raise QMakeFepError("Inter-residue bond/angle/torsion '{}'" " not supported. Combine the residues " "into a single library entry if you " "want to make changes over the " "'head-tail' bond.".format(key)) # add bati to bati_map try: bati_map[bati_type][key][state] = bati except KeyError: bati_map[bati_type][key] = [ None for _ in range(num_evb_states) ] bati_map[bati_type][key][state] = bati # DEBUG # for k,v in bati_map.iteritems(): # print k # for k2, v2 in v.iteritems(): # print k2, v2[0], v2[1] def _bati_sort(key, bati_all_states): # to sort bonds/angles.. based on the key # also, breaking and forming bonds have priority try: return (-1 * bati_all_states.index(None), key) except: return (1, key) # find changes between states (add to fep_changes dict) for bati_type, batis in bati_map.iteritems(): for bati_key, bati_all_states in sorted(batis.items(), key=lambda (key, val): \ _bati_sort(key, val)): # bond/angle/.. breaking or forming if None in bati_all_states: fep_changes[bati_type][bati_key] = bati_all_states # add bond atoms to "reactive atoms" set # and replace the bond parameter with a Morse type if bati_type == "bonds": for bati in bati_all_states: if bati != None: fep_reacting_atoms |= set(bati.atoms) # the bond parameter is replaced with a Morse # parameter (_FepPrmMorse) prm_id = bati.prm.prm_id try: bati.prm = fep_morse_prms[prm_id] except KeyError: bati.prm = _FepPrmMorse(bati.prm) fep_morse_prms[prm_id] = bati.prm # the actual values of the parameters are not exactly the same else: tmp = [ bati_all_states[0].prm.strval == bati.prm.strval for bati in bati_all_states ] if not all(tmp): fep_changes[bati_type][bati_key] = bati_all_states # DEBUG # for k,v in fep_changes.iteritems(): # print k # try: # for k2,(v1,v2) in v.iteritems(): # print k2,v1,v2 # except: # for (v1,v2) in v: # print v1,v2 # add parameters of changing batis to fep_types for bati_type in bati_map: for bati_all_states in fep_changes[bati_type].values(): prms = [bati.prm for bati in bati_all_states if bati != None] for prm in prms: if prm not in fep_types[bati_type]: fep_types[bati_type].append(prm) # DEBUG # for k,v in fep_types.iteritems(): # print k # for v2 in v: # print v2 # add reactive atoms from states that have bond==None to fep_reacting_atoms for atom_all_states in fep_changes["atoms"]: for atom in atom_all_states: if atom in fep_reacting_atoms: fep_reacting_atoms |= set(atom_all_states) ######################## # Prepare the output ######################## fep_l = { "atoms": [], "atom_types": [], "qcp_mass": [], "change_atoms": [], "change_charges": [], "soft_pairs": [], "off_diagonals": [], "bond_types": [], "change_bonds": [], "angle_types": [], "change_angles": [], "torsion_types": [], "change_torsions": [], "improper_types": [], "change_impropers": [] } #################### # ATOMS # CHANGE_ATOMS # CHANGE_CHARGES #################### format_atoms = "{:<15} {:<10} # {:<15} {:<15} {:>3}" format_ch_atoms = "{:<10} " + " {:<12}" * num_evb_states + " # {:<}" format_ch_crgs = "{:<10} " + " {:12}"*num_evb_states + " # {:<10}"\ + " {:>12}"*(num_evb_states-1) format_qcp = "{:<15} {:<10} # {:<10}" fep_l["atoms"].append( format_atoms.format("#Q index", "PDB index", "St.1 PDB_ID", "St.1 LIB_ID", "")) tmp = ["#Q index"] tmp.extend(["Type st.{}".format(n + 1) for n in range(num_evb_states)]) tmp.append("St.1 PDB_ID") fep_l["change_atoms"].append(format_ch_atoms.format(*tmp)) tmp = ["#Q index"] tmp.extend(["Charge st.{}".format(n + 1) for n in range(num_evb_states)]) tmp.append("St.1 PDB_ID") tmp.extend( ["dq({}->{})".format(n + 1, n + 2) for n in range(num_evb_states - 1)]) fep_l["change_charges"].append(format_ch_crgs.format(*tmp)) if fep_qcp_atoms: fep_l["qcp_mass"].append("[qcp_mass]") fep_l["qcp_mass"].append( format_qcp.format("#Q index", "Mass", "St.1 PDB_ID")) for i, atom_all_states in enumerate(fep_changes["atoms"]): q_index = i + 1 a = atom_all_states[0] pdb_id = "{}.{}".format(a.residue.index, a.name) lib_id = "{}.{}".format(a.residue.name, a.name) # atoms reacting_flag = " !" * bool( [atom for atom in atom_all_states if atom in fep_reacting_atoms]) fep_l["atoms"].append( format_atoms.format(q_index, "$" + pdb_id + "$", pdb_id, lib_id, reacting_flag)) # change_atoms tmp = [q_index] + [a.prm.prm_id for a in atom_all_states] + [pdb_id] fep_l["change_atoms"].append(format_ch_atoms.format(*tmp)) # charges crgs = [float(a.charge) for a in atom_all_states] tmp = [q_index] + crgs + [pdb_id] \ + [crgs[n+1]-crgs[n] for n in range(num_evb_states-1)] fep_l["change_charges"].append(format_ch_crgs.format(*tmp)) # qcp_atoms if atom_all_states in fep_qcp_atoms: fep_l["qcp_mass"].append( format_qcp.format(q_index, "<FIX>", pdb_id)) ############### # ATOM_TYPES ############### format_atypes = "{:<12} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}" if forcefield == "amber": fep_l["atom_types"].append( format_atypes.format("#Atom_type", "LJ_Rm", "LJ_eps", "SP_Ci", "SP_ai", "LJ_Rm", "LJ_eps_14", "mass")) else: fep_l["atom_types"].append( format_atypes.format("#Atom_type", "LJ_A", "LJ_B", "SP_Ci", "SP_ai", "LJ_A_14", "LJ_B_14", "mass")) fep_reacting_atoms_prms = [a.prm for a in fep_reacting_atoms] for prm in fep_types["atoms"]: sp_c = 1 sp_a = 2.5 if prm in fep_reacting_atoms_prms: sp_c = "<FIX>" if forcefield == "amber": lj1, lj2 = prm.lj_R, prm.lj_eps lj3, lj4 = lj1, round(lj2 / 1.2, 4) else: lj1, lj2 = prm.lj_A, prm.lj_B lj3, lj4 = round(lj1 / (2**0.5), 4), round(lj2 / (2**0.5), 4) fep_l["atom_types"].append( format_atypes.format(prm.prm_id, lj1, lj2, sp_c, sp_a, lj3, lj4, prm.mass)) ############### # BOND_TYPES ############### format_hbonds = "{:<8} {:>10} {:>10} # {}" format_mbonds = "{:<8} {:^10} {:^10} {:>10} # {}" fep_l["bond_types"].append("## Harmonic format") fep_l["bond_types"].append( format_hbonds.format("#Index", "Fc", "r0", "PRM_ID")) fep_l["bond_types"].append("## Morse format") fep_l["bond_types"].append( format_mbonds.format("#Index", "D", "alpha", "r0", "PRM_ID")) for i, bond_type in enumerate(fep_types["bonds"]): b_index = i + 1 if isinstance(bond_type, _FepPrmMorse): prm_id = "-".join(bond_type.harmonic_prm.prm_id.split()) tmp = format_mbonds.format(b_index, "<FIX_D>", "<FIX_a>", "<FIX_r0>", prm_id) fep_l["bond_types"].append(tmp) else: prm_id = "-".join(bond_type.prm_id.split()) tmp = format_hbonds.format(b_index, bond_type.fc, bond_type.r0, prm_id) fep_l["bond_types"].append(tmp) ############### # CHANGE_BONDS ############### format_bondch = "{:<10} {:<10} " + "{:^5} " * num_evb_states + " # {}" tmp = ["#Atom1", "Atom2"] tmp.extend(["St.{}".format(n + 1) for n in range(num_evb_states)]) tmp.append("St.1 PDB_IDs") fep_l["change_bonds"].append(format_bondch.format(*tmp)) for bond_key, bond_all_states in fep_changes["bonds"].iteritems(): # bond_key == "PDB_ID1 PDB_ID2" prm_indexes = [] for b in bond_all_states: if b == None: prm_indexes.append(0) else: btype_index = fep_types["bonds"].index(b.prm) + 1 prm_indexes.append(btype_index) placeholders = ["${}$".format(a) for a in bond_key.split()] pdb_id = "-".join(bond_key.split()) tmp = placeholders + prm_indexes + [pdb_id] fep_l["change_bonds"].append(format_bondch.format(*tmp)) ############### # ANGLE_TYPES ############### format_angles = "{:<8} {:>10} {:>10} # {}" fep_l["angle_types"].append( format_angles.format("#Index", "Fc", "theta0", "PRM_ID")) for i, angle_type in enumerate(fep_types["angles"]): an_index = i + 1 prm_id = "-".join(angle_type.prm_id.split()) tmp = format_angles.format(an_index, angle_type.fc, angle_type.theta0, prm_id) fep_l["angle_types"].append(tmp) ################# # CHANGE_ANGLES ################# format_angch = "{:<10} {:<10} {:<10} " + "{:^5} " * num_evb_states + " # {}" tmp = ["#Atom1", "Atom2", "Atom3"] tmp.extend(["St.{}".format(n + 1) for n in range(num_evb_states)]) tmp.append("St.1 PDB_IDs") fep_l["change_angles"].append(format_angch.format(*tmp)) for angle_key, angle_all_states in fep_changes["angles"].iteritems(): # angle_key == "PDB_ID1 PDB_ID2 PDB_ID3" prm_indexes = [] for ang in angle_all_states: if ang == None: prm_indexes.append(0) else: atype_index = fep_types["angles"].index(ang.prm) + 1 prm_indexes.append(atype_index) placeholders = ["${}$".format(a) for a in angle_key.split()] pdb_id = "-".join(angle_key.split()) tmp = placeholders + prm_indexes + [pdb_id] fep_l["change_angles"].append(format_angch.format(*tmp)) ################# # TORSION_TYPES ################# format_torsions = "{:<8} {:>10} {:>10} {:>10} # {}" fep_l["torsion_types"].append( format_torsions.format("#Index", "Fc", "mult", "psi0", "PRM_ID")) tor_index = 1 tor_indexes = [] for i, torsion_type in enumerate(fep_types["torsions"]): prm_id = "-".join(torsion_type.prm_id.split()) prm_indexes = [] for fc, per, psi0, npath in torsion_type.get_prms(): fc = fc / npath tmp = format_torsions.format(tor_index, fc, per, psi0, prm_id) fep_l["torsion_types"].append(tmp) prm_indexes.append(tor_index) tor_index += 1 tor_indexes.append(prm_indexes) ################### # CHANGE_TORSIONS ################### format_torch = "{:<10} {:<10} {:<10} {:<10} " \ + "{:^5} "*num_evb_states + " # {}" tmp = ["#Atom1", "Atom2", "Atom3", "Atom4"] tmp.extend(["St.{}".format(n + 1) for n in range(num_evb_states)]) tmp.append("St.1 PDB_IDs") fep_l["change_torsions"].append(format_torch.format(*tmp)) for torsion_key, torsion_all_states in fep_changes["torsions"].iteritems(): # torsion_key == "PDB_ID1 PDB_ID2 PDB_ID3 PDB_ID4" for state, tor in enumerate(torsion_all_states): if tor == None: continue for i in range(len(tor.prm.fcs)): tprm_index = fep_types["torsions"].index(tor.prm) ttype_index = tor_indexes[tprm_index][i] prm_indexes = [0 for _ in range(len(torsion_all_states))] prm_indexes[state] = ttype_index placeholders = ["${}$".format(t) for t in torsion_key.split()] pdb_id = "-".join(torsion_key.split()) tmp = placeholders + prm_indexes + [pdb_id] fep_l["change_torsions"].append(format_torch.format(*tmp)) ################# # IMPROPER_TYPES ################# format_impropers = "{:<8} {:>10} {:>10} # {}" fep_l["improper_types"].append( format_impropers.format("#Index", "Fc", "phi0", "PRM_ID")) for i, improper_type in enumerate(fep_types["impropers"]): imp_index = i + 1 prm_id = "-".join(improper_type.prm_id.split()) tmp = format_impropers.format(imp_index, improper_type.fc, improper_type.phi0, prm_id) fep_l["improper_types"].append(tmp) ################### # CHANGE_IMPROPERS ################### format_impch = "{:<10} {:<10} {:<10} {:<10} " \ + "{:^5} "*num_evb_states + " # {}" tmp = ["#Atom1", "Atom2", "Atom3", "Atom4"] tmp.extend(["St.{}".format(n + 1) for n in range(num_evb_states)]) tmp.append("St.1 PDB_IDs") fep_l["change_impropers"].append(format_impch.format(*tmp)) for improper_key, improper_all_states in fep_changes[ "impropers"].iteritems(): # improper_key == "PDB_ID1 PDB_ID2 PDB_ID3 PDB_ID4" prm_indexes = [] for imp in improper_all_states: if imp == None: prm_indexes.append(0) else: itype_index = fep_types["impropers"].index(imp.prm) + 1 prm_indexes.append(itype_index) placeholders = ["${}$".format(i) for i in improper_key.split()] pdb_id = "-".join(improper_key.split()) tmp = placeholders + prm_indexes + [pdb_id] fep_l["change_impropers"].append(format_impch.format(*tmp)) ############## # SOFT_PAIRS ############## for bond_key, bond_all_states in fep_changes["bonds"].iteritems(): if None in bond_all_states: for state, bond in enumerate(bond_all_states): if bond == None: continue atoms_in_state = [atom_all_states[state] for atom_all_states \ in fep_changes["atoms"]] a1_qindex = atoms_in_state.index(bond.atoms[0]) + 1 a2_qindex = atoms_in_state.index(bond.atoms[1]) + 1 fep_l["soft_pairs"].append("{:10} {:10}".format( a1_qindex, a2_qindex)) for k in fep_l.keys(): fep_l[k] = "\n".join(fep_l[k]) fepstr = """\ # Generated with Qtools, version {version} # Date: {date} # CWD: {cwd} # CMDline: {cmd} # [FEP] states {states} [atoms] {atoms} [atom_types] {atom_types} [change_atoms] {change_atoms} [change_charges] {change_charges} [soft_pairs] {soft_pairs} [off_diagonals] # State_i State_j Atom1 Atom2 A_ij mu_ij # ## Example1, Hij=H12=0 (not known in advance) ## 1 2 13 14 0 0 ## Example2, Hij=H12=C*exp(-mu * r_13_14) (C=20.0, mu=0.45) ## 1 2 13 14 20.0 0.45 # <FIX> [bond_types] {bond_types} [change_bonds] {change_bonds} [angle_types] {angle_types} [change_angles] {change_angles} [torsion_types] {torsion_types} [change_torsions] {change_torsions} [improper_types] {improper_types} [change_impropers] {change_impropers} {qcp_mass} """.format(states=num_evb_states, date=time.ctime(), cmd=" ".join(sys.argv), cwd=os.getcwd(), version=__version__, **fep_l) return fepstr
def test_ff14sb_conversion(): # Amber14FF to Qamber14 # # Convert Amber14 lib (+prepin for impropers) and parm+frcmod to Q lib/prm # Load the structure 'all_amino_acids.pdb' and build the topology # Check the total bonding energy contributions and number of bonding terms # and compare the library and parameter set with official qamber14. # qal = QLib("amber") qap = QPrm("amber", ignore_errors=True) # duplicates qal.read_amber_lib("data/ff-amber14/amber12_mod.lib") qal.read_amber_lib("data/ff-amber14/arn.lib") qal.read_prepin_impropers("data/ff-amber14/prep/amino12.in") qal.read_prepin_impropers("data/ff-amber14/arn.prepi") qap.read_amber_parm("data/ff-amber14/parm/parm10.dat") qap.read_amber_frcmod("data/ff-amber14/parm/frcmod.ff14SB") # add options to parameters for line in """\ name Q-Amber14SB type AMBER vdw_rule arithmetic !vdW combination rule (geometric or arithmetic) scale_14 0.8333 ! electrostatic 1-4 scaling factor switch_atoms off improper_potential periodic improper_definition explicit\ """.splitlines(): lf = line.split() qap.options[lf[0]] = " ".join(lf[1:]) # remove head from ACE and tail from NME cons = qal.residue_dict["ACE"].connections cons = [con for con in cons if "head" not in con] qal.residue_dict["ACE"].connections = cons cons = qal.residue_dict["NME"].connections cons = [con for con in cons if "tail" not in con] qal.residue_dict["NME"].connections = cons qas1 = QStruct("data/all_amino_acids.pdb", "pdb", ignore_errors=True) qat = QTopology(qal, qap, qas1) q_tors = sum([len(list(tor.prm.get_prms())) for tor in qat.torsions]) assert len(qat.bonds) == 464 assert len(qat.angles) == 829 assert len(qat.torsions) == 1221 assert q_tors == 1950 assert len(qat.impropers) == 102 be = sum([bond.calc()[0] for bond in qat.bonds]) ae = sum([ang.calc()[0] for ang in qat.angles]) te = sum([tor.calc()[0] for tor in qat.torsions]) ie = sum([imp.calc()[0] for imp in qat.impropers]) assert is_close(be, 181.2572830) assert is_close(ae, 212.8539304) assert is_close(te, 417.2919960) assert is_close(ie, 22.8171235) # compare with official lib qa14_lib = open("data/qamber14.lib", "r").read() qa14_prm = open("data/qamber14.prm", "r").read() assert qal.get_string() in qa14_lib assert qap.get_string() in qa14_prm
def test_convert_oplsaa(self): qlib = QLib("oplsaa") qstruct = QStruct("data/ace_ash_nma.pdb", "pdb") qlib.read_ffld("data/ace_ash_nma.ffld11", qstruct) ql_str = qlib.get_string() assert ql_str == open("data/ace_ash_nma.lib").read()
parser.print_help() sys.exit(1) args = parser.parse_args() for k, v in vars(args).iteritems(): if k in ['ffld_output', 'pdb'] and not os.path.lexists(v): print "FATAL! File '{}' doesn't exist.".format(v) sys.exit(1) # # create QLib, QPrm, QStruct and QTopology objects # qlib = QLib("oplsaa", ignore_errors=args.ignore_errors) qprm = QPrm("oplsaa", ignore_errors=args.ignore_errors) try: qstruct = QStruct(args.pdb, "pdb", ignore_errors=args.ignore_errors) except QStructError as err: print "FATAL! Problem with pdb: {}".format(err) sys.exit(1) try: qprm.read_ffld(args.ffld_output, qstruct) except QPrmError as err: print "FATAL! Problem with ffld file: {}".format(err) sys.exit(1)
help="show this " "help message and exit") if len(sys.argv) == 1: parser.print_help() sys.exit(1) args = parser.parse_args() if not os.path.lexists(args.lib_file): print "FATAL! File %s doesn't exist." % args.lib_file sys.exit(1) # load the library try: qlib = QLib("oplsaa", ignore_errors=args.ignore_errors) qlib.read_lib(args.lib_file) except QLibError as e: print "FATAL! Problem with library: {}".format(str(e)) sys.exit(1) if len(qlib.residue_dict) > 1: print "FATAL! Please supply a library with just one residue entry." sys.exit(1) residue_lib = qlib.residue_dict.values()[0] if not residue_lib.charge_groups: print "No charge groups found, using all atoms in residue '{}'"\ "".format(residue_lib.name) ch_groups = [[a.name for a in residue_lib.atoms]]