def test_restraint_probability_beads(self): """Test restraint works for all-bead systems""" m = IMP.Model() rbeads, dof = self.init_representation_beads_pmi2(m) xlbeads, cldb = self.setup_crosslinks_beads(root_hier=rbeads, mode="single_category") self.assertEqual(len(dof.get_movers()), 60) dof.get_nuisances_from_restraint(xlbeads) self.assertEqual(len(dof.get_movers()), 62) for xl in xlbeads.xl_list: chain1 = xl[cldb.protein1_key] chain2 = xl[cldb.protein2_key] res1 = xl[cldb.residue1_key] res2 = xl[cldb.residue2_key] ids = xl[cldb.unique_id_key] # randomize coordinates and check that the probability is OK for j in range(100): IMP.pmi.tools.shuffle_configuration(rbeads, max_translation=10) cross_link_dict = {} for xl in xlbeads.xl_list: p0 = xl["Particle1"] p1 = xl["Particle2"] prob = xl["Restraint"].get_probability() resid1 = xl[cldb.residue1_key] chain1 = xl[cldb.protein1_key] resid2 = xl[cldb.residue2_key] chain2 = xl[cldb.protein2_key] xlid = xl[cldb.unique_id_key] d0 = IMP.core.XYZ(p0) d1 = IMP.core.XYZ(p1) sig1 = xl["Particle_sigma1"] sig2 = xl["Particle_sigma2"] psi = xl["Particle_psi"] if xlid not in cross_link_dict: cross_link_dict[xlid] = ([d0], [d1], [sig1], [sig2], [psi], prob) else: cross_link_dict[xlid][0].append(d0) cross_link_dict[xlid][1].append(d1) cross_link_dict[xlid][2].append(sig1) cross_link_dict[xlid][3].append(sig2) cross_link_dict[xlid][4].append(psi) for xlid in cross_link_dict: test_prob = get_probability(cross_link_dict[xlid][0], cross_link_dict[xlid][1], cross_link_dict[xlid][2], cross_link_dict[xlid][3], cross_link_dict[xlid][4], 21.0, 0.01) prob = cross_link_dict[xlid][5] self.assertAlmostEqual(test_prob, prob, delta=0.0001) rex = IMP.pmi.macros.ReplicaExchange0( m, root_hier=rbeads, monte_carlo_sample_objects=dof.get_movers(), number_of_frames=2, test_mode=True, replica_exchange_object=rem) rex.execute_macro() for output in [ 'excluded.None.xl.db', 'included.None.xl.db', 'missing.None.xl.db' ]: os.unlink(output)
kw.set_id_score_key("score") xldb = IMP.pmi.io.crosslink.CrossLinkDataBase(kw) xldb.create_set_from_file(tf.name) os.remove(tf.name) # 3) Add the restraint xlr = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( root_hier=root_hier, database=xldb, length=21, label="XL", resolution=1, slope=0.01) xlr.add_to_model() output_objects.append(xlr) dof.get_nuisances_from_restraint( xlr) # needed to sample the nuisance particles (noise params) ### Connectivity keeps things connected along the backbone crs = [] for mol in (m1A, m1B, m1C, m2A, m2C): cr = IMP.pmi.restraints.stereochemistry.ConnectivityRestraint(mol) cr.add_to_model() output_objects.append(cr) ### Excluded volume - one for each state (they don't interact) evr1 = IMP.pmi.restraints.stereochemistry.ExcludedVolumeSphere( included_objects=(m1A, m1B, m1C)) evr1.add_to_model() output_objects.append(evr1) evr2 = IMP.pmi.restraints.stereochemistry.ExcludedVolumeSphere( included_objects=(m2A, m2C))
def test_restraint_probability_complex(self): """Test restraint gets correct probabilities""" m = IMP.Model() rcomplex, dof = self.init_representation_complex_pmi2(m) xlc, cldb = self.setup_crosslinks_complex(root_hier=rcomplex, mode="single_category") self.assertEqual(len(dof.get_movers()), 42) dof.get_nuisances_from_restraint(xlc) self.assertEqual(len(dof.get_movers()), 44) # check all internals didn't change since last time o = IMP.pmi.output.Output() o.write_test("expensive_test_new_cross_link_ms_restraint.dat", [xlc]) passed = o.test( self.get_input_file_name( "expensive_test_new_cross_link_ms_restraint.dat"), [xlc]) self.assertEqual(passed, True) rs = xlc.get_restraint() # check the probability of cross-links restraints = [] for xl in xlc.xl_list: p0 = xl["Particle1"] p1 = xl["Particle2"] prob = xl["Restraint"].get_probability() resid1 = xl[cldb.residue1_key] chain1 = xl[cldb.protein1_key] resid2 = xl[cldb.residue2_key] chain2 = xl[cldb.protein2_key] d0 = IMP.core.XYZ(p0) d1 = IMP.core.XYZ(p1) sig1 = xl["Particle_sigma1"] sig2 = xl["Particle_sigma2"] psi = xl["Particle_psi"] d0 = IMP.core.XYZ(p0) d1 = IMP.core.XYZ(p1) dist = IMP.core.get_distance(d0, d1) test_prob = get_probability([d0], [d1], [sig1], [sig2], [psi], 21.0, 0.0) restraints.append(xl["Restraint"]) # check that the probability is the same for # each cross-link self.assertAlmostEqual(prob, test_prob, delta=0.00001) # check the log_wrapper log_wrapper_score = rs.unprotected_evaluate(None) test_log_wrapper_score = log_evaluate(restraints) self.assertAlmostEqual(log_wrapper_score, test_log_wrapper_score, delta=0.00001) rex = IMP.pmi.macros.ReplicaExchange0( m, root_hier=rcomplex, monte_carlo_sample_objects=dof.get_movers(), number_of_frames=2, test_mode=True, replica_exchange_object=rem) rex.execute_macro() for output in [ 'excluded.None.xl.db', 'expensive_test_new_cross_link_ms_restraint.dat', 'included.None.xl.db', 'missing.None.xl.db' ]: os.unlink(output)
DSS1 = IMP.pmi.io.crosslink.CrossLinkDataBase(kw) DSS1.create_set_from_file(datadirectory + 'DSS.Inter.csv') x_dss1 = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( root_hier=representation, CrossLinkDataBase=DSS1, length=21, label="DSS_Inter", resolution=1.0, slope=0.02) x_dss1.rs.set_weight(5.0) x_dss1.add_to_model() sampleobjects.append(x_dss1) outputobjects.append(x_dss1) dof.get_nuisances_from_restraint(x_dss1) sf = IMP.core.RestraintsScoringFunction(IMP.pmi.tools.get_restraint_set(m)) print "ilan3", sf.evaluate(False) ###Medium + High Confidence Intramolecular crosslinks DSS2 = IMP.pmi.io.crosslink.CrossLinkDataBase(kw) DSS2.create_set_from_file(datadirectory + 'DSS.Intra.csv') x_dss2 = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( root_hier=representation, CrossLinkDataBase=DSS2, length=21, label="DSS_Intra", resolution=1.0, slope=0.02)
"../data/Interlinks_Nef_AP2_20190723_renamed_renumbered_nonambiguos.csv") xl1 = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( root_hier=hier, database=cldb, resolution=1.0, length=21.0, slope=0.02, linker=ihm.cross_linkers.dsso) xl1.add_to_model() xl1.set_weight(1.0) rmf_restraints.append(xl1) output_objects.append(xl1) dof.get_nuisances_from_restraint(xl1) ############################## # Occams Spring restaint ############################## if include_Occams: occ = IMP.pmi.restraints.occams.OccamsRestraint( hier, hier, 'equiv_assis_self.dat', '../data/alns', sample_sys_1=False, sigma_init=8.0, slope=0.0005, psi_nuisances_are_optimized=True, sigma_nuisances_are_optimized=True)
def test_restraint_probability_beads(self): """Test restraint works for all-bead systems""" for i in range(2): m = IMP.Model() if i==0: rbeads=self.init_representation_beads(m) xlbeads,cldb=self.setup_crosslinks_beads(rbeads,"single_category") else: rbeads,dof=self.init_representation_beads_pmi2(m) xlbeads,cldb=self.setup_crosslinks_beads(root_hier=rbeads,mode="single_category") self.assertEqual(len(dof.get_movers()),60) dof.get_nuisances_from_restraint(xlbeads) self.assertEqual(len(dof.get_movers()),62) for xl in xlbeads.xl_list: chain1 = xl[cldb.protein1_key] chain2 = xl[cldb.protein2_key] res1 = xl[cldb.residue1_key] res2 = xl[cldb.residue2_key] ids = xl[cldb.unique_id_key] # randomize coordinates and check that the probability is OK print("testing PMI version "+str(i+1)) for j in range(100): if i==0: rbeads.shuffle_configuration(max_translation=10) else: IMP.pmi.tools.shuffle_configuration(rbeads,max_translation=10) cross_link_dict={} for xl in xlbeads.xl_list: p0 = xl["Particle1"] p1 = xl["Particle2"] prob = xl["Restraint"].get_probability() resid1 = xl[cldb.residue1_key] chain1 = xl[cldb.protein1_key] resid2 = xl[cldb.residue2_key] chain2 = xl[cldb.protein2_key] xlid=xl[cldb.unique_id_key] d0 = IMP.core.XYZ(p0) d1 = IMP.core.XYZ(p1) sig1 = xl["Particle_sigma1"] sig2 = xl["Particle_sigma2"] psi = xl["Particle_psi"] if xlid not in cross_link_dict: cross_link_dict[xlid]=([d0],[d1],[sig1],[sig2],[psi],prob) else: cross_link_dict[xlid][0].append(d0) cross_link_dict[xlid][1].append(d1) cross_link_dict[xlid][2].append(sig1) cross_link_dict[xlid][3].append(sig2) cross_link_dict[xlid][4].append(psi) for xlid in cross_link_dict: test_prob=get_probability(cross_link_dict[xlid][0], cross_link_dict[xlid][1], cross_link_dict[xlid][2], cross_link_dict[xlid][3], cross_link_dict[xlid][4],21.0,0.01) prob=cross_link_dict[xlid][5] self.assertAlmostEqual(test_prob,prob, delta=0.0001) if i==0: rex0 = IMP.pmi.macros.ReplicaExchange0(m, rbeads, monte_carlo_sample_objects=[rbeads], number_of_frames=2, test_mode=True, replica_exchange_object = rem) rex0.execute_macro() else: rex = IMP.pmi.macros.ReplicaExchange0(m, root_hier=rbeads, monte_carlo_sample_objects=dof.get_movers(), number_of_frames=2, test_mode=True, replica_exchange_object = rem) rex.execute_macro() for output in ['excluded.None.xl.db', 'included.None.xl.db', 'missing.None.xl.db']: os.unlink(output)
xldb = IMP.pmi.io.crosslink.CrossLinkDataBase(kw) xldb.create_set_from_file(tf.name) os.remove(tf.name) # 3) Add the restraint xlr = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( root_hier=root_hier, CrossLinkDataBase=xldb, length=21, label="XL", resolution=1, slope=0.01) xlr.add_to_model() output_objects.append(xlr) rmf_restraints.append(xlr) dof.get_nuisances_from_restraint(xlr) # needed to sample the nuisance particles (noise params) ### Connectivity keeps things connected along the backbone crs = [] for mol in (m1A,m1B,m1C,m2A,m2C): cr = IMP.pmi.restraints.stereochemistry.ConnectivityRestraint(mol) cr.add_to_model() output_objects.append(cr) rmf_restraints.append(cr) ### Excluded volume - one for each state (they don't interact) evr1 = IMP.pmi.restraints.stereochemistry.ExcludedVolumeSphere(included_objects = (m1A,m1B,m1C)) evr1.add_to_model() output_objects.append(evr1) evr2 = IMP.pmi.restraints.stereochemistry.ExcludedVolumeSphere(included_objects = (m2A,m2C)) evr2.add_to_model()
xldb = IMP.pmi.io.crosslink.CrossLinkDataBase(kw) xldb.create_set_from_file(tf.name) os.remove(tf.name) # 3) Add the restraint xlr = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( root_hier=root_hier, database=xldb, length=21, label="XL", resolution=1, slope=0.01) xlr.add_to_model() output_objects.append(xlr) # needed to sample the nuisance particles (noise params) dof.get_nuisances_from_restraint(xlr) # Connectivity keeps things connected along the backbone crs = [] for mol in (m1A, m1B, m1C, m2A, m2C): cr = IMP.pmi.restraints.stereochemistry.ConnectivityRestraint(mol) cr.add_to_model() output_objects.append(cr) # Excluded volume - one for each state (they don't interact) evr1 = IMP.pmi.restraints.stereochemistry.ExcludedVolumeSphere( included_objects=(m1A, m1B, m1C)) evr1.add_to_model() output_objects.append(evr1) evr2 = IMP.pmi.restraints.stereochemistry.ExcludedVolumeSphere( included_objects=(m2A, m2C))
def test_restraint_probability_complex(self): """Test restraint gets correct probabilities""" for i in range(2): m = IMP.Model() print("Testing PMI version",i+1) if i==0: rcomplex = self.init_representation_complex(m) xlc,cldb = self.setup_crosslinks_complex(rcomplex,"single_category") else: rcomplex,dof=self.init_representation_complex_pmi2(m) xlc,cldb = self.setup_crosslinks_complex(root_hier=rcomplex, mode="single_category") self.assertEqual(len(dof.get_movers()),42) dof.get_nuisances_from_restraint(xlc) self.assertEqual(len(dof.get_movers()),44) # check all internals didn't change since last time o=IMP.pmi.output.Output() o.write_test("expensive_test_new_cross_link_ms_restraint.dat", [xlc]) passed=o.test(self.get_input_file_name("expensive_test_new_cross_link_ms_restraint.dat"), [xlc]) self.assertEqual(passed, True) rs = xlc.get_restraint() # check the probability of cross-links restraints=[] for xl in xlc.xl_list: p0 = xl["Particle1"] p1 = xl["Particle2"] prob = xl["Restraint"].get_probability() resid1 = xl[cldb.residue1_key] chain1 = xl[cldb.protein1_key] resid2 = xl[cldb.residue2_key] chain2 = xl[cldb.protein2_key] d0 = IMP.core.XYZ(p0) d1 = IMP.core.XYZ(p1) sig1 = xl["Particle_sigma1"] sig2 = xl["Particle_sigma2"] psi = xl["Particle_psi"] d0 = IMP.core.XYZ(p0) d1 = IMP.core.XYZ(p1) dist=IMP.core.get_distance(d0, d1) test_prob=get_probability([d0],[d1],[sig1],[sig2],[psi],21.0,0.0) restraints.append(xl["Restraint"]) # check that the probability is the same for # each cross-link self.assertAlmostEqual(prob, test_prob, delta=0.00001) # check the log_wrapper log_wrapper_score=rs.unprotected_evaluate(None) test_log_wrapper_score=log_evaluate(restraints) self.assertAlmostEqual(log_wrapper_score, test_log_wrapper_score, delta=0.00001) if i==0: rex0 = IMP.pmi.macros.ReplicaExchange0(m, rcomplex, monte_carlo_sample_objects=[rcomplex], number_of_frames=2, test_mode=True, replica_exchange_object = rem) rex0.execute_macro() else: rex = IMP.pmi.macros.ReplicaExchange0(m, root_hier=rcomplex, monte_carlo_sample_objects=dof.get_movers(), number_of_frames=2, test_mode=True, replica_exchange_object = rem) rex.execute_macro() for output in ['excluded.None.xl.db', 'expensive_test_new_cross_link_ms_restraint.dat', 'included.None.xl.db', 'missing.None.xl.db']: os.unlink(output)
x_Robinson = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( root_hier=representation, label="Robinson", database=Robinson, length=30, resolution=1.0, slope=0.02, weight=1.0, ) x_Robinson.add_to_model() sampleobjects.append(x_Robinson) outputobjects.append(x_Robinson) dof.get_nuisances_from_restraint(x_Robinson) # # XL-MS Dataset 2 # Schilbach = IMP.pmi.io.crosslink.CrossLinkDataBase(kw) # Schilbach.create_set_from_file(datadirectory + 'schilbach_mpic_xls.csv') # Schilbach.create_set_from_file(datadirectory + 'schilbach_tfiik_xls.csv') # x_Schilbach = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( # root_hier=representation, # label="Schilbach", # # database=Schilbach, # length=30, # resolution=1.0, #
def test_atomic_xl(self): """ test PMI setup of atomic XL restraint """ # fake data tname = self.get_tmp_file_name("test.txt") with open(tname, "w") as fh: fh.write("prot1,res1,prot2,res2\nProt1,7,Prot1,39\n") cldbkc=IMP.pmi.io.crosslink.CrossLinkDataBaseKeywordsConverter() cldbkc.set_protein1_key("prot1") cldbkc.set_protein2_key("prot2") cldbkc.set_residue1_key("res1") cldbkc.set_residue2_key("res2") cldb = IMP.pmi.io.crosslink.CrossLinkDataBase(cldbkc) cldb.create_set_from_file(tname) self.assertEqual(cldb.get_number_of_xlid(),1) # create two states, each with two copies of the protein mdl = IMP.Model() s = IMP.pmi.topology.System(mdl) seqs = IMP.pmi.topology.Sequences(self.get_input_file_name('multi_seq.fasta'), name_map={'Protein_1':'Prot1'}) # build state 1 st1 = s.create_state() m1 = st1.create_molecule("Prot1",sequence=seqs["Prot1"],chain_id='A') m1_res = m1.add_structure(self.get_input_file_name('multi.pdb'), chain_id='A',offset=-54, model_num=0) m1.add_representation(m1_res,resolutions=[0]) m1a = m1.create_copy(chain_id='G') m1a_res = m1a.add_structure(self.get_input_file_name('multi.pdb'),chain_id='G',offset=-54) m1a.add_representation(m1a_res,resolutions=[0]) # build state 2 st2 = s.create_state() m2 = st2.create_molecule("Prot1",sequence=seqs["Prot1"],chain_id='A') m2_res = m2.add_structure(self.get_input_file_name('multi.pdb'), chain_id='A',offset=-54, model_num=1) m2.add_representation(m2_res,resolutions=[0]) m2a = m2.create_copy(chain_id='G') m2a_res = m2a.add_structure(self.get_input_file_name('multi.pdb'),chain_id='G',offset=-54) m2a.add_representation(m2a_res,resolutions=[0]) hier = s.build() #IMP.atom.show_molecular_hierarchy(hier) xl = IMP.pmi.restraints.crosslinking.AtomicCrossLinkMSRestraint(hier, cldb, nstates=[0,1], atom_type="CA") # check that you created 8 restraints: # Each state: A1-B1, A1-B2, A2-B1, A2-B2 rs=xl.get_restraint_set() self.assertEqual(rs.get_number_of_restraints(),1) xlrs=IMP.isd.AtomicCrossLinkMSRestraint.get_from(rs.get_restraint(0)) self.assertIsInstance(xlrs,IMP.isd.AtomicCrossLinkMSRestraint) self.assertEqual(xlrs.get_number_of_contributions(),8) dof = IMP.pmi.dof.DegreesOfFreedom(mdl) dof.get_nuisances_from_restraint(xl) self.assertEqual(len(dof.get_movers()),2)
output_rs.append(rnoes) # add dihedral restraints rdihed = DihedralsRestraint(tuple_selection_quads, dihedrals, hier) rdihed.add_to_model() all_rs.append(rdihed) output_rs.append(rdihed) # add CHARMM restraints rcharmm = CharmmForceFieldRestraint(hier) rcharmm.add_to_model() all_rs.append(rcharmm) # set up simulation dof = IMP.pmi.dof.DegreesOfFreedom(m) dof.get_nuisances_from_restraint(rnoes) dof.get_nuisances_from_restraint(rdihed) # minimize via md md_ps = dof.setup_md(ubq) IMP.set_log_level(IMP.SILENT) # run replica exchange md with monte carlo rex = IMP.pmi.macros.ReplicaExchange0(m, root_hier=hier, crosslink_restraints=output_rs, output_objects=all_rs, number_of_frames=100000, monte_carlo_sample_objects=dof.get_movers(), monte_carlo_steps=10, molecular_dynamics_sample_objects=md_ps, molecular_dynamics_steps=10,
def test_atomic_xl(self): """ test PMI setup of atomic XL restraint """ # fake data tname = self.get_tmp_file_name("test.txt") with open(tname, "w") as fh: fh.write("prot1,res1,prot2,res2\nProt1,7,Prot1,39\n") cldbkc = IMP.pmi.io.crosslink.CrossLinkDataBaseKeywordsConverter() cldbkc.set_protein1_key("prot1") cldbkc.set_protein2_key("prot2") cldbkc.set_residue1_key("res1") cldbkc.set_residue2_key("res2") cldb = IMP.pmi.io.crosslink.CrossLinkDataBase(cldbkc) cldb.create_set_from_file(tname) self.assertEqual(cldb.get_number_of_xlid(), 1) # create two states, each with two copies of the protein mdl = IMP.Model() s = IMP.pmi.topology.System(mdl) seqs = IMP.pmi.topology.Sequences( self.get_input_file_name('multi_seq.fasta'), name_map={'Protein_1': 'Prot1'}) # build state 1 st1 = s.create_state() m1 = st1.create_molecule("Prot1", sequence=seqs["Prot1"], chain_id='A') m1_res = m1.add_structure(self.get_input_file_name('multi.pdb'), chain_id='A', offset=-54, model_num=0) m1.add_representation(m1_res, resolutions=[0]) m1a = m1.create_copy(chain_id='G') m1a_res = m1a.add_structure(self.get_input_file_name('multi.pdb'), chain_id='G', offset=-54) m1a.add_representation(m1a_res, resolutions=[0]) # build state 2 st2 = s.create_state() m2 = st2.create_molecule("Prot1", sequence=seqs["Prot1"], chain_id='A') m2_res = m2.add_structure(self.get_input_file_name('multi.pdb'), chain_id='A', offset=-54, model_num=1) m2.add_representation(m2_res, resolutions=[0]) m2a = m2.create_copy(chain_id='G') m2a_res = m2a.add_structure(self.get_input_file_name('multi.pdb'), chain_id='G', offset=-54) m2a.add_representation(m2a_res, resolutions=[0]) hier = s.build() #IMP.atom.show_molecular_hierarchy(hier) xl = IMP.pmi.restraints.crosslinking.AtomicCrossLinkMSRestraint( hier, cldb, nstates=[0, 1], atom_type="CA") # check that you created 8 restraints: # Each state: A1-B1, A1-B2, A2-B1, A2-B2 rs = xl.get_restraint_set() rs.set_was_used(True) self.assertEqual(rs.get_number_of_restraints(), 1) xlrs = IMP.isd.AtomicCrossLinkMSRestraint.get_from(rs.get_restraint(0)) self.assertIsInstance(xlrs, IMP.isd.AtomicCrossLinkMSRestraint) self.assertEqual(xlrs.get_number_of_contributions(), 8) dof = IMP.pmi.dof.DegreesOfFreedom(mdl) dof.get_nuisances_from_restraint(xl) self.assertEqual(len(dof.get_movers()), 2)
DHS1.create_set_from_file(datadirectory + 'DHS.Inter.csv') x_dhs1 = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( root_hier=representation, database=DHS1, linker=ihm.cross_linkers.dhso, length=21, label="DHS_Inter", resolution=1.0, slope=0.02) x_dhs1.rs.set_weight(7.5) x_dhs1.add_to_model() sampleobjects.append(x_dhs1) sampleobjects.append(x_dhs1) outputobjects.append(x_dhs1) dof.get_nuisances_from_restraint(x_dhs1) sf = IMP.core.RestraintsScoringFunction(IMP.pmi.tools.get_restraint_set(m)) print "ilan3", sf.evaluate(False) # Medium + High Confidence Intramolecular crosslinks DHS2 = IMP.pmi.io.crosslink.CrossLinkDataBase(kw) DHS2.create_set_from_file(datadirectory + 'DHS.Intra.csv') x_dhs2 = IMP.pmi.restraints.crosslinking.CrossLinkingMassSpectrometryRestraint( root_hier=representation, database=DHS2, linker=ihm.cross_linkers.dhso, length=21, label="DHS_Intra", resolution=1.0,