github.com/rmera/gochem@v0.7.1/ramacalc.go (about) 1 /* 2 * ramacalc.go, part of gochem. 3 * 4 * 5 * Copyright 2012 Raul Mera <rmera{at}chemDOThelsinkiDOTfi> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU Lesser General Public License as 9 * published by the Free Software Foundation; either version 2.1 of the 10 * License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General 18 * Public License along with this program. If not, see 19 * <http://www.gnu.org/licenses/>. 20 * 21 * 22 * Gochem is developed at the laboratory for instruction in Swedish, Department of Chemistry, 23 * University of Helsinki, Finland. 24 * 25 * 26 */ 27 package chem 28 29 import ( 30 "fmt" 31 "strings" 32 33 v3 "github.com/rmera/gochem/v3" 34 ) 35 36 //A structure with the data for obtaining one pair of Ramachandran angles. 37 type RamaSet struct { 38 Cprev int 39 N int 40 Ca int 41 C int 42 Npost int 43 MolID int 44 MolName string 45 } 46 47 // RamaCalc Obtains the values for the phi and psi dihedrals indicated in []Ramaset, for the 48 // structure M. The angles are in *degrees*. It returns a slice of 2-element slices, one for the phi the next for the psi 49 // dihedral, a and an error or nil. 50 func RamaCalc(M *v3.Matrix, dihedrals []RamaSet) ([][]float64, error) { 51 if M == nil || dihedrals == nil { 52 return nil, CError{string(ErrNilData), []string{"RamaCalc"}} 53 } 54 r, _ := M.Dims() 55 Rama := make([][]float64, 0, len(dihedrals)) 56 for _, j := range dihedrals { 57 if j.Npost >= r { 58 return nil, CError{"Data out of range", []string{"RamaCalc"}} 59 } 60 Cprev := M.VecView(j.Cprev) 61 N := M.VecView(j.N) 62 Ca := M.VecView(j.Ca) 63 C := M.VecView(j.C) 64 Npost := M.VecView(j.Npost) 65 phi := DihedralRama(Cprev, N, Ca, C) 66 psi := DihedralRama(N, Ca, C, Npost) 67 temp := []float64{phi * Rad2Deg, psi * Rad2Deg} 68 Rama = append(Rama, temp) 69 } 70 return Rama, nil 71 } 72 73 // RamaResidueFilter filters the set of a slice of RamaSet (i.e. a set of Ramachandran angles to be calculated) 74 //by residue.(ex. only GLY, everything but GLY) 75 // The 3 letter code of the residues to be filtered in or out is in filterdata, whether they are filter in 76 // or out depends on shouldBePresent. It returns the filtered data and a slice containing the indexes in 77 // the new data of the residues in the old data, when they are included, or -1 when they are not included. 78 func RamaResidueFilter(dihedrals []RamaSet, filterdata []string, shouldBePresent bool) ([]RamaSet, []int) { 79 RetList := make([]RamaSet, 0, 0) 80 Index := make([]int, len(dihedrals)) 81 var added int 82 for key, val := range dihedrals { 83 isPresent := isInString(filterdata, val.MolName) 84 if isPresent == shouldBePresent { 85 RetList = append(RetList, val) 86 Index[key] = added 87 added++ 88 } else { 89 Index[key] = -1 90 } 91 } 92 return RetList, Index 93 } 94 95 // RamaList takes a molecule and returns a slice of RamaSet, which contains the 96 // indexes for each dihedral to be included in a Ramachandran plot. It gets the dihedral 97 // indices for all residues in the range resran. if resran has 2 elements defining the 98 // boundaries. Otherwise, returns dihedral lists for the residues included in 99 // resran. If resran has 2 elements and the last is -1, RamaList will 100 // get all the dihedral for residues from resran[0] to the end of the chain. 101 // It only obtain dihedral lists for residues belonging to a chain included in chains. 102 func RamaList(M Atomer, chains string, resran []int) ([]RamaSet, error) { 103 RamaList := make([]RamaSet, 0, 0) 104 if len(resran) == 2 { 105 if resran[1] == -1 { 106 resran[1] = 999999999 //should work! 107 } 108 } 109 if M == nil { 110 return nil, CError{"Nil data given", []string{"RamaList"}} 111 } 112 C := -1 113 N := -1 114 Ca := -1 115 Cprev := -1 116 Npost := -1 117 chainprev := "NOTAVALIDCHAIN" //any non-valid chain name 118 for num := 0; num < M.Len(); num++ { 119 at := M.Atom(num) 120 //First get the indexes we need. Change: If you give RamaList an empty string for "chains", it will 121 //include all chains in the chem.Atomer. 122 if strings.Contains(chains, string(at.Chain)) || at.Chain == " " || chains == "" { 123 if at.Chain != chainprev { 124 chainprev = at.Chain 125 C = -1 126 N = -1 127 Ca = -1 128 Cprev = -1 129 Npost = -1 130 } 131 if at.Name == "C" && Cprev == -1 { 132 Cprev = num 133 } 134 if at.Name == "N" && Cprev != -1 && N == -1 && at.MolID > M.Atom(Cprev).MolID { 135 N = num 136 } 137 if at.Name == "C" && Cprev != -1 && at.MolID > M.Atom(Cprev).MolID { 138 C = num 139 } 140 if at.Name == "CA" && Cprev != -1 && at.MolID > M.Atom(Cprev).MolID { 141 Ca = num 142 } 143 if at.Name == "N" && Ca != -1 && at.MolID > M.Atom(Ca).MolID { 144 Npost = num 145 } 146 //when we have them all, we save 147 if Cprev != -1 && Ca != -1 && N != -1 && C != -1 && Npost != -1 { 148 //We check that the residue ids are what they are supposed to be 149 r1 := M.Atom(Cprev).MolID 150 r2 := M.Atom(N).MolID 151 r2a := M.Atom(Ca).MolID 152 r2b := M.Atom(C).MolID 153 r3 := M.Atom(Npost).MolID 154 if (len(resran) == 2 && (r2 >= resran[0] && r2 <= resran[1])) || isInInt(resran, r2) { 155 if r1 != r2-1 || r2 != r2a || r2a != r2b || r2b != r3-1 { 156 return nil, CError{fmt.Sprintf("Incorrect backbone Cprev: %d N-1: %d CA: %d C: %d Npost-1: %d", r1, r2-1, r2a, r2b, r3-1), []string{"RamaList"}} 157 } 158 temp := RamaSet{Cprev, N, Ca, C, Npost, r2, M.Atom(Ca).MolName} 159 RamaList = append(RamaList, temp) 160 } 161 N = Npost 162 Ca = -1 163 Cprev = C 164 C = -1 165 Npost = -1 166 } 167 } 168 } 169 // fmt.Println("Rama",Rama, "failed", failed) 170 return RamaList, nil 171 }