diff --git a/loop/src/backbone_loop_score.cc b/loop/src/backbone_loop_score.cc index 5400261e05e1aeb39d4ae7eb09cf468a96e33021..8c43233738d1b954ffadc3382b2c579346329ee4 100644 --- a/loop/src/backbone_loop_score.cc +++ b/loop/src/backbone_loop_score.cc @@ -1492,9 +1492,9 @@ void BackboneLoopScorer::SetEnvironment(const ost::mol::EntityHandle& env){ ost::mol::ChainHandleList chain_list = env.GetChainList(); ost::mol::ResidueHandleList res_list; - if(chain_list.size() > num_chains_){ + if(chain_list.size() < num_chains_){ std::stringstream ss; - ss << "More chains than seqres sequences when setting the loop scorers environment! "; + ss << "Less chains than seqres sequences when setting the loop scorers environment! "; ss << "Don't do anything..."; throw promod3::Error(ss.str()); } @@ -1548,7 +1548,7 @@ void BackboneLoopScorer::SetEnvironment(const ost::mol::EntityHandle& env){ geom::Vec3 n_pos, ca_pos, c_pos, o_pos, cb_pos, old_pos; uint num, index; - for(uint i = 0; i < chain_list.size(); ++i){ + for(uint i = 0; i < num_chains_; ++i){ res_list = chain_list[i].GetResidueList(); for(ost::mol::ResidueHandleList::iterator j = res_list.begin(),e = res_list.end(); diff --git a/modelling/doc/index.rst b/modelling/doc/index.rst index 3ea3dff228a3cc3f8dbc53492d4865115dc4aca7..b5415e7707b9a0d06a7979db7d37f60d8358af6d 100644 --- a/modelling/doc/index.rst +++ b/modelling/doc/index.rst @@ -84,7 +84,13 @@ Modelling Pipeline list of alignment handles for raw model with multiple chains. :type aln: :class:`~ost.seq.AlignmentHandle` / :class:`~ost.seq.AlignmentList` - :param include_ligands: True, if we wish to include ligands in the model. + :param include_ligands: True, if we wish to include ligands in the model. This + searches for ligands in all OST handles of the views + attached to the alignments. Ligands are identified + with the "ligand" property in the handle (set by OST + based on HET records) or by the chain name '_' (as set + in SMTL). All ligands are added to a new chain named + '_'. :type include_ligands: :class:`bool` :param chain_names: Chains are named by a single chanacter taken from this. diff --git a/modelling/pymod/_pipeline.py b/modelling/pymod/_pipeline.py index a09efd0241c10e109bf25b1dca37cfa49db6403d..9790e2ab8f1184ce4321cd2378c25281efb38a41 100644 --- a/modelling/pymod/_pipeline.py +++ b/modelling/pymod/_pipeline.py @@ -15,6 +15,56 @@ from ost import mol from ost.mol import mm import os +############################################################################### +# helper functions +def _RemoveHydrogens(ent): + '''Hydrogen naming depends on the force field used. + To be on the safe side, we simply remove all of them. + Note that the pipeline ignores hydrogens when building the raw model and + when rebuilding sidechains. + ''' + edi = ent.EditXCS() + ha = ent.Select('ele=H') + for a in ha.atoms: + edi.DeleteAtom(a.handle) + +def _SetupMmSimulation(mhandle): + '''Get mm simulation object for the model.''' + # get general settings + settings = mm.Settings() + settings.integrator = mm.LangevinIntegrator(310, 1, 0.002) + settings.init_temperature = 0 + settings.nonbonded_method = mm.NonbondedMethod.CutoffNonPeriodic + settings.keep_ff_specific_naming = False + # use fast CPU platform by default + settings.platform = mm.Platform.CPU + if os.getenv('PM3_OPENMM_CPU_THREADS') is None: + settings.cpu_properties["CpuThreads"] = "1" + else: + settings.cpu_properties["CpuThreads"] = os.getenv('PM3_OPENMM_CPU_THREADS') + # prepare protein + model = mol.CreateEntityFromView(mhandle.model.Select("cname!='_'"), True) + _RemoveHydrogens(model) + # NOTE: both FF work, performance is similar, CHARMM slightly better + settings.forcefield = mm.LoadCHARMMForcefield() + #settings.forcefield = mm.LoadAMBERForcefield() + + # TODO: add ligands + # TODO: check when platform problem appears when having multiple topologies! + + # finally set up the simulation + try: + sim = mm.Simulation(model, settings) + except RuntimeError as ex: + ost.LogWarning("OpenMM failed to initialize with error: %s" % str(ex)) + # switch to "mm.Platform.Reference" as fallback + settings.platform = mm.Platform.Reference + sim = mm.Simulation(model, settings) + ost.LogWarning("Switched to slower reference platform of OpenMM!") + return sim + +############################################################################### + def BuildSidechains(mhandle, merge_distance=4, scorer=None, fragment_db=None, structure_db=None, torsion_sampler=None): '''Build sidechains for model. @@ -121,26 +171,8 @@ def MinimizeModelEnergy(mhandle, max_iterations=12, max_iter_sd=20, # ignore LogInfo in stereochemical problems if output up to info done ignore_stereo_log = (ost.GetVerbosityLevel() == 3) # setup mm simulation - settings = mm.Settings() - settings.integrator = mm.LangevinIntegrator(310, 1, 0.002) - settings.init_temperature = 0 - settings.forcefield = mm.LoadCHARMMForcefield() - settings.nonbonded_method = mm.NonbondedMethod.CutoffNonPeriodic - settings.keep_ff_specific_naming = False - # use fast CPU platform by default - settings.platform = mm.Platform.CPU - if os.getenv('PM3_OPENMM_CPU_THREADS') is None: - settings.cpu_properties["CpuThreads"] = "1" - else: - settings.cpu_properties["CpuThreads"] = os.getenv('PM3_OPENMM_CPU_THREADS') - try: - sim = mm.Simulation(mhandle.model, settings) - except RuntimeError as ex: - ost.LogWarning("OpenMM failed to initialize with error: %s" % str(ex)) - # switch to "mm.Platform.Reference" as fallback - settings.platform = mm.Platform.Reference - sim = mm.Simulation(mhandle.model, settings) - ost.LogWarning("Switched to slower reference platform of OpenMM!") + sim = _SetupMmSimulation(mhandle) + # settings to check for stereochemical problems clashing_distances = mol.alg.DefaultClashingDistances() bond_stereo_chemical_param = mol.alg.DefaultBondStereoChemicalParams() @@ -148,7 +180,8 @@ def MinimizeModelEnergy(mhandle, max_iterations=12, max_iter_sd=20, for i in range(max_iterations): # update atoms - ost.LogInfo("Perform energy minimization (iteration %d)" % (i+1)) + ost.LogInfo("Perform energy minimization " + "(iteration %d, energy: %g)" % (i+1, sim.GetEnergy())) sim.ApplySD(tolerance = 1.0, max_iterations = max_iter_sd) sim.ApplyLBFGS(tolerance = 1.0, max_iterations = max_iter_lbfgs) sim.UpdatePositions() @@ -171,10 +204,9 @@ def MinimizeModelEnergy(mhandle, max_iterations=12, max_iter_sd=20, # checks above would remove bad atoms if len(temp_ent_stereo_checked.Select("ele!=H").atoms) \ == len(temp_ent.Select("ele!=H").atoms): + ost.LogInfo("No more stereo-chemical problems " + "-> final energy: %g" % (sim.GetEnergy())) break - else: - ost.LogInfo("Stereo-chemical problems found, need more energy" - + " minimization (iteration %d)" % (i+1)) # update model simulation_ent = sim.GetEntity() diff --git a/modelling/src/model.cc b/modelling/src/model.cc index a90685cc3823b5a88a3751abe5e11932a86c783d..d53e4d2a0f5464291009e7464ed7d28257f748e5 100644 --- a/modelling/src/model.cc +++ b/modelling/src/model.cc @@ -431,17 +431,21 @@ ModellingHandle BuildRawModel(const ost::seq::AlignmentList& aln, void AddLigands(const ost::seq::AlignmentList& aln, XCSEditor& edi, EntityHandle& model) { - + // make sure we handle duplicates + std::set<mol::EntityHandle> unique_handles; std::vector<mol::ResidueViewList> ligand_views; for(ost::seq::AlignmentList::const_iterator it=aln.begin(); it!=aln.end(); ++it) { mol::EntityHandle handle = it->GetSequence(1).GetAttachedView().GetHandle(); - mol::EntityView ligand_view = handle.Select("ligand=true"); + // did we already do that? + if (unique_handles.find(handle) != unique_handles.end()) continue; + mol::EntityView ligand_view = handle.Select("ligand=true or cname='_'"); mol::ResidueViewList ligands = ligand_view.GetResidueList(); if(!ligands.empty()) { ligand_views.push_back(ligands); } + unique_handles.insert(handle); } if (ligand_views.size()>0) { diff --git a/sidechain/pymod/_reconstruct_sidechains.py b/sidechain/pymod/_reconstruct_sidechains.py index 9b147775d790ac06447fd60a5dd6bc183feb86fb..c385aac2f632bb716d769501f334b0338494067f 100644 --- a/sidechain/pymod/_reconstruct_sidechains.py +++ b/sidechain/pymod/_reconstruct_sidechains.py @@ -23,7 +23,8 @@ def Reconstruct(ent, keep_sidechains = False, build_disulfids = True, bbdep = True residues_with_rotamer_group = list() rotamer_groups = list() - prot = ent.Select("peptide=true") + # ignore ligand chain and any non-peptides + prot = ent.Select("peptide=true and cname!='_'") incomplete_sidechains = list() #extract rotamer ids