diff --git a/modelling/doc/gap_handling.rst b/modelling/doc/gap_handling.rst
index b2dd897ccaaa61ff1fae0f7846bf1e1c07a05335..e939f9f60fa779b43bfdb196e0649ebd28306a08 100644
--- a/modelling/doc/gap_handling.rst
+++ b/modelling/doc/gap_handling.rst
@@ -278,6 +278,26 @@ Gap Handling Functions
:raises: A :exc:`RuntimeError` if any gap in mhandle.gaps is only partially
enclosed by given gap.
+.. function:: InsertLoopClearGaps(mhandle, bb_list, gap)
+
+ Insert loop into model, update scoring environments and remove all gaps from
+ *mhandle* which are fully enclosed by given *gap* (see :meth:`InsertLoop` and
+ :meth:`ClearGaps`).
+
+ :param mhandle: Modelling handle on which to apply change.
+ :type mhandle: :class:`ModellingHandle`
+ :param bb_list: Loop to insert (backbone only).
+ :type bb_list: :class:`~promod3.loop.BackboneList`
+ :param gap: Gap defining range of loop to insert (must be consistent!).
+ :type gap: :class:`StructuralGap`
+
+ :return: Index of next gap in mhandle.gaps after removal.
+ Returns -1 if last gap was removed or no gaps in *mhandle*.
+ :rtype: :class:`int`
+
+ :raises: A :exc:`RuntimeError` if *bb_list* and *gap* are inconsistent or
+ if any gap in mhandle.gaps is only partially enclosed by *gap*.
+
.. function:: MergeGaps(mhandle, index)
Merges two gaps `mhandle.gaps[index]` and `mhandle.gaps[index+1]`.
diff --git a/modelling/doc/pipeline.rst b/modelling/doc/pipeline.rst
index 2db13068e8ab3637707d0d8b85ce74694cd8af04..68839c6707fd955c7ac1d022caa1fb22329d0a68 100644
--- a/modelling/doc/pipeline.rst
+++ b/modelling/doc/pipeline.rst
@@ -273,6 +273,22 @@ Modelling Steps
:param mhandle: Modelling handle to check.
:type mhandle: :class:`ModellingHandle`
+.. function:: InsertLoop(mhandle, bb_list, start_resnum, chain_idx)
+
+ Insert loop into model and ensure consistent updating of scoring environments.
+ Note that we do not update :attr:`~ModellingHandle.all_atom_scorer_env` as
+ that one is meant to be updated only while scoring. To clear a gap while
+ inserting a loop, use the simpler :meth:`InsertLoopClearGaps`.
+
+ :param mhandle: Modelling handle on which to apply change.
+ :type mhandle: :class:`ModellingHandle`
+ :param bb_list: Loop to insert (backbone only).
+ :type bb_list: :class:`~promod3.loop.BackboneList`
+ :param start_resnum: Res. number defining the start position in the SEQRES.
+ :type start_resnum: :class:`int`
+ :param chain_idx: Index of chain the loop belongs to.
+ :type chain_idx: :class:`int`
+
.. function:: RemoveTerminalGaps(mhandle)
Removes terminal gaps without modelling them (just removes them from the list
diff --git a/modelling/pymod/_closegaps.py b/modelling/pymod/_closegaps.py
index 0ed500e5689e09124f1245478b81630e49db8c1a..279c13b3d5b930668892904f1fb9dfb2b3863171 100644
--- a/modelling/pymod/_closegaps.py
+++ b/modelling/pymod/_closegaps.py
@@ -289,18 +289,11 @@ def _CloseLoopFrame(mhandle, gap_orig, actual_candidates, actual_extended_gaps,
_ResolveLogInfo(gap_orig, actual_extended_gaps[idx_a],
len(final_loop_candidates), bool(actual_db_scores),
max_num_all_atom > 0)
- # update model
- start_resnum = actual_extended_gaps[idx_a].before.GetNumber()
+ # update model and clear gaps
bb_list = actual_candidates[idx_a][idx_b]
- bb_list.InsertInto(mhandle.model.chains[actual_chain_idx], start_resnum)
- # update score env.
- mhandle.backbone_scorer_env.SetEnvironment(bb_list, start_resnum,
- actual_chain_idx)
- if IsAllAtomScoringSetUp(mhandle):
- mhandle.all_atom_sidechain_env.SetEnvironment(bb_list, start_resnum,
- actual_chain_idx)
- # will return -1 if last gap removed
- return ClearGaps(mhandle, actual_extended_gaps[idx_a])
+ actual_gap = actual_extended_gaps[idx_a]
+ # will return -1 if last gap removed, else next gap idx
+ return InsertLoopClearGaps(mhandle, bb_list, actual_gap)
else:
ost.LogInfo("Failed at loop insertion (%s)" % str(gap_orig))
return -2
@@ -349,20 +342,9 @@ def _CloseLoopBare(mhandle, gap_orig, actual_candidates, actual_extended_gaps,
# report
_ResolveLogInfo(gap_orig, optimal_gap, n_candidates,
bool(actual_db_scores), max_num_all_atom > 0)
- # update model
- start_resnum = optimal_gap.before.GetNumber()
- optimal_candidate.InsertInto(mhandle.model.chains[actual_chain_idx],
- start_resnum)
- # update score env.
- mhandle.backbone_scorer_env.SetEnvironment(optimal_candidate,
- start_resnum,
- actual_chain_idx)
- if IsAllAtomScoringSetUp(mhandle):
- mhandle.all_atom_sidechain_env.SetEnvironment(optimal_candidate,
- start_resnum,
- actual_chain_idx)
- # will return -1 if last gap removed
- return ClearGaps(mhandle, optimal_gap)
+ # update model and clear gaps
+ # will return -1 if last gap removed, else next gap idx
+ return InsertLoopClearGaps(mhandle, optimal_candidate, optimal_gap)
else:
ost.LogInfo("Failed at loop insertion (%s)" % str(gap_orig))
return -2
@@ -573,11 +555,7 @@ def CloseSmallDeletions(mhandle, max_extension=9, clash_thresh=1.0,
bb_list.TransOmegaTorsions():
ost.LogInfo("Closed: %s by relaxing %s" % \
(mhandle.gaps[current_gap_index], current_gap))
- chain = current_gap.before.GetChain()
- bb_list.InsertInto(chain, n_stem_resnum)
- mhandle.backbone_scorer_env.SetEnvironment(\
- bb_list, n_stem_resnum, current_chain_index)
- ClearGaps(mhandle, current_gap)
+ InsertLoopClearGaps(mhandle, bb_list, current_gap)
success = True
break
@@ -1365,15 +1343,11 @@ def ModelTermini(mhandle, torsion_sampler, fragger_handles=None,
actual_chain_idx)
min_score = min(scores)
min_idx = scores.index(min_score)
- # update model
- bb_list = candidates[min_idx]
- bb_list.InsertInto(actual_chain, start_resnum)
- # update score env.
- mhandle.backbone_scorer_env.SetEnvironment(bb_list, start_resnum,
- actual_chain_idx)
+ # report
ost.LogInfo("Resolved terminal gap %s (%d candidates)" % \
(str(actual_gap), len(candidates)))
- ClearGaps(mhandle, actual_gap)
+ # update model and clear gap
+ InsertLoopClearGaps(mhandle, candidates[min_idx], actual_gap)
else:
ost.LogInfo("Failed to model terminal gap (%s)" % str(actual_gap))
@@ -1578,7 +1552,7 @@ def CloseLargeDeletions(mhandle, structure_db, linker_length=8,
# replace fragment part
fragment.InsertInto(actual_chain, n_stem_res_num)
- # update score env.
+ # update score env. (manual to keep all atom sidechains)
mhandle.backbone_scorer_env.SetEnvironment(bb_list, first_num,
actual_chain_idx)
# will return -1 if last gap removed
@@ -1586,7 +1560,10 @@ def CloseLargeDeletions(mhandle, structure_db, linker_length=8,
ost.LogInfo("Resolved %s by sampling %s as linker" % \
(str(gap_orig), str(actual_gap)))
-
+
+ # reset all atom env. if it's set (changes are too drastic so we set all)
+ if IsAllAtomScoringSetUp(mhandle):
+ mhandle.all_atom_sidechain_env.SetInitialEnvironment(mhandle.model)
# these methods will be exported into module
__all__ = ('CloseSmallDeletions', 'MergeGapsByDistance', 'FillLoopsByDatabase',
diff --git a/modelling/pymod/export_model.cc b/modelling/pymod/export_model.cc
index 542934720aa121d053924c05e262e32fd70abd0c..a6851322639523545597fdb64782bed101865ff4 100644
--- a/modelling/pymod/export_model.cc
+++ b/modelling/pymod/export_model.cc
@@ -158,7 +158,7 @@ void export_model()
&WrapSetSidechainRec)
;
- def("ClearGaps", ClearGaps, (arg("mhandle"),arg("gap")));
+ def("ClearGaps", ClearGaps, (arg("mhandle"), arg("gap")));
def("CountEnclosedGaps", WrapCountEnclosedGaps, (arg("mhandle"),
arg("gap")));
def("CountEnclosedInsertions", WrapCountEnclosedIns, (arg("mhandle"),
@@ -184,6 +184,10 @@ void export_model()
arg("end_resnum"),
arg("transform"),
arg("reset_scoring_env") = true));
+ def("InsertLoop", InsertLoop,
+ (arg("mhandle"), arg("bb_list"), arg("start_resnum"), arg("chain_idx")));
+ def("InsertLoopClearGaps", InsertLoopClearGaps,
+ (arg("mhandle"), arg("bb_list"), arg("gap")));
def("BuildRawModel", BuildRawModelHandle,
(arg("aln"),
arg("include_ligands")=false,
diff --git a/modelling/src/model.cc b/modelling/src/model.cc
index 59371f1cfee9d87823b5d480407b4df37234190f..9e2b8b26abab6ad078186f4b3929efdcc47d5f08 100644
--- a/modelling/src/model.cc
+++ b/modelling/src/model.cc
@@ -627,6 +627,37 @@ void MergeMHandle(const ModellingHandle& source_mhandle,
}
}
+void InsertLoop(ModellingHandle& mhandle, const loop::BackboneList& bb_list,
+ uint start_resnum, uint chain_idx) {
+ // update model
+ ost::mol::ChainHandleList chain_list = mhandle.model.GetChainList();
+ bb_list.InsertInto(chain_list[chain_idx], start_resnum);
+ // update scoring
+ if (IsBackboneScoringSetUp(mhandle)) {
+ mhandle.backbone_scorer_env->SetEnvironment(bb_list, start_resnum,
+ chain_idx);
+ }
+ if (IsAllAtomScoringSetUp(mhandle)) {
+ mhandle.all_atom_sidechain_env->SetEnvironment(bb_list, start_resnum,
+ chain_idx);
+ }
+}
+
+int InsertLoopClearGaps(ModellingHandle& mhandle,
+ const loop::BackboneList& bb_list,
+ const StructuralGap& gap) {
+ // check consistency
+ if (bb_list.size() != gap.GetFullSeq().size()) {
+ throw promod3::Error("Inconsistent gap for given loop to insert.");
+ }
+ // update model
+ uint chain_idx = gap.GetChainIndex();
+ uint start_resnum = 1;
+ if (!gap.IsNTerminal()) start_resnum = gap.before.GetNumber().GetNum();
+ InsertLoop(mhandle, bb_list, start_resnum, chain_idx);
+ return ClearGaps(mhandle, gap);
+}
+
bool CopyConserved(ResidueView src_res, ResidueHandle dst_res, XCSEditor& edi,
bool& has_cbeta)
{
diff --git a/modelling/src/model.hh b/modelling/src/model.hh
index 8ef7ac4044c857464bb300ffbf326ac5b5ab760a..9637b1763415802139f6c215290145c7bb786cff 100644
--- a/modelling/src/model.hh
+++ b/modelling/src/model.hh
@@ -90,7 +90,7 @@ void SetPsipredPredictions(ModellingHandle& mhandle,
const promod3::loop::PsipredPredictionList&
psipred_predictions);
-//see Python doc
+// see Python doc
void MergeMHandle(const ModellingHandle& source_mhandle,
ModellingHandle& target_mhandle,
uint source_chain_idx, uint target_chain_idx,
@@ -98,6 +98,12 @@ void MergeMHandle(const ModellingHandle& source_mhandle,
const geom::Mat4& transform,
bool reset_scoring_env = true);
+// see Python doc
+void InsertLoop(ModellingHandle& mhandle, const loop::BackboneList& bb_list,
+ uint start_resnum, uint chain_idx);
+int InsertLoopClearGaps(ModellingHandle& mhandle,
+ const loop::BackboneList& bb_list,
+ const StructuralGap& gap);
/// \brief copies all atom of src_res to dst_res
/// \param has_cbeta will be set to true if the src_res has a cbeta and the