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  }