diff --git a/modules/mol/alg/pymod/scoring.py b/modules/mol/alg/pymod/scoring.py index 4444e463709703042b6395da3c866804cd971237..d415b0aad53c7f9327d2f7c47191a8e4d28e1940 100644 --- a/modules/mol/alg/pymod/scoring.py +++ b/modules/mol/alg/pymod/scoring.py @@ -381,6 +381,7 @@ class Scorer: self._rigid_n_target_not_mapped = None self._rigid_transform = None + self._gdt_window_sizes = [5, 7, 9, 12, 24, 36, 48] self._gdt_05 = None self._gdt_1 = None self._gdt_2 = None @@ -1399,7 +1400,13 @@ class Scorer: :type: :class:`float` """ if self._gdt_05 is None: - n, m = GDT(self.rigid_mapped_model_pos, self.rigid_mapped_target_pos, 7, 1000, 0.5) + N = list() + for window_size in self._gdt_window_sizes: + n = GDT(self.rigid_mapped_model_pos, + self.rigid_mapped_target_pos, + window_size, 1000, 0.5)[0] + N.append(n) + n = max(N) n_full = len(self.rigid_mapped_target_pos) + self.rigid_n_target_not_mapped if n_full > 0: self._gdt_05 = float(n) / n_full @@ -1417,7 +1424,13 @@ class Scorer: :type: :class:`float` """ if self._gdt_1 is None: - n, m = GDT(self.rigid_mapped_model_pos, self.rigid_mapped_target_pos, 7, 1000, 1.0) + N = list() + for window_size in self._gdt_window_sizes: + n = GDT(self.rigid_mapped_model_pos, + self.rigid_mapped_target_pos, + window_size, 1000, 1.0)[0] + N.append(n) + n = max(N) n_full = len(self.rigid_mapped_target_pos) + self.rigid_n_target_not_mapped if n_full > 0: self._gdt_1 = float(n) / n_full @@ -1436,7 +1449,13 @@ class Scorer: :type: :class:`float` """ if self._gdt_2 is None: - n, m = GDT(self.rigid_mapped_model_pos, self.rigid_mapped_target_pos, 7, 1000, 2.0) + N = list() + for window_size in self._gdt_window_sizes: + n = GDT(self.rigid_mapped_model_pos, + self.rigid_mapped_target_pos, + window_size, 1000, 2.0)[0] + N.append(n) + n = max(N) n_full = len(self.rigid_mapped_target_pos) + self.rigid_n_target_not_mapped if n_full > 0: self._gdt_2 = float(n) / n_full @@ -1454,7 +1473,13 @@ class Scorer: :type: :class:`float` """ if self._gdt_4 is None: - n, m = GDT(self.rigid_mapped_model_pos, self.rigid_mapped_target_pos, 7, 1000, 4.0) + N = list() + for window_size in self._gdt_window_sizes: + n = GDT(self.rigid_mapped_model_pos, + self.rigid_mapped_target_pos, + window_size, 1000, 4.0)[0] + N.append(n) + n = max(N) n_full = len(self.rigid_mapped_target_pos) + self.rigid_n_target_not_mapped if n_full > 0: self._gdt_4 = float(n) / n_full @@ -1471,7 +1496,13 @@ class Scorer: :type: :class:`float` """ if self._gdt_8 is None: - n, m = GDT(self.rigid_mapped_model_pos, self.rigid_mapped_target_pos, 7, 1000, 8.0) + N = list() + for window_size in self._gdt_window_sizes: + n = GDT(self.rigid_mapped_model_pos, + self.rigid_mapped_target_pos, + window_size, 1000, 8.0)[0] + N.append(n) + n = max(N) n_full = len(self.rigid_mapped_target_pos) + self.rigid_n_target_not_mapped if n_full > 0: self._gdt_8 = float(n) / n_full diff --git a/modules/mol/alg/src/gdt.cc b/modules/mol/alg/src/gdt.cc index 93b6e8b173b51b04fb4c0e01c092f661b7507d04..8eedd7796241085c89ec8ed15d8f28ca168afd03 100644 --- a/modules/mol/alg/src/gdt.cc +++ b/modules/mol/alg/src/gdt.cc @@ -302,8 +302,126 @@ void GDT(const geom::Vec3List& mdl_pos, const geom::Vec3List& ref_pos, int n_pos = mdl_pos.size(); + // deal with special cases that don't produce valid transforms + if(n_pos == 1) { + transform = geom::Mat4::Identity(); + transform.PasteTranslation(ref_pos[0] - mdl_pos[0]); + n_superposed = 1; + return; + } + + if(n_pos == 2) { + Real mdl_d = geom::Distance(mdl_pos[0], mdl_pos[1]); + Real ref_d = geom::Distance(ref_pos[0], ref_pos[1]); + Real dd = std::abs(mdl_d - ref_d); + if(dd/2 <= distance_thresh) { + // the two can be superposed within specified distance threshold + // BUT: cannot construct valid transformation from two positions + // => Construct matrix with four positions + // Two are constructed starting from the center point +- some direction + // vector that is orthogonal to the vector connecting the original two + // points. + geom::Vec3 mdl_center = (mdl_pos[0] + mdl_pos[1])*0.5; + geom::Vec3 ref_center = (ref_pos[0] + ref_pos[1])*0.5; + Eigen::Matrix<double, 4, 3> eigen_mdl_pos = \ + Eigen::Matrix<double, 4, 3>::Zero(4, 3); + Eigen::Matrix<double, 4, 3> eigen_ref_pos = \ + Eigen::Matrix<double, 4, 3>::Zero(4, 3); + Eigen::Matrix<double,3,3> eigen_rot = \ + Eigen::Matrix<double,3,3>::Identity(); + + geom::Vec3 mdl_dir = geom::Normalize(mdl_pos[1] - mdl_pos[0]); + geom::Vec3 ref_dir = geom::Normalize(ref_pos[1] - ref_pos[0]); + geom::Vec3 mdl_normal; + geom::Vec3 ref_normal; + + // Use cross product to get some normal on mdl_dir + // The direction of the second vector doesn't really matter, but shouldnt + // be collinear with mdl_dir + if(mdl_dir[0] < 0.999) { + mdl_normal = geom::Cross(geom::Vec3(1,0,0), mdl_dir); + } else { + mdl_normal = geom::Cross(geom::Vec3(0,1,0), mdl_dir); + } + + // same for ref_dir + if(ref_dir[0] < 0.999) { + ref_normal = geom::Cross(geom::Vec3(1,0,0), ref_dir); + } else { + ref_normal = geom::Cross(geom::Vec3(0,1,0), ref_dir); + } + + eigen_mdl_pos(0, 0) = mdl_pos[0][0] - mdl_center[0]; + eigen_mdl_pos(0, 1) = mdl_pos[0][1] - mdl_center[1]; + eigen_mdl_pos(0, 2) = mdl_pos[0][2] - mdl_center[2]; + eigen_mdl_pos(1, 0) = mdl_pos[1][0] - mdl_center[0]; + eigen_mdl_pos(1, 1) = mdl_pos[1][1] - mdl_center[1]; + eigen_mdl_pos(1, 2) = mdl_pos[1][2] - mdl_center[2]; + eigen_mdl_pos(2, 0) = mdl_normal[0]; + eigen_mdl_pos(2, 1) = mdl_normal[1]; + eigen_mdl_pos(2, 2) = mdl_normal[2]; + eigen_mdl_pos(3, 0) = -mdl_normal[0]; + eigen_mdl_pos(3, 1) = -mdl_normal[1]; + eigen_mdl_pos(3, 2) = -mdl_normal[2]; + eigen_ref_pos(0, 0) = ref_pos[0][0] - ref_center[0]; + eigen_ref_pos(0, 1) = ref_pos[0][1] - ref_center[1]; + eigen_ref_pos(0, 2) = ref_pos[0][2] - ref_center[2]; + eigen_ref_pos(1, 0) = ref_pos[1][0] - ref_center[0]; + eigen_ref_pos(1, 1) = ref_pos[1][1] - ref_center[1]; + eigen_ref_pos(1, 2) = ref_pos[1][2] - ref_center[2]; + eigen_ref_pos(2, 0) = ref_normal[0]; + eigen_ref_pos(2, 1) = ref_normal[1]; + eigen_ref_pos(2, 2) = ref_normal[2]; + eigen_ref_pos(3, 0) = -ref_normal[0]; + eigen_ref_pos(3, 1) = -ref_normal[1]; + eigen_ref_pos(3, 2) = -ref_normal[2]; + + Real tmp; // no need to store RMSD + TheobaldRMSD(eigen_mdl_pos, eigen_ref_pos, tmp, eigen_rot); + transform = geom::Mat4(); + transform(0, 0) = eigen_rot(0, 0); + transform(0, 1) = eigen_rot(0, 1); + transform(0, 2) = eigen_rot(0, 2); + transform(1, 0) = eigen_rot(1, 0); + transform(1, 1) = eigen_rot(1, 1); + transform(1, 2) = eigen_rot(1, 2); + transform(2, 0) = eigen_rot(2, 0); + transform(2, 1) = eigen_rot(2, 1); + transform(2, 2) = eigen_rot(2, 2); + + // there are three transformation to be applied to reach ref_pos from + // mdl_pos: + // 1: shift mdl_pos to center + // 2: apply estimated rotation + // 3: shift onto average of ref_pos + Eigen::Matrix<double,1,3> eigen_avg_mdl = Eigen::Matrix<double,1,3>::Zero(); + Eigen::Matrix<double,1,3> eigen_avg_ref = Eigen::Matrix<double,1,3>::Zero(); + eigen_avg_mdl(0,0) = mdl_center[0]; + eigen_avg_mdl(0,1) = mdl_center[1]; + eigen_avg_mdl(0,2) = mdl_center[2]; + eigen_avg_ref(0,0) = ref_center[0]; + eigen_avg_ref(0,1) = ref_center[1]; + eigen_avg_ref(0,2) = ref_center[2]; + Eigen::Matrix<double,1,3> translation = eigen_rot * + (-eigen_avg_mdl.transpose()) + + eigen_avg_ref.transpose(); + transform(0, 3) = translation(0, 0); + transform(1, 3) = translation(0, 1); + transform(2, 3) = translation(0, 2); + n_superposed = 2; + } else { + // the two cannot be superposed within specified distance threshold + // => just set n_superposed to 1 and generate the same transformation + // as in n_pos == 1 + transform = geom::Mat4::Identity(); + transform.PasteTranslation(ref_pos[0] - mdl_pos[0]); + n_superposed = 1; + } + return; + } + if(window_size > n_pos) { - throw ost::Error("Window size in GDT algorithm is larger than positions"); + window_size = n_pos; } Eigen::Matrix<double, Eigen::Dynamic, 3> eigen_mdl_pos = \