6c63c4dbe17ef674e45ffe190e6488abf193716a
[alexxy/gromacs-dssp.git] / src / dssptools.cpp
1 /*
2 * This file is part of the GROMACS molecular simulation package.
3 *
4 * Copyright (c) 2021, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
8 *
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
13 *
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23 *
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
31 *
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
34 */
35 /*! \internal \file
36 * \brief
37 * Implements gmx::analysismodules::Trajectory.
38 *
39 * \author Sergey Gorelov <gorelov_sv@pnpi.nrcki.ru>
40 * \author Anatoly Titov <titov_ai@pnpi.nrcki.ru>
41 * \author Alexey Shvetsov <alexxyum@gmail.com>
42 * \ingroup module_trajectoryanalysis
43 */
44
45 /*
46     There's something wrong with energy of the last residue
47 */
48
49
50 #include "dssptools.h"
51
52 #include <algorithm>
53 #include "gromacs/math/units.h"
54
55 #include "gromacs/pbcutil/pbc.h"
56 #include <gromacs/trajectoryanalysis.h>
57 #include "gromacs/trajectoryanalysis/topologyinformation.h"
58 #include <set>
59 #include <fstream>
60 #include <mutex>
61 #include <iostream>
62
63 namespace gmx
64 {
65
66 namespace analysismodules
67 {
68
69 //void ResInfo::setIndex(backboneAtomTypes atomTypeName, std::size_t atomIndex)
70 //{
71 //   _ResInfo.at(static_cast<std::size_t>(atomTypeName)) = atomIndex;
72 //}
73
74 //std::size_t ResInfo::getIndex(backboneAtomTypes atomTypeName) const
75 //{
76 //   return _ResInfo[static_cast<std::size_t>(atomTypeName)];
77 //}
78
79 std::size_t ResInfo::getIndex(backboneAtomTypes atomTypeName) const{
80     return _backboneIndices[static_cast<std::size_t>(atomTypeName)];
81 }
82
83 secondaryStructures::secondaryStructures(){
84 }
85 void secondaryStructures::initiateSearch(const std::vector<ResInfo> &ResInfoMatrix, const bool PiHelicesPreferencez){
86     SecondaryStructuresStatusMap.resize(0);
87     SecondaryStructuresStringLine.resize(0);
88     std::vector<std::size_t> temp; temp.resize(0),
89     PiHelixPreference = PiHelicesPreferencez;
90     ResInfoMap = &ResInfoMatrix;
91     SecondaryStructuresStatusMap.resize(ResInfoMatrix.size());
92     SecondaryStructuresStringLine.resize(ResInfoMatrix.size(), '~');
93 }
94
95 void secondaryStructures::secondaryStructuresData::setStatus(const secondaryStructureTypes secondaryStructureTypeName){
96     SecondaryStructuresStatusArray[static_cast<std::size_t>(secondaryStructureTypeName)] = true;
97 }
98
99 void secondaryStructures::secondaryStructuresData::setStatus(const HelixPositions helixPosition, const turnsTypes turn){
100     TurnsStatusArray[static_cast<std::size_t>(turn)] = helixPosition;
101 }
102
103 bool secondaryStructures::secondaryStructuresData::getStatus(const secondaryStructureTypes secondaryStructureTypeName) const{
104     return SecondaryStructuresStatusArray[static_cast<std::size_t>(secondaryStructureTypeName)];
105 }
106
107 bool secondaryStructures::secondaryStructuresData::isBreakPartnerWith(const secondaryStructuresData *partner) const{
108     return breakPartners[0] == partner || breakPartners[1] == partner;
109 }
110
111 HelixPositions secondaryStructures::secondaryStructuresData::getStatus(const turnsTypes turn) const{
112     return TurnsStatusArray[static_cast<std::size_t>(turn)];
113 }
114
115 void secondaryStructures::secondaryStructuresData::setBreak(secondaryStructuresData *breakPartner){
116     if (breakPartners[0] == nullptr){
117         breakPartners[0] = breakPartner;
118     }
119     else{
120         breakPartners[1] = breakPartner;
121     }
122     setStatus(secondaryStructureTypes::Break);
123 }
124
125 bool secondaryStructures::hasHBondBetween(std::size_t Donor, std::size_t Acceptor) const{ // prob should add resi name comparison ?
126     if( (*ResInfoMap)[Donor].acceptor[0] == nullptr ||
127         (*ResInfoMap)[Donor].acceptor[1] == nullptr ||
128         (*ResInfoMap)[Acceptor].info == nullptr ){
129         return false;
130     }
131     else {
132
133 //        std::cout << "Comparing DONOR " << Donor << " And ACCEPTOR " << Acceptor << " :";
134 //        std::cout << "DONOR's acceptor adresses are " << (*ResInfoMap)[Donor].acceptor[0] << ", " << (*ResInfoMap)[Donor].acceptor[1] << " and ACCEPTOR adress is " << (*ResInfoMap)[Acceptor].info << std::endl;
135 //        std::cout << "DONOR's acceptors' nr are = " << (*ResInfoMap)[Donor].acceptor[0]->nr << ", " << (*ResInfoMap)[Donor].acceptor[1]->nr << " And ACCEPTOR's nr = " << (*ResInfoMap)[Acceptor].info->nr << std::endl;
136 //        std::cout << "DONOR's acceptors' chainID are = " << (*ResInfoMap)[Donor].acceptor[0]->chainid << ", " << (*ResInfoMap)[Donor].acceptor[1]->chainid << " And ACCEPTOR's chainID = " << (*ResInfoMap)[Acceptor].info->chainid << std::endl;
137
138 //        if( ( (*ResInfoMap)[Donor].acceptor[0] == (*ResInfoMap)[Acceptor].info && (*ResInfoMap)[Donor].acceptorEnergy[0] < HBondEnergyCutOff ) ||
139 //                ( (*ResInfoMap)[Donor].acceptor[1] == (*ResInfoMap)[Acceptor].info && (*ResInfoMap)[Donor].acceptorEnergy[1] < HBondEnergyCutOff ) ){
140 //            std::cout << "HBond Exist" << std::endl;
141 //        }
142         return ( (*ResInfoMap)[Donor].acceptor[0] == (*ResInfoMap)[Acceptor].info && (*ResInfoMap)[Donor].acceptorEnergy[0] < HBondEnergyCutOff ) ||
143                 ( (*ResInfoMap)[Donor].acceptor[1] == (*ResInfoMap)[Acceptor].info && (*ResInfoMap)[Donor].acceptorEnergy[1] < HBondEnergyCutOff );
144     }
145 }
146
147 bool secondaryStructures::NoChainBreaksBetween(std::size_t Resi1, std::size_t Resi2) const{
148     bool flag{true};
149     std::size_t i{Resi1}, j{Resi2}; // From i to j
150     if ( Resi2 < Resi1){
151         std::swap(i, j);
152     }
153
154     for (; i != j; ++i){
155         flag = !(SecondaryStructuresStatusMap[i].isBreakPartnerWith(&SecondaryStructuresStatusMap[i + 1]) && SecondaryStructuresStatusMap[i + 1].isBreakPartnerWith(&SecondaryStructuresStatusMap[i]));
156     }
157
158     if ( SecondaryStructuresStatusMap[i].isBreakPartnerWith(&SecondaryStructuresStatusMap[i + 1]) && SecondaryStructuresStatusMap[i + 1].isBreakPartnerWith(&SecondaryStructuresStatusMap[i]) ){
159         std::cout << " SASSA " << std::endl;
160     }
161
162     if (!flag){
163         std::cout << "Patternsearch has detected a CHAINBREAK between " << Resi1 << " and " << Resi2 << std::endl;
164     }
165
166     return flag;
167 }
168
169 bridgeTypes secondaryStructures::calculateBridge(std::size_t i, std::size_t j) const{
170     if( i < 1 || j < 1 || i + 1 >= ResInfoMap->size() || j + 1 >= ResInfoMap->size() ){
171         return bridgeTypes::None;
172     }
173     if(NoChainBreaksBetween(i - 1, i + 1) && NoChainBreaksBetween(j - 1, j + 1)){
174         if((hasHBondBetween(i + 1, j) && hasHBondBetween(j, i - 1)) || (hasHBondBetween(j + 1, i) && hasHBondBetween(i, j - 1)) ){ //possibly swap
175             return bridgeTypes::ParallelBridge;
176         }
177         else if((hasHBondBetween(i + 1, j - 1) && hasHBondBetween(j + 1, i - 1)) || (hasHBondBetween(j, i) && hasHBondBetween(i, j)) ){ //possibly swap
178             return bridgeTypes::AntiParallelBridge;
179         }
180     }
181     return bridgeTypes::None;
182 }
183
184 void secondaryStructures::analyzeBridgesAndLaddersPatterns(){
185     for(std::size_t i {1}; i + 4 < SecondaryStructuresStatusMap.size(); ++i){
186         for(std::size_t j {i + 3}; j + 1 < SecondaryStructuresStatusMap.size(); ++j ){
187             bridgeTypes type {calculateBridge(i, j)};
188             if (type == bridgeTypes::None){
189                 continue;
190             }
191             bool found {false};
192         }
193     }
194
195
196
197
198
199
200
201
202
203
204
205
206 //    for (std::size_t i{ 1 }; i < HBondsMap.front().size() - 1; ++i){
207 //        for (std::size_t j{ 1 }; j < HBondsMap.front().size() - 1; ++j){
208 //            if (std::abs(static_cast<int>(i) - static_cast<int>(j)) > 2){
209 //                if ((HBondsMap[i - 1][j] && HBondsMap[j][i + 1])    ||
210 //                    (HBondsMap[j - 1][i] && HBondsMap[i][j + 1])){
211 //                    Bridge[i].push_back(j);
212 //                }
213 //                if ((HBondsMap[i][j] && HBondsMap[j][i])    ||
214 //                    (HBondsMap[i - 1][j + 1] && HBondsMap[j - 1][i + 1])){
215 //                    AntiBridge[i].push_back(j);
216 //                }
217 //            }
218 //        }
219 //    }
220 //    for (std::size_t i{ 0 }; i < HBondsMap.front().size(); ++i){
221 //        if ((!Bridge[i].empty() || !AntiBridge[i].empty())){
222 //            setStatus(i, secondaryStructureTypes::Bulge);
223 //        }
224 //    }
225 //    for (std::size_t i{ 2 }; i + 2 < HBondsMap.front().size(); ++i){
226 //        for (std::size_t j { i - 2 }; j <= (i + 2); ++j){
227 //            if (j == i){
228 //                continue;
229 //            }
230 //            else {
231 //                for (std::vector<bridgeTypes>::const_iterator bridge {Bridges.begin()}; bridge != Bridges.end(); ++bridge ){
232 //                    if (!getBridge(*bridge)[i].empty() || !getBridge(*bridge)[j].empty()){
233 //                        for (std::size_t i_resi{ 0 }; i_resi < getBridge(*bridge)[i].size(); ++i_resi){
234 //                            for (std::size_t j_resi{ 0 }; j_resi < getBridge(*bridge)[j].size(); ++j_resi){
235 //                                if (abs(static_cast<int>(getBridge(*bridge)[i][i_resi])
236 //                                        - static_cast<int>(getBridge(*bridge)[j][j_resi]))
237 //                                        && (abs(static_cast<int>(getBridge(*bridge)[i][i_resi])
238 //                                        - static_cast<int>(getBridge(*bridge)[j][j_resi]))
239 //                                        < 5)){
240 //                                    if (j < i){
241 //                                        for (std::size_t k{ 0 }; k <= i - j; ++k){
242 //                                            setStatus(i + k, secondaryStructureTypes::Ladder);
243 //                                        }
244 //                                    }
245 //                                    else{
246 //                                        for (std::size_t k{ 0 }; k <= j - i; ++k){
247 //                                            setStatus(i + k, secondaryStructureTypes::Ladder);
248 //                                        }
249 //                                    }
250 //                                }
251 //                            }
252 //                        }
253 //                    }
254 //                }
255 //            }
256 //        }
257 //    }
258 }
259
260 void secondaryStructures::analyzeTurnsAndHelicesPatterns(){
261     for(const turnsTypes &i : { turnsTypes::Turn_4, turnsTypes::Turn_3, turnsTypes::Turn_5 }){
262         std::cout << "Testing Helix_" << static_cast<std::size_t>(i) + 3 << std::endl;
263         std::size_t stride {static_cast<std::size_t>(i) + 3};
264         for(std::size_t j {0}; j + static_cast<std::size_t>(i) < SecondaryStructuresStatusMap.size(); ++j){
265             std::cout << "Testing " << j << " and " << j + stride << std::endl;
266             if ( hasHBondBetween(j, j + (static_cast<std::size_t>(i))) && NoChainBreaksBetween(j, j + stride) ){
267                 std::cout << j << " and " << j + stride << " has hbond!" << std::endl;
268                 SecondaryStructuresStatusMap[j + static_cast<std::size_t>(i)].setStatus(HelixPositions::End, i);
269
270                 for (std::size_t k {1}; k < (static_cast<std::size_t>(i)); ++k){
271                     if( SecondaryStructuresStatusMap[j + k].getStatus(i) == HelixPositions::None ){
272                         SecondaryStructuresStatusMap[j + k].setStatus(HelixPositions::Middle, i);
273                     }
274
275                 }
276
277                 if( SecondaryStructuresStatusMap[j].getStatus(i) == HelixPositions::End ){
278                     SecondaryStructuresStatusMap[j].setStatus(HelixPositions::Start_AND_End, i);
279                 }
280                 else {
281                     SecondaryStructuresStatusMap[j].setStatus(HelixPositions::Start, i);
282                 }
283             }
284         }
285     }
286
287     for(const turnsTypes &i : { turnsTypes::Turn_4, turnsTypes::Turn_3, turnsTypes::Turn_5 }){
288         std::size_t stride {static_cast<std::size_t>(i) + 3};
289         for(std::size_t j {1}; j + stride < SecondaryStructuresStatusMap.size(); ++j){
290             if ( (SecondaryStructuresStatusMap[j - 1].getStatus(i) == HelixPositions::Start || SecondaryStructuresStatusMap[j - 1].getStatus(i) == HelixPositions::Start_AND_End ) &&
291                  (SecondaryStructuresStatusMap[j].getStatus(i) == HelixPositions::Start || SecondaryStructuresStatusMap[j].getStatus(i) == HelixPositions::Start_AND_End ) ){
292                 bool empty = true;
293                 secondaryStructureTypes Helix;
294                 switch(i){
295                 case turnsTypes::Turn_3:
296                     for (std::size_t k {0}; empty && k < stride; ++k){
297                         empty = SecondaryStructuresStatusMap[j + k].getStatus(secondaryStructureTypes::Loop ) || SecondaryStructuresStatusMap[j + k].getStatus(secondaryStructureTypes::Helix_3);
298                     }
299                     Helix = secondaryStructureTypes::Helix_3;
300                     break;
301                 case turnsTypes::Turn_5:
302                     for (std::size_t k {0}; empty && k < stride; ++k){
303                         empty = SecondaryStructuresStatusMap[j + k].getStatus(secondaryStructureTypes::Loop ) || SecondaryStructuresStatusMap[j + k].getStatus(secondaryStructureTypes::Helix_5) || (PiHelixPreference && SecondaryStructuresStatusMap[j + k].getStatus(secondaryStructureTypes::Helix_5));
304                     }
305                     Helix = secondaryStructureTypes::Helix_4;
306                     break;
307                 default:
308                     Helix = secondaryStructureTypes::Helix_4;
309                     break;
310                 }
311                 std::cout << j << " is HELIX" << std::endl;
312                 if ( empty ){
313                     for(std::size_t k {0}; k < (static_cast<std::size_t>(i)); ++k ){
314                         SecondaryStructuresStatusMap[j + k].setStatus(Helix);
315                     }
316                 }
317             }
318         }
319     }
320
321     for(std::size_t i {1}; i + 1 < SecondaryStructuresStatusMap.size(); ++i){
322         if (SecondaryStructuresStatusMap[i].getStatus(secondaryStructureTypes::Loop)){
323             bool isTurn = false;
324             for(const turnsTypes &j : {turnsTypes::Turn_3, turnsTypes::Turn_4, turnsTypes::Turn_5}){
325                 std::size_t stride {static_cast<std::size_t>(i) + 3};
326                 for(std::size_t k {1}; k < stride; ++k){
327                     isTurn = (i >= k) && (SecondaryStructuresStatusMap[i - k].getStatus(j) == HelixPositions::Start || SecondaryStructuresStatusMap[i - k].getStatus(j) == HelixPositions::Start_AND_End) ;
328                 }
329             }
330
331             if (isTurn){
332                 SecondaryStructuresStatusMap[i].setStatus(secondaryStructureTypes::Turn);
333             }
334             else if (SecondaryStructuresStatusMap[i].getStatus(secondaryStructureTypes::Bend)){
335                 SecondaryStructuresStatusMap[i].setStatus(secondaryStructureTypes::Bend);
336             }
337         }
338     }
339
340 }
341
342 void secondaryStructures::analyzePPHelicesPatterns(){}
343
344 std::string secondaryStructures::patternSearch(){
345
346 //    analyzeBridgesAndLaddersPatterns();
347     analyzeTurnsAndHelicesPatterns();
348 //    analyzePPHelicesPatterns();
349
350 //    for(std::size_t i {0}; i < ResInfoMap->size(); ++i){
351 //        std::cout << (*ResInfoMap)[i].info->nr << " " << *((*ResInfoMap)[i].info->name) << std::endl;
352 //    }
353
354 //    std::cout.precision(5);
355 //    for(std::size_t i{0}; i < ResInfoMap->size(); ++i, std::cout << std::endl << std::endl){
356 //        std::cout << (*ResInfoMap)[i].info->nr << " " << *((*ResInfoMap)[i].info->name) ;
357 //        if ( (*ResInfoMap)[i].donor[0] != nullptr ){
358 //            std::cout << " has donor[0] = " << (*ResInfoMap)[i].donor[0]->nr << " " << *((*ResInfoMap)[i].donor[0]->name) << " with E = " << (*ResInfoMap)[i].donorEnergy[0] << " and" ;
359 //        }
360 //        else {
361 //            std::cout << " has no donor[0] and" ;
362 //        }
363 //        if ( (*ResInfoMap)[i].acceptor[0] != nullptr ){
364 //            std::cout << " has acceptor[0] = " << (*ResInfoMap)[i].acceptor[0]->nr << " " << *((*ResInfoMap)[i].acceptor[0]->name) << " with E = " << (*ResInfoMap)[i].acceptorEnergy[0] ;
365 //        }
366 //        else {
367 //            std::cout << " has no acceptor[0]" ;
368 //        }
369 //        std::cout << std::endl << "Also, " << (*ResInfoMap)[i].info->nr << " " << *((*ResInfoMap)[i].info->name);
370 //        if ( (*ResInfoMap)[i].donor[1] != nullptr ){
371 //            std::cout << " has donor[1] = " << (*ResInfoMap)[i].donor[1]->nr << " " << *((*ResInfoMap)[i].donor[1]->name) << " with E = " << (*ResInfoMap)[i].donorEnergy[1] << " and" ;
372 //        }
373 //        else {
374 //            std::cout << " has no donor[1] and" ;
375 //        }
376 //        if ( (*ResInfoMap)[i].acceptor[1] != nullptr ){
377 //            std::cout << " has acceptor[1] = " << (*ResInfoMap)[i].acceptor[1]->nr << " " << *((*ResInfoMap)[i].acceptor[1]->name) << " with E = " << (*ResInfoMap)[i].acceptorEnergy[1] ;
378 //        }
379 //        else {
380 //            std::cout << " has no acceptor[1]" ;
381 //        }
382 //    }
383
384     /*Write Data*/
385
386     for(std::size_t i {static_cast<std::size_t>(secondaryStructureTypes::Bend)}; i != static_cast<std::size_t>(secondaryStructureTypes::Count); ++i){
387         for(std::size_t j {0}; j < SecondaryStructuresStatusMap.size(); ++j){
388             if (SecondaryStructuresStatusMap[j].getStatus(static_cast<secondaryStructureTypes>(i))){
389                 SecondaryStructuresStringLine[j] = secondaryStructureTypeNames[i] ;
390             }
391         }
392     }
393
394     /*Add breaks*/
395
396     if(SecondaryStructuresStatusMap.size() > 1){
397         for(std::size_t i {0}, linefactor{1}; i + 1 < SecondaryStructuresStatusMap.size(); ++i){
398             if( SecondaryStructuresStatusMap[i].getStatus(secondaryStructureTypes::Break) && SecondaryStructuresStatusMap[i + 1].getStatus(secondaryStructureTypes::Break) ){
399                 if(SecondaryStructuresStatusMap[i].isBreakPartnerWith(&SecondaryStructuresStatusMap[i + 1]) && SecondaryStructuresStatusMap[i + 1].isBreakPartnerWith(&SecondaryStructuresStatusMap[i]) ){
400                     SecondaryStructuresStringLine.insert(SecondaryStructuresStringLine.begin() + i + linefactor, secondaryStructureTypeNames[secondaryStructureTypes::Break]);
401                     ++linefactor;
402                 }
403             }
404         }
405     }
406     return SecondaryStructuresStringLine;
407 }
408
409 secondaryStructures::~secondaryStructures(){
410     SecondaryStructuresStatusMap.resize(0);
411     SecondaryStructuresStringLine.resize(0);
412 }
413
414 DsspTool::DsspStorage::DsspStorage(){
415     storaged_data.resize(0);
416 }
417
418 void DsspTool::DsspStorage::clearAll(){
419     storaged_data.resize(0);
420 }
421
422 std::mutex DsspTool::DsspStorage::mx;
423
424 void DsspTool::DsspStorage::storageData(int frnr, std::string data){
425     std::lock_guard<std::mutex> guardian(mx);
426     std::pair<int, std::string> datapair(frnr, data);
427     storaged_data.push_back(datapair);
428 }
429
430 std::vector<std::pair<int, std::string>> DsspTool::DsspStorage::returnData(){
431     std::sort(storaged_data.begin(), storaged_data.end());
432     return storaged_data;
433 }
434
435 void alternateNeighborhoodSearch::setCutoff(const real &cutoff_init){
436     cutoff = cutoff_init;
437 }
438
439 void alternateNeighborhoodSearch::FixAtomCoordinates(real &coordinate, const real vector_length){
440     while (coordinate < 0) {
441         coordinate += vector_length;
442     }
443     while (coordinate >= vector_length) {
444         coordinate -= vector_length;
445     }
446 }
447
448 void alternateNeighborhoodSearch::ReCalculatePBC(int &x, const int &x_max) {
449     if (x < 0) {
450         x += x_max;
451     }
452     if (x >= x_max) {
453         x -= x_max;
454     }
455 }
456
457 void alternateNeighborhoodSearch::GetMiniBoxesMap(const t_trxframe &fr, const std::vector<ResInfo> &IndexMap){
458     rvec coordinates, box_vector_length;
459     num_of_miniboxes.resize(0);
460     num_of_miniboxes.resize(3);
461     for (std::size_t i{XX}; i <= ZZ; ++i) {
462         box_vector_length[i] = std::sqrt(
463                     std::pow(fr.box[i][XX], 2) + std::pow(fr.box[i][YY], 2) + std::pow(fr.box[i][ZZ], 2));
464         num_of_miniboxes[i] = std::floor((box_vector_length[i] / cutoff)) + 1;
465     }
466     MiniBoxesMap.resize(0);
467     MiniBoxesReverseMap.resize(0);
468     MiniBoxesMap.resize(num_of_miniboxes[XX], std::vector<std::vector<std::vector<std::size_t> > >(
469                              num_of_miniboxes[YY], std::vector<std::vector<std::size_t> >(
470                              num_of_miniboxes[ZZ], std::vector<std::size_t>(
471                              0))));
472     MiniBoxesReverseMap.resize(IndexMap.size(), std::vector<std::size_t>(3));
473     for (std::vector<ResInfo>::const_iterator i {IndexMap.begin()}; i != IndexMap.end(); ++i) {
474         for (std::size_t j{XX}; j <= ZZ; ++j) {
475             coordinates[j] = fr.x[i->getIndex(backboneAtomTypes::AtomCA)][j];
476             FixAtomCoordinates(coordinates[j], box_vector_length[j]);
477         }
478         MiniBoxesMap[std::floor(coordinates[XX] / cutoff)][std::floor(coordinates[YY] / cutoff)][std::floor(
479                           coordinates[ZZ] / cutoff)].push_back(i - IndexMap.begin());
480         for (std::size_t j{XX}; j <= ZZ; ++j){
481             MiniBoxesReverseMap[i - IndexMap.begin()][j] = std::floor(coordinates[j] / cutoff);
482         }
483     }
484 }
485
486 void alternateNeighborhoodSearch::AltPairSearch(const t_trxframe &fr, const std::vector<ResInfo> &IndexMap){
487     GetMiniBoxesMap(fr, IndexMap);
488     MiniBoxSize[XX] = MiniBoxesMap.size();
489     MiniBoxSize[YY] = MiniBoxesMap.front().size();
490     MiniBoxSize[ZZ] = MiniBoxesMap.front().front().size();
491     PairMap.resize(0);
492     PairMap.resize(IndexMap.size(), std::vector<bool>(IndexMap.size(), false));
493     ResiI = PairMap.begin();
494     ResiJ = ResiI->begin();
495
496     for (std::vector<ResInfo>::const_iterator i = IndexMap.begin(); i != IndexMap.end(); ++i){
497         for (offset[XX] = -1; offset[XX] <= 1; ++offset[XX]) {
498             for (offset[YY] = -1; offset[YY] <= 1; ++offset[YY]) {
499                 for (offset[ZZ] = -1; offset[ZZ] <= 1; ++offset[ZZ]) {
500                     for (std::size_t k{XX}; k <= ZZ; ++k) {
501                         fixBox[k] = MiniBoxesReverseMap[i - IndexMap.begin()][k] + offset[k];
502                         ReCalculatePBC(fixBox[k], MiniBoxSize[k]);
503                     }
504                     for (std::size_t j{0}; j < MiniBoxesMap[fixBox[XX]][fixBox[YY]][fixBox[ZZ]].size(); ++j) {
505                         if ( (i - IndexMap.begin()) != MiniBoxesMap[fixBox[XX]][fixBox[YY]][fixBox[ZZ]][j]){
506                             PairMap[i - IndexMap.begin()][MiniBoxesMap[fixBox[XX]][fixBox[YY]][fixBox[ZZ]][j]] = true;
507                             PairMap[MiniBoxesMap[fixBox[XX]][fixBox[YY]][fixBox[ZZ]][j]][i - IndexMap.begin()] = true;
508                         }
509                     }
510                 }
511             }
512         }
513     }
514 }
515
516 bool alternateNeighborhoodSearch::findNextPair(){
517
518     if(!PairMap.size()){
519         return false;
520     }
521
522     for(; ResiI != PairMap.end(); ++ResiI, ResiJ = ResiI->begin() ){
523         for(; ResiJ != ResiI->end(); ++ResiJ){
524             if(*ResiJ){
525                 resiIpos = ResiI - PairMap.begin();
526                 resiJpos = ResiJ - ResiI->begin();
527                 if ( ResiJ != ResiI->end() ){
528                     ++ResiJ;
529                 }
530                 else if (ResiI != PairMap.end()) {
531                     ++ResiI;
532                     ResiJ = ResiI->begin();
533                 }
534                 else {
535                     return false; // ???
536                 }
537                 return true;
538             }
539         }
540     }
541
542     return false;
543 }
544
545 std::size_t alternateNeighborhoodSearch::getResiI() const {
546     return resiIpos;
547 }
548
549 std::size_t alternateNeighborhoodSearch::getResiJ() const {
550     return resiJpos;
551 }
552
553
554 DsspTool::DsspStorage DsspTool::Storage;
555
556 DsspTool::DsspTool(){
557 }
558
559 void DsspTool::calculateBends(const t_trxframe &fr, const t_pbc *pbc)
560 {
561    const float benddegree{ 70.0 }, maxdist{ 2.5 };
562    float       degree{ 0 }, vdist{ 0 }, vprod{ 0 };
563    gmx::RVec   a{ 0, 0, 0 }, b{ 0, 0, 0 };
564    for (std::size_t i{ 0 }; i + 1 < IndexMap.size(); ++i)
565    {
566        if (CalculateAtomicDistances(static_cast<int>(IndexMap[i].getIndex(backboneAtomTypes::AtomC)),
567                                     static_cast<int>(IndexMap[i + 1].getIndex(backboneAtomTypes::AtomN)),
568                                     fr,
569                                     pbc)
570            > maxdist)
571        {
572            PatternSearch.SecondaryStructuresStatusMap[i].setBreak(&PatternSearch.SecondaryStructuresStatusMap[i + 1]);
573            PatternSearch.SecondaryStructuresStatusMap[i + 1].setBreak(&PatternSearch.SecondaryStructuresStatusMap[i]);
574
575 //           std::cout << "Break between " << i + 1 << " and " << i + 2 << std::endl;
576        }
577    }
578    for (std::size_t i{ 2 }; i + 2 < IndexMap.size() ; ++i)
579    {
580        if (PatternSearch.SecondaryStructuresStatusMap[i - 2].getStatus(secondaryStructureTypes::Break) ||
581            PatternSearch.SecondaryStructuresStatusMap[i - 1].getStatus(secondaryStructureTypes::Break) ||
582            PatternSearch.SecondaryStructuresStatusMap[i].getStatus(secondaryStructureTypes::Break) ||
583            PatternSearch.SecondaryStructuresStatusMap[i + 1].getStatus(secondaryStructureTypes::Break)
584           )
585        {
586            continue;
587        }
588        for (int j{ 0 }; j < 3; ++j)
589        {
590            a[j] = fr.x[IndexMap[i].getIndex(backboneAtomTypes::AtomCA)][j]
591                   - fr.x[IndexMap[i - 2].getIndex(backboneAtomTypes::AtomCA)][j];
592            b[j] = fr.x[IndexMap[i + 2].getIndex(backboneAtomTypes::AtomCA)][j]
593                   - fr.x[IndexMap[i].getIndex(backboneAtomTypes::AtomCA)][j];
594        }
595        vdist = (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
596        vprod = CalculateAtomicDistances(IndexMap[i - 2].getIndex(backboneAtomTypes::AtomCA),
597                                         IndexMap[i].getIndex(backboneAtomTypes::AtomCA),
598                                         fr,
599                                         pbc)
600                * gmx::c_angstrom / gmx::c_nano
601                * CalculateAtomicDistances(IndexMap[i].getIndex(backboneAtomTypes::AtomCA),
602                                           IndexMap[i + 2].getIndex(backboneAtomTypes::AtomCA),
603                                           fr,
604                                           pbc)
605                * gmx::c_angstrom / gmx::c_nano;
606        degree = std::acos(vdist / vprod) * gmx::c_rad2Deg;
607        if (degree > benddegree)
608        {
609            PatternSearch.SecondaryStructuresStatusMap[i].setStatus(secondaryStructureTypes::Bend);
610        }
611    }
612 }
613
614 void DsspTool::calculateHBondEnergy(ResInfo& Donor,
615                        ResInfo& Acceptor,
616                        const t_trxframe&          fr,
617                        const t_pbc*               pbc)
618 {
619    /*
620     * DSSP uses eq from dssp 2.x
621     * kCouplingConstant = 27.888,  //  = 332 * 0.42 * 0.2
622     * E = k * (1/rON + 1/rCH - 1/rOH - 1/rCN) where CO comes from one AA and NH from another
623     * if R is in A
624     * Hbond if E < -0.5
625     */
626
627     const float kCouplingConstant = 27.888;
628     const float minimalAtomDistance{ 0.5 },
629             minEnergy{ -9.9 };
630     float HbondEnergy{ 0 };
631     float distanceNO{ 0 }, distanceHC{ 0 }, distanceHO{ 0 }, distanceNC{ 0 };
632
633    if( !(Donor.is_proline) ){
634        if (Acceptor.getIndex(backboneAtomTypes::AtomC) && Acceptor.getIndex(backboneAtomTypes::AtomO)
635            && Donor.getIndex(backboneAtomTypes::AtomN) && ( Donor.getIndex(backboneAtomTypes::AtomH) || (initParams.addHydrogens) ) )  // Kinda ew
636        {
637
638            distanceNO = CalculateAtomicDistances(
639                    Donor.getIndex(backboneAtomTypes::AtomN), Acceptor.getIndex(backboneAtomTypes::AtomO), fr, pbc);
640            distanceNC = CalculateAtomicDistances(
641                    Donor.getIndex(backboneAtomTypes::AtomN), Acceptor.getIndex(backboneAtomTypes::AtomC), fr, pbc);
642
643            if (initParams.addHydrogens){
644                if (Donor.prevResi != nullptr && Donor.prevResi->getIndex(backboneAtomTypes::AtomC) && Donor.prevResi->getIndex(backboneAtomTypes::AtomO)){
645 //                   std::cout << "On donor " << Donor.info->nr << *(Donor.info->name) << std::endl;
646 //                   std::cout << "Prev donor is " << Donor.prevResi->info->nr << *(Donor.prevResi->info->name) << std::endl;
647 //                   std::cout << "Prev C index is " << Donor.prevResi->getIndex(backboneAtomTypes::AtomC) << std::endl;
648 //                   std::cout << "Prev O index is " << Donor.prevResi->getIndex(backboneAtomTypes::AtomO) << std::endl;
649                    rvec atomH{};
650                    float prevCODist {CalculateAtomicDistances(Donor.prevResi->getIndex(backboneAtomTypes::AtomC), Donor.prevResi->getIndex(backboneAtomTypes::AtomO), fr, pbc)};
651                    for (int i{XX}; i <= ZZ; ++i){
652                        float prevCO = fr.x[Donor.prevResi->getIndex(backboneAtomTypes::AtomC)][i] - fr.x[Donor.prevResi->getIndex(backboneAtomTypes::AtomO)][i];
653                        atomH[i] = prevCO / prevCODist;
654                    }
655                    distanceHO = CalculateAtomicDistances(atomH, Acceptor.getIndex(backboneAtomTypes::AtomO), fr, pbc);
656                    distanceHC = CalculateAtomicDistances(atomH, Acceptor.getIndex(backboneAtomTypes::AtomC), fr, pbc);
657                }
658                else{
659                    distanceHO = distanceNO;
660                    distanceHC = distanceNC;
661                }
662            }
663            else {
664                distanceHO = CalculateAtomicDistances(
665                        Donor.getIndex(backboneAtomTypes::AtomH), Acceptor.getIndex(backboneAtomTypes::AtomO), fr, pbc);
666                distanceHC = CalculateAtomicDistances(
667                        Donor.getIndex(backboneAtomTypes::AtomH), Acceptor.getIndex(backboneAtomTypes::AtomC), fr, pbc);
668            }
669
670            if (CalculateAtomicDistances(
671                        Donor.getIndex(backboneAtomTypes::AtomCA), Acceptor.getIndex(backboneAtomTypes::AtomCA), fr, pbc)
672                < minimalCAdistance)
673            {
674                if ((distanceNO < minimalAtomDistance) || (distanceHC < minimalAtomDistance)
675                    || (distanceHO < minimalAtomDistance) || (distanceNC < minimalAtomDistance))
676                {
677                    HbondEnergy = minEnergy;
678
679 //                   std::cout << "HBOND exists cause of distance" << std::endl;
680                }
681                else
682                {
683                    HbondEnergy =
684                            kCouplingConstant
685                            * ((1 / distanceNO) + (1 / distanceHC) - (1 / distanceHO) - (1 / distanceNC));
686                    HbondEnergy = std::round(HbondEnergy * 1000) / 1000;
687
688 //                   std::cout.precision(5);
689 //                   std::cout << "Calculated ENERGY = " << HbondEnergy << std::endl;
690
691 //                   if ( HbondEnergy == 0){
692 //                       std::cout << "Calculated ENERGY = " << HbondEnergy << " For donor " << Donor.info->nr << " and acceptor " << Acceptor.info->nr << std::endl;
693 //                   }
694
695                    if ( HbondEnergy < minEnergy ){
696                        HbondEnergy = minEnergy;
697                    }
698                }
699            }
700        }
701    }
702 //   else {
703 //       std::cerr << "PRO DETECTED! THIS IS RESI № " << Donor.info->nr << std::endl; //IT WORKS JUST FINE
704 //   }
705
706    if (HbondEnergy < Donor.acceptorEnergy[0]){
707        Donor.acceptor[1] = Donor.acceptor[0];
708        Donor.acceptor[0] = Acceptor.info;
709        Donor.acceptorEnergy[0] = HbondEnergy;
710    }
711    else if (HbondEnergy < Donor.acceptorEnergy[1]){
712        Donor.acceptor[1] = Acceptor.info;
713        Donor.acceptorEnergy[1] = HbondEnergy;
714    }
715
716    if (HbondEnergy < Acceptor.donorEnergy[0]){
717        Acceptor.donor[1] = Acceptor.donor[0];
718        Acceptor.donor[0] = Donor.info;
719        Acceptor.donorEnergy[0] = HbondEnergy;
720    }
721    else if (HbondEnergy < Acceptor.donorEnergy[1]){
722        Acceptor.donor[1] = Donor.info;
723        Acceptor.donorEnergy[1] = HbondEnergy;
724    }
725 }
726
727
728 /* Calculate Distance From B to A */
729 float DsspTool::CalculateAtomicDistances(const int &A, const int &B, const t_trxframe &fr, const t_pbc *pbc)
730 {
731    gmx::RVec r{ 0, 0, 0 };
732    pbc_dx(pbc, fr.x[A], fr.x[B], r.as_vec());
733 //   return r.norm(); // TODO * gmx::c_nm2A; if not PDB, i guess????
734    return r.norm() * gmx::c_nm2A; // TODO * gmx::c_nm2A; if not PDB, i guess????
735 }
736
737 /* Calculate Distance From B to A, where A is only fake coordinates */
738 float DsspTool::CalculateAtomicDistances(const rvec &A, const int &B, const t_trxframe &fr, const t_pbc *pbc)
739 {
740    gmx::RVec r{ 0, 0, 0 };
741    pbc_dx(pbc, A, fr.x[B], r.as_vec());
742 //   return r.norm(); // TODO * gmx::c_nm2A; if not PDB, i guess????
743    return r.norm() * gmx::c_nm2A; // TODO * gmx::c_nm2A; if not PDB, i guess????
744 }
745
746 void DsspTool::initAnalysis(/*const TrajectoryAnalysisSettings &settings,*/const TopologyInformation& top, const initParameters &initParamz)
747 {
748
749    std::cout << "Init started" << std::endl;
750    initParams = initParamz;
751    ResInfo _backboneAtoms;
752    std::size_t                 i{ 0 };
753    std::string proLINE;
754    int resicompare{ top.atoms()->atom[static_cast<std::size_t>(*(initParams.sel_.atomIndices().begin()))].resind };
755    IndexMap.resize(0);
756    IndexMap.push_back(_backboneAtoms);
757    IndexMap[i].info = &(top.atoms()->resinfo[resicompare]);
758    proLINE = *(IndexMap[i].info->name);
759    if( proLINE.compare("PRO") == 0 ){
760        IndexMap[i].is_proline = true;
761    }
762
763    for (gmx::ArrayRef<const int>::iterator ai{ initParams.sel_.atomIndices().begin() }; (ai != initParams.sel_.atomIndices().end()); ++ai){
764        if (resicompare != top.atoms()->atom[static_cast<std::size_t>(*ai)].resind)
765        {
766            ++i;
767            resicompare = top.atoms()->atom[static_cast<std::size_t>(*ai)].resind;
768            IndexMap.emplace_back(_backboneAtoms);
769            IndexMap[i].info = &(top.atoms()->resinfo[resicompare]);
770            proLINE = *(IndexMap[i].info->name);
771            if( proLINE.compare("PRO") == 0 ){
772                IndexMap[i].is_proline = true;
773            }
774
775        }
776        std::string atomname(*(top.atoms()->atomname[static_cast<std::size_t>(*ai)]));
777        if (atomname == backboneAtomTypeNames[backboneAtomTypes::AtomCA])
778        {
779            IndexMap[i]._backboneIndices[static_cast<std::size_t>(backboneAtomTypes::AtomCA)] = *ai;
780        }
781        else if (atomname == backboneAtomTypeNames[backboneAtomTypes::AtomC])
782        {
783            IndexMap[i]._backboneIndices[static_cast<std::size_t>(backboneAtomTypes::AtomC)] = *ai;
784        }
785        else if (atomname == backboneAtomTypeNames[backboneAtomTypes::AtomO])
786        {
787            IndexMap[i]._backboneIndices[static_cast<std::size_t>(backboneAtomTypes::AtomO)] = *ai;
788        }
789        else if (atomname == backboneAtomTypeNames[backboneAtomTypes::AtomN])
790        {
791            IndexMap[i]._backboneIndices[static_cast<std::size_t>(backboneAtomTypes::AtomN)] = *ai;
792        }
793        else if (atomname == backboneAtomTypeNames[backboneAtomTypes::AtomH] && initParamz.addHydrogens == false) // Юзать водород в структуре
794        {
795            IndexMap[i]._backboneIndices[static_cast<std::size_t>(backboneAtomTypes::AtomH)] = *ai;
796        }
797
798 //       if( atomname == backboneAtomTypeNames[backboneAtomTypes::AtomCA] || atomname == backboneAtomTypeNames[backboneAtomTypes::AtomC] || atomname == backboneAtomTypeNames[backboneAtomTypes::AtomO]
799 //       || atomname == backboneAtomTypeNames[backboneAtomTypes::AtomN] || atomname == backboneAtomTypeNames[backboneAtomTypes::AtomH]){
800 //           std::cout << "Atom " << atomname << " №" << *ai << " From Resi " << *(top.atoms()->resinfo[i].name) << " №" << resicompare << std::endl;
801 //       }
802    }
803
804    for (std::size_t j {1}; j < IndexMap.size(); ++j){
805        IndexMap[j].prevResi = &(IndexMap[j - 1]);
806
807        IndexMap[j - 1].nextResi = &(IndexMap[j]);
808
809 //           std::cout << "Resi " << IndexMap[i].info->nr << *(IndexMap[i].info->name) << std::endl;
810 //           std::cout << "Prev resi is " << IndexMap[i].prevResi->info->nr << *(IndexMap[i].prevResi->info->name) << std::endl;
811 //           std::cout << "Prev resi's next resi is " << IndexMap[i - 1].nextResi->info->nr << *(IndexMap[i - 1].nextResi->info->name) << std::endl;
812 //         std::cout << IndexMap[j].prevResi->info->nr;
813 //         std::cout << *(IndexMap[j].prevResi->info->name) ;
814 //         std::cout << " have CA = " << IndexMap[j].prevResi->getIndex(backboneAtomTypes::AtomCA) ;
815 //         std::cout << " C = " << IndexMap[j].prevResi->getIndex(backboneAtomTypes::AtomC);
816 //         std::cout << " O = " << IndexMap[j].prevResi->getIndex(backboneAtomTypes::AtomO);
817 //         std::cout << " N = " << IndexMap[j].prevResi->getIndex(backboneAtomTypes::AtomN);
818 //         std::cout << " H = " << IndexMap[j].prevResi->getIndex(backboneAtomTypes::AtomH) << std::endl;
819    }
820
821    nres = i + 1;
822 }
823
824 void DsspTool::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc)
825 {
826
827     switch(initParams.NBS){
828     case (NBSearchMethod::Classique): {
829
830         // store positions of CA atoms to use them for nbSearch
831         std::vector<gmx::RVec> positionsCA_;
832         for (std::size_t i{ 0 }; i < IndexMap.size(); ++i)
833         {
834             positionsCA_.emplace_back(fr.x[IndexMap[i].getIndex(backboneAtomTypes::AtomCA)]);
835         }
836
837         AnalysisNeighborhood nb_;
838         nb_.setCutoff(initParams.cutoff_);
839         AnalysisNeighborhoodPositions       nbPos_(positionsCA_);
840         gmx::AnalysisNeighborhoodSearch     start      = nb_.initSearch(pbc, nbPos_);
841         gmx::AnalysisNeighborhoodPairSearch pairSearch = start.startPairSearch(nbPos_);
842         gmx::AnalysisNeighborhoodPair       pair;
843         while (pairSearch.findNextPair(&pair))
844         {
845             if(CalculateAtomicDistances(
846                         IndexMap[pair.refIndex()].getIndex(backboneAtomTypes::AtomCA), IndexMap[pair.testIndex()].getIndex(backboneAtomTypes::AtomCA), fr, pbc)
847                 < minimalCAdistance){
848                 calculateHBondEnergy(IndexMap[pair.refIndex()], IndexMap[pair.testIndex()], fr, pbc);
849                 if (IndexMap[pair.testIndex()].info != IndexMap[pair.refIndex() + 1].info){
850                     calculateHBondEnergy(IndexMap[pair.testIndex()], IndexMap[pair.refIndex()], fr, pbc);
851                 }
852             }
853         }
854
855         break;
856     }
857     case (NBSearchMethod::Experimental): { // TODO FIX
858
859         alternateNeighborhoodSearch as_;
860
861         as_.setCutoff(initParams.cutoff_);
862
863         as_.AltPairSearch(fr, IndexMap);
864
865         while (as_.findNextPair()){
866             if(CalculateAtomicDistances(
867                         IndexMap[as_.getResiI()].getIndex(backboneAtomTypes::AtomCA), IndexMap[as_.getResiJ()].getIndex(backboneAtomTypes::AtomCA), fr, pbc)
868                 < minimalCAdistance){
869                 calculateHBondEnergy(IndexMap[as_.getResiI()], IndexMap[as_.getResiJ()], fr, pbc);
870                 if (IndexMap[as_.getResiJ()].info != IndexMap[as_.getResiI() + 1].info){
871                     calculateHBondEnergy(IndexMap[as_.getResiJ()], IndexMap[as_.getResiI()], fr, pbc);
872                 }
873             }
874         }
875
876         break;
877     }
878     default: {
879
880         for(std::vector<ResInfo>::iterator Donor {IndexMap.begin()}; Donor != IndexMap.end() ; ++Donor){
881             for(std::vector<ResInfo>::iterator Acceptor {Donor + 1} ; Acceptor != IndexMap.end() ; ++Acceptor){
882                 if(CalculateAtomicDistances(
883                             Donor->getIndex(backboneAtomTypes::AtomCA), Acceptor->getIndex(backboneAtomTypes::AtomCA), fr, pbc)
884                     < minimalCAdistance){
885                     calculateHBondEnergy(*Donor, *Acceptor, fr, pbc);
886                     if (Acceptor != Donor + 1){
887                         calculateHBondEnergy(*Acceptor, *Donor, fr, pbc);
888                     }
889                 }
890             };
891         };
892         break;
893     }
894     }
895
896
897 //    for(std::size_t i {0}; i < IndexMap.size(); ++i){
898 //        std::cout << IndexMap[i].info->nr << " " << *(IndexMap[i].info->name) << std::endl;
899 //    }
900
901    PatternSearch.initiateSearch(IndexMap, initParams.PPHelices);
902    calculateBends(fr, pbc);
903    Storage.storageData(frnr, PatternSearch.patternSearch());
904
905 }
906
907 std::vector<std::pair<int, std::string>> DsspTool::getData(){
908     return Storage.returnData();
909 }
910
911 } // namespace analysismodules
912
913 } // namespace gmx