github.com/rmera/gochem@v0.7.1/handy.go (about)

     1  /*
     2   * handy.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  
    28  package chem
    29  
    30  import (
    31  	"fmt"
    32  	"math"
    33  	"strings"
    34  
    35  	v3 "github.com/rmera/gochem/v3"
    36  )
    37  
    38  // NegateIndexes, given a set of indexes and the length of a molecule, produces
    39  // a set of all the indexes _not_ in the original set.
    40  func NegateIndexes(indexes []int, length int) []int {
    41  	ret := make([]int, 0, length-len(indexes))
    42  	for i := 0; i < length; i++ {
    43  		if !isInInt(indexes, i) {
    44  			ret = append(ret, i)
    45  
    46  		}
    47  	}
    48  	return ret
    49  }
    50  
    51  const allchains = "*ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    52  
    53  // FixGromacsPDB fixes the problem that Gromacs PDBs have when there are more than 10000 residues
    54  // Gromacs simply restarts the numbering. Since solvents (where this is likely to happen) don't have
    55  // chain ID in Gromacs, it's impossible to distinguish between the water 1 and the water 10001. FixGromacsPDB
    56  // Adds a chain ID to the newly restrated residue that is the letter/symbol coming after the last seen chain ID
    57  // in the constant allchains defined in this file. The current implementation does nothing if a chain ID is already
    58  // defined, even if it is wrong (if 9999 and the following 0 residue have the same chain).
    59  func FixGromacsPDB(mol Atomer) {
    60  	//	fmt.Println("FIXING!")
    61  	previd := 999999
    62  	lastchain := "*"
    63  	for i := 0; i < mol.Len(); i++ {
    64  		at := mol.Atom(i)
    65  		if at.Chain == " " {
    66  			if previd > at.MolID {
    67  				index := strings.Index(allchains, lastchain) + 1
    68  				//	fmt.Println("new chain index:", index)
    69  				lastchain = string(allchains[index])
    70  			}
    71  			at.Chain = lastchain
    72  			//		fmt.Println(lastchain) /////////
    73  		} else {
    74  			lastchain = at.Chain
    75  		}
    76  		previd = at.MolID
    77  	}
    78  }
    79  
    80  // Molecules2Atoms gets a selection list from a list of residues.
    81  // It select all the atoms that form part of the residues in the list.
    82  // It doesnt return errors. If a residue is out of range, no atom will
    83  // be returned for it. Atoms are also required to be part of one of the chains
    84  // specified in chains, but a nil "chains" can be given to select all chains.
    85  func Molecules2Atoms(mol Atomer, residues []int, chains []string) []int {
    86  	atlist := make([]int, 0, len(residues)*3)
    87  	for key := 0; key < mol.Len(); key++ {
    88  		at := mol.Atom(key)
    89  		if isInInt(residues, at.MolID) && (isInString(chains, at.Chain) || len(chains) == 0) {
    90  			atlist = append(atlist, key)
    91  		}
    92  	}
    93  	return atlist
    94  
    95  }
    96  
    97  // EasyShape takes a matrix of coordinates, a value for epsilon (a number close to
    98  // zero, the closer, the more
    99  // strict the orthogonality requriements are) and an (optative) masser and returns
   100  // two shape indicators based on the elipsoid of inertia (or it massless equivalent)
   101  // a linear and circular distortion indicators, and an error or nil (in that order).
   102  // if you give a negative number as epsilon, the default (quite strict) will be used.
   103  func EasyShape(coords *v3.Matrix, epsilon float64, mol ...Masser) (float64, float64, error) {
   104  	var masses []float64
   105  	var err2 error
   106  	var err error
   107  	if len(mol) < 0 {
   108  		masses = nil
   109  	} else {
   110  		masses, err = mol[0].Masses()
   111  		if err != nil {
   112  			masses = nil
   113  			err2 = err
   114  		}
   115  	}
   116  	moment, err := MomentTensor(coords, masses)
   117  	if err != nil {
   118  		return -1, -1, err
   119  	}
   120  	rhos, err := Rhos(moment, epsilon)
   121  	if err != nil {
   122  		return -1, -1, err
   123  	}
   124  	linear, circular, err := RhoShapeIndexes(rhos)
   125  	if err != nil {
   126  		return -1, -1, err
   127  	}
   128  	return linear, circular, err2
   129  }
   130  
   131  // MolIDNameChain2Index takes a molID (residue number), atom name, chain index and a molecule Atomer.
   132  // it returns the index associated with the atom in question in the Ref. The function returns also an error (if failure of warning)
   133  // or nil (if succses and no warnings). Note that this function is not efficient to call several times to retrieve many atoms.
   134  func MolIDNameChain2Index(mol Atomer, molID int, name, chain string) (int, error) {
   135  	var ret int = -1
   136  	var err error
   137  	if mol == nil {
   138  		return -1, CError{"goChem: Given a nil chem.Atomer", []string{"MolIDNameChain2Index"}}
   139  	}
   140  	for i := 0; i != mol.Len(); i++ {
   141  		a := mol.Atom(i)
   142  		if a.Name == "" && err == nil {
   143  			err = CError{"Warning: The Atoms does not seem to contain PDB-type information", []string{"MolIDNameChain2Index"}} //We set this error but will still keep running the function in case the data is present later in the molecule.
   144  		}
   145  		if a.MolID == molID && a.Name == name && a.Chain == chain {
   146  			ret = i
   147  			break
   148  		}
   149  
   150  	}
   151  	if ret == -1 {
   152  		var p string
   153  		if err != nil {
   154  			p = err.Error()
   155  		}
   156  		err = CError{fmt.Sprintf("%s, No atomic index found in the Atomer given for the given MolID, atom name and chain. %s %d", p, chain, molID), []string{"MolIDNameChain2Index"}}
   157  	}
   158  	return ret, err
   159  }
   160  
   161  // OnesMass returns a column matrix with lenght rosw.
   162  // This matrix can be used as a dummy mass matrix
   163  // for geometric calculations.
   164  func OnesMass(lenght int) *v3.Matrix {
   165  	return v3.Dense2Matrix(gnOnes(lenght, 1))
   166  }
   167  
   168  // Super determines the best rotation and translations to superimpose the coords in test
   169  // considering only the atoms present in the slices of int slices indexes.
   170  // The first indexes slices will be assumed to contain test indexes and the second, template indexes.
   171  // If you give only one, it will be assumed to correspond to test, if test has more atoms than
   172  // elements on the indexes set, or templa, otherwise. If no indexes are given, all atoms on each system
   173  // will be superimposed. The number of atoms superimposed on both systems must be equal.
   174  // Super modifies the test matrix, but template and indexes are not touched.
   175  func Super(test, templa *v3.Matrix, indexes ...[]int) (*v3.Matrix, error) {
   176  	var ctest *v3.Matrix
   177  	var ctempla *v3.Matrix
   178  	if len(indexes) == 0 || indexes[0] == nil || len(indexes[0]) == 0 { //If you put the date in the SECOND slice, you are just messing with me.
   179  		ctest = test
   180  		ctempla = templa
   181  	} else if len(indexes) == 1 {
   182  		if test.NVecs() > len(indexes[0]) {
   183  			ctest = v3.Zeros(len(indexes[0]))
   184  			ctest.SomeVecs(test, indexes[0])
   185  			ctempla = templa
   186  		} else if templa.NVecs() > len(indexes[0]) {
   187  			ctempla = v3.Zeros(len(indexes[0]))
   188  			ctempla.SomeVecs(templa, indexes[0])
   189  		} else {
   190  			return nil, fmt.Errorf("chem.Super: Indexes don't match molecules")
   191  		}
   192  	} else {
   193  		ctest = v3.Zeros(len(indexes[0]))
   194  		ctest.SomeVecs(test, indexes[0])
   195  		ctempla = v3.Zeros(len(indexes[1]))
   196  		ctempla.SomeVecs(templa, indexes[1])
   197  	}
   198  
   199  	if ctest.NVecs() != ctempla.NVecs() {
   200  		return nil, fmt.Errorf("chem.Super: Ill formed coordinates for Superposition")
   201  	}
   202  
   203  	_, rotation, trans1, trans2, err1 := RotatorTranslatorToSuper(ctest, ctempla)
   204  	if err1 != nil {
   205  		return nil, errDecorate(err1, "Super")
   206  	}
   207  	test.AddVec(test, trans1)
   208  	//	fmt.Println("test1",test, rotation) /////////////77
   209  	test.Mul(test, rotation)
   210  	//	fmt.Println("test2",test) ///////////
   211  	test.AddVec(test, trans2)
   212  	//	fmt.Println("test3",test) ///////
   213  	return test, nil
   214  }
   215  
   216  // RotateAbout about rotates the coordinates in coordsorig around by angle radians around the axis
   217  // given by the vector axis. It returns the rotated coordsorig, since the original is not affected.
   218  // Uses Clifford algebra.
   219  func RotateAbout(coordsorig, ax1, ax2 *v3.Matrix, angle float64) (*v3.Matrix, error) {
   220  	coordsLen := coordsorig.NVecs()
   221  	coords := v3.Zeros(coordsLen)
   222  	translation := v3.Zeros(ax1.NVecs())
   223  	translation.Copy(ax1)
   224  	axis := v3.Zeros(ax2.NVecs())
   225  	axis.Sub(ax2, ax1) // the rotation axis
   226  	f := func() { coords.SubVec(coordsorig, translation) }
   227  	if err := gnMaybe(gnPanicker(f)); err != nil {
   228  		return nil, CError{err.Error(), []string{"v3.Matrix.SubVec", "RotateAbout"}}
   229  	}
   230  	Rot := v3.Zeros(coordsLen)
   231  	Rot = Rotate(coords, Rot, axis, angle)
   232  	g := func() { Rot.AddVec(Rot, translation) }
   233  	if err := gnMaybe(gnPanicker(g)); err != nil {
   234  		return nil, CError{err.Error(), []string{"v3.Matrix.AddVec", "RotateAbout"}}
   235  
   236  	}
   237  	return Rot, nil
   238  }
   239  
   240  // EulerRotateAbout uses Euler angles to rotate the coordinates in coordsorig around by angle
   241  // radians around the axis given by the vector axis. It returns the rotated coordsorig,
   242  // since the original is not affected. It seems more clunky than the RotateAbout, which uses Clifford algebra.
   243  // I leave it for benchmark, mostly, and might remove it later. There is no test for this function!
   244  func EulerRotateAbout(coordsorig, ax1, ax2 *v3.Matrix, angle float64) (*v3.Matrix, error) {
   245  	r, _ := coordsorig.Dims()
   246  	coords := v3.Zeros(r)
   247  	translation := v3.Zeros(ax1.NVecs())
   248  	translation.Copy(ax1)
   249  	axis := v3.Zeros(ax2.NVecs())
   250  	axis.Sub(ax2, ax1) //now it became the rotation axis
   251  	f := func() { coords.SubVec(coordsorig, translation) }
   252  	if err := gnMaybe(gnPanicker(f)); err != nil {
   253  		return nil, CError{err.Error(), []string{"v3.Matrix.Subvec", "EulerRotateAbout"}}
   254  
   255  	}
   256  	Zswitch := RotatorToNewZ(axis)
   257  	coords.Mul(coords, Zswitch) //rotated
   258  	Zrot, err := RotatorAroundZ(angle)
   259  	if err != nil {
   260  		return nil, errDecorate(err, "EulerRotateAbout")
   261  	}
   262  	//	Zsr, _ := Zswitch.Dims()
   263  	//	RevZ := v3.Zeros(Zsr)
   264  	RevZ, err := gnInverse(Zswitch, nil)
   265  	if err != nil {
   266  		return nil, errDecorate(err, "EulerRotateAbout")
   267  	}
   268  	coords.Mul(coords, Zrot) //rotated
   269  	coords.Mul(coords, RevZ)
   270  	coords.AddVec(coords, translation)
   271  	return coords, nil
   272  }
   273  
   274  // Corrupted is a convenience function to check that a reference and a trajectory have the same number of atoms
   275  func Corrupted(X Traj, R Atomer) error {
   276  	if X.Len() != R.Len() {
   277  		return CError{"Mismatched number of atoms/coordinates", []string{"Corrupted"}}
   278  	}
   279  	return nil
   280  }
   281  
   282  //Some internal convenience functions.
   283  
   284  // isInInt is a helper for the RamaList function,
   285  // returns true if test is in container, false otherwise.
   286  func isInInt(container []int, test int) bool {
   287  	if container == nil {
   288  		return false
   289  	}
   290  	for _, i := range container {
   291  		if test == i {
   292  			return true
   293  		}
   294  	}
   295  	return false
   296  }
   297  
   298  // Same as the previous, but with strings.
   299  func isInString(container []string, test string) bool {
   300  	if container == nil {
   301  		return false
   302  	}
   303  	for _, i := range container {
   304  		if test == i {
   305  			return true
   306  		}
   307  	}
   308  	return false
   309  }
   310  
   311  // MakeWater Creates a water molecule at distance Angstroms from a2, in a direction that is angle radians from the axis defined by a1 and a2.
   312  // Notice that the exact position of the water is not well defined when angle is not zero. One can always use the RotateAbout
   313  // function to move the molecule to the desired location. If oxygen is true, the oxygen will be pointing to a2. Otherwise,
   314  // one of the hydrogens will.
   315  func MakeWater(a1, a2 *v3.Matrix, distance, angle float64, oxygen bool) *v3.Matrix {
   316  	water := v3.Zeros(3)
   317  	const WaterOHDist = 0.96
   318  	const WaterAngle = 52.25
   319  	const deg2rad = 0.0174533
   320  	w := water.VecView(0) //we first set the O coordinates
   321  	w.Copy(a2)
   322  	w.Sub(w, a1)
   323  	w.Unit(w)
   324  	dist := v3.Zeros(1)
   325  	dist.Sub(a1, a2)
   326  	a1a2dist := dist.Norm(2)
   327  	//	fmt.Println("ala2dist", a1a2dist, distance) ////////////////7777
   328  	w.Scale(distance+a1a2dist, w)
   329  	w.Add(w, a1)
   330  	for i := 0; i <= 1; i++ {
   331  		o := water.VecView(0)
   332  		w = water.VecView(i + 1)
   333  		w.Copy(o)
   334  		//		fmt.Println("w1", w) ////////
   335  		w.Sub(w, a2)
   336  		//		fmt.Println("w12", w) ///////////////
   337  		w.Unit(w)
   338  		//		fmt.Println("w4", w)
   339  		w.Scale(WaterOHDist+distance, w)
   340  		//		fmt.Println("w3", w, WaterOHDist, distance)
   341  		o.Sub(o, a2)
   342  		t, _ := v3.NewMatrix([]float64{0, 0, 1})
   343  		upp := v3.Zeros(1)
   344  		upp.Cross(w, t)
   345  		//		fmt.Println("upp", upp, w, t)
   346  		upp.Add(upp, o)
   347  		upp.Add(upp, a2)
   348  		//water.SetMatrix(3,0,upp)
   349  		w.Add(w, a2)
   350  		o.Add(o, a2)
   351  		sign := 1.0
   352  		if i == 1 {
   353  			sign = -1.0
   354  		}
   355  		temp, _ := RotateAbout(w, o, upp, deg2rad*WaterAngle*sign)
   356  		w.SetMatrix(0, 0, temp)
   357  	}
   358  	var v1, v2 *v3.Matrix
   359  	if angle != 0 {
   360  		v1 = v3.Zeros(1)
   361  		v2 = v3.Zeros(1)
   362  		v1.Sub(a2, a1)
   363  		v2.Copy(v1)
   364  		v2.Set(0, 2, v2.At(0, 2)+1) //a "random" modification. The idea is that its not colinear with v1
   365  		v3 := cross(v1, v2)
   366  		v3.Add(v3, a2)
   367  		water, _ = RotateAbout(water, a2, v3, angle)
   368  	}
   369  	if oxygen {
   370  		return water
   371  	}
   372  	//we move things so an hydrogen points to a2 and modify the distance acordingly.
   373  	e1 := water.VecView(0)
   374  	e2 := water.VecView(1)
   375  	e3 := water.VecView(2)
   376  	if v1 == nil {
   377  		v1 = v3.Zeros(1)
   378  	}
   379  	if v2 == nil {
   380  		v2 = v3.Zeros(1)
   381  	}
   382  	v1.Sub(e2, e1)
   383  	v2.Sub(e3, e1)
   384  	axis := cross(v1, v2)
   385  	axis.Add(axis, e1)
   386  	water, _ = RotateAbout(water, e1, axis, deg2rad*(180-WaterAngle))
   387  	v1.Sub(e1, a2)
   388  	v1.Unit(v1)
   389  	v1.Scale(WaterOHDist, v1)
   390  	water.AddVec(water, v1)
   391  	return water
   392  }
   393  
   394  // FixNumbering will put the internal numbering+1 in the atoms and residue fields, so they match the current residues/atoms
   395  // in the molecule
   396  func FixNumbering(r Atomer) {
   397  	resid := 0
   398  	prevres := -1
   399  	for i := 0; i < r.Len(); i++ {
   400  		at := r.Atom(i)
   401  		at.ID = i + 1
   402  		if prevres != at.MolID {
   403  			prevres = at.MolID
   404  			resid++
   405  		}
   406  		at.MolID = resid
   407  	}
   408  }
   409  
   410  // CutBackRef takes a list of lists of residues and selects
   411  // from r all atoms in each the list list[i] and belonging to the chain chain[i].
   412  // It caps the N and C terminal
   413  // of each list with -COH for the N terminal and NH2 for C terminal.
   414  // the residues on each sublist should be contiguous to each other.
   415  // for instance, {6,7,8} is a valid sublist, {6,8,9} is not.
   416  // This is NOT currently checked by the function!. It returns the list of kept atoms
   417  func CutBackRef(r Atomer, chains []string, list [][]int) ([]int, error) {
   418  	//i:=r.Len()
   419  	if len(chains) != len(list) {
   420  		return nil, CError{fmt.Sprintf("Mismatched chains (%d) and list (%d) slices", len(chains), len(list)), []string{"CutBackRef"}}
   421  	}
   422  	var ret []int //This will be filled with the atoms that are kept, and will be returned.
   423  	for k, v := range list {
   424  		nter := v[0]
   425  		cter := v[len(v)-1]
   426  		nresname := ""
   427  		cresname := ""
   428  		for j := 0; j < r.Len(); j++ {
   429  			if r.Atom(j).MolID == nter && r.Atom(j).Chain == chains[k] {
   430  				nresname = r.Atom(j).MolName
   431  				break
   432  			}
   433  		}
   434  		if nresname == "" {
   435  			//we will protest if the Nter is not found. If Cter is not found we will just
   436  			//cut at the real Cter
   437  			return nil, CError{fmt.Sprintf("list %d contains residue numbers out of boundaries", k), []string{"CutBackRef"}}
   438  
   439  		}
   440  		for j := 0; j < r.Len(); j++ {
   441  			curr := r.Atom(j)
   442  			if curr.Chain != chains[k] {
   443  				continue
   444  			}
   445  			if curr.MolID == cter {
   446  				cresname = curr.MolName
   447  			}
   448  			if curr.MolID == nter-1 {
   449  				makeNcap(curr, nresname)
   450  			}
   451  			if curr.MolID == cter+1 {
   452  				makeCcap(curr, cresname)
   453  			}
   454  		}
   455  	}
   456  	for _, i := range list {
   457  		t := Molecules2Atoms(r, i, chains)
   458  		//	fmt.Println("t", len(t))
   459  		ret = append(ret, t...)
   460  	}
   461  	//	j:=0
   462  	//	for i:=0;;i++{
   463  	//		index:=i-j
   464  	//		if index>=r.Len(){
   465  	//			break
   466  	//		}
   467  	//		if !isInInt(ret, i){
   468  	//			r.DelAtom(index)
   469  	//			j++
   470  	//		}
   471  	//	}
   472  	return ret, nil
   473  }
   474  
   475  func makeNcap(at *Atom, resname string) {
   476  	if !isInString([]string{"C", "O", "CA"}, at.Name) {
   477  		return
   478  	}
   479  	at.MolID = at.MolID + 1
   480  	at.MolName = resname
   481  	if at.Name == "C" {
   482  		at.Name = "CTZ"
   483  	}
   484  	if at.Name == "CA" {
   485  		at.Name = "HCZ"
   486  		at.Symbol = "H"
   487  	}
   488  }
   489  
   490  func makeCcap(at *Atom, resname string) {
   491  	if !isInString([]string{"N", "H", "CA"}, at.Name) {
   492  		return
   493  	}
   494  	at.MolID = at.MolID - 1
   495  	at.MolName = resname
   496  	if at.Name == "N" {
   497  		at.Name = "NTZ"
   498  	}
   499  	if at.Name == "CA" {
   500  		at.Name = "HNZ"
   501  		at.Symbol = "H"
   502  	}
   503  }
   504  
   505  /*
   506  //Takes a list of lists of residues and produces a new set of coordinates
   507  //whitout any atom not in the lists or not from the chain chain. It caps the N and C terminal
   508  //of each list with -COH for the N terminal and NH2 for C terminal.
   509  //the residues on each sublist should be contiguous to each other.
   510  //for instance, {6,7,8} is a valid sublist, {6,8,9} is not.
   511  //This is NOT currently checked by the function!
   512  //In addition, the Ref provided should have already been processed by
   513  //CutBackRef, which is not checked either.
   514  func CutBackCoords(r Ref, coords *v3.Matrix, chain string, list [][]int) (*v3.Matrix, error) {
   515  	//this is actually a really silly function. So far I dont check for errors, but I keep the return balue
   516  	//In case I do later.
   517  	var biglist []int
   518  	for _, i := range list {
   519  		smallist := Molecules2Atoms(r, i, []string{chain})
   520  		biglist = append(biglist, smallist...)
   521  	}
   522  	NewVecs := v3.Zeros(len(biglist), 3)
   523  	NewVecs.SomeVecs(coords, biglist)
   524  	return NewVecs, nil
   525  
   526  }
   527  */
   528  
   529  // CutLateralRef will return a list with the atom indexes of the lateral chains of the residues in list
   530  // for each of these residues it will change the alpha carbon to oxygen and change the residue number of the rest
   531  // of the backbone to -1.
   532  func CutBetaRef(r Atomer, chain []string, list []int) []int {
   533  	//	pairs := make([][]int,1,10)
   534  	//	pairs[0]=make([]int,0,2)
   535  	for i := 0; i < r.Len(); i++ {
   536  		curr := r.Atom(i)
   537  		if isInInt(list, curr.MolID) && isInString(chain, curr.Chain) {
   538  			if curr.Name == "CB" {
   539  				//			pairs[len(pairs)-1][1]=i //I am assuming that CA will show before CB in the PDB, which is rather weak
   540  				//		paairs=append(pairs,make([]int,1,2))
   541  			}
   542  			if curr.Name == "CA" {
   543  				curr.Name = "HB4"
   544  				curr.Symbol = "H"
   545  				//		pairs[len(pairs)-1]=append(pairs[len(pairs)-1],i)
   546  			} else if isInString([]string{"C", "H", "HA", "O", "N"}, curr.Name) { //change the res number of the backbone so it is not considered
   547  				curr.MolID = -1
   548  			}
   549  
   550  		}
   551  	}
   552  	newlist := Molecules2Atoms(r, list, chain)
   553  	return newlist
   554  }
   555  
   556  // CutAlphaRef will return a list with the atoms in the residues indicated by list, in the chains given.
   557  // The carbonyl carbon and amide nitrogen for each residue will be transformer into hydrogens. The MolID of the
   558  // other backbone atoms will be set to -1 so they are no longer considered.
   559  func CutAlphaRef(r Atomer, chain []string, list []int) []int {
   560  	for i := 0; i < r.Len(); i++ {
   561  		curr := r.Atom(i)
   562  		if isInInt(list, curr.MolID) && isInString(chain, curr.Chain) {
   563  			if curr.Name == "C" {
   564  				curr.Name = "HA2"
   565  				curr.Symbol = "H"
   566  			} else if curr.Name == "N" {
   567  				curr.Name = "HA3"
   568  				curr.Symbol = "H"
   569  			} else if isInString([]string{"H", "O"}, curr.Name) { //change the res number of the backbone so it is not considered
   570  				curr.MolID = -1
   571  			}
   572  
   573  		}
   574  	}
   575  	newlist := Molecules2Atoms(r, list, chain)
   576  	return newlist
   577  }
   578  
   579  // TagAtomsByName will tag all atoms with a given name in a given list of atoms.
   580  // return the number of tagged atoms
   581  func TagAtomsByName(r Atomer, name string, list []int) int {
   582  	tag := 0
   583  	for i := 0; i < r.Len(); i++ {
   584  		curr := r.Atom(i)
   585  		if isInInt(list, i) && curr.Name == name {
   586  			curr.Tag = 1
   587  			tag++
   588  		}
   589  	}
   590  	return tag
   591  }
   592  
   593  // ScaleBonds scales all bonds between atoms in the same residue with names n1, n2 to a final lenght finallengt, by moving the atoms n2.
   594  // the o<ration is executed in place.
   595  func ScaleBonds(coords *v3.Matrix, mol Atomer, n1, n2 string, finallenght float64) {
   596  	for i := 0; i < mol.Len(); i++ {
   597  		c1 := mol.Atom(i)
   598  		if c1.Name != n1 {
   599  			continue
   600  		}
   601  		for j := 0; j < mol.Len(); j++ {
   602  			c2 := mol.Atom(j)
   603  			if c1.MolID == c2.MolID && c1.Name == n1 && c2.Name == n2 {
   604  				A := coords.VecView(i)
   605  				B := coords.VecView(j)
   606  				ScaleBond(A, B, finallenght)
   607  			}
   608  		}
   609  	}
   610  }
   611  
   612  // ScaleBond takes a C-H bond and moves the H (in place) so the distance between them is the one given (bond).
   613  // CAUTION: I have only tested it for the case where the original distance>bond, although I expect it to also work in the other case.
   614  func ScaleBond(C, H *v3.Matrix, bond float64) {
   615  	Odist := v3.Zeros(1)
   616  	Odist.Sub(H, C)
   617  	distance := Odist.Norm(2)
   618  	Odist.Scale((distance-bond)/distance, Odist)
   619  	H.Sub(H, Odist)
   620  }
   621  
   622  // Merges A and B in a single topology which is returned
   623  func MergeAtomers(A, B Atomer) *Topology {
   624  	al := A.Len()
   625  	l := al + B.Len()
   626  	full := make([]*Atom, l, l)
   627  	for k, _ := range full {
   628  		if k < al {
   629  			full[k] = A.Atom(k)
   630  		} else {
   631  			full[k] = B.Atom(k - al)
   632  		}
   633  	}
   634  	a, aok := A.(AtomMultiCharger)
   635  	b, bok := B.(AtomMultiCharger)
   636  	var charge, multi int
   637  	if aok && bok {
   638  		charge = a.Charge() + b.Charge()
   639  		multi = (a.Multi() - 1 + b.Multi()) //Not TOO sure about this.
   640  	} else {
   641  		multi = 1
   642  	}
   643  	return NewTopology(charge, multi, full)
   644  }
   645  
   646  // SelCone, Given a set of cartesian points in sellist, obtains a vector "plane" normal to the best plane passing through the points.
   647  // It selects atoms from the set A that are inside a cone in the direction of "plane" that starts from the geometric center of the cartesian points,
   648  // and has an angle of angle (radians), up to a distance distance. The cone is approximated by a set of radius-increasing cilinders with height thickness.
   649  // If one starts from one given point, 2 cgnOnes, one in each direction, are possible. If whatcone is 0, both cgnOnes are considered.
   650  // if whatcone<0, only the cone opposite to the plane vector direction. If whatcone>0, only the cone in the plane vector direction.
   651  // the 'initial' argument  allows the construction of a truncate cone with a radius of initial.
   652  func SelCone(B, selection *v3.Matrix, angle, distance, thickness, initial float64, whatcone int) []int {
   653  	A := v3.Zeros(B.NVecs())
   654  	A.Copy(B) //We will be altering the input so its better to work with a copy.
   655  	ar, _ := A.Dims()
   656  	selected := make([]int, 0, 3)
   657  	neverselected := make([]int, 0, 30000)     //waters that are too far to ever be selected
   658  	nevercutoff := distance / math.Cos(angle)  //cutoff to be added to neverselected
   659  	A, _, err := MassCenter(A, selection, nil) //Centrate A in the geometric center of the selection, Its easier for the following calculations
   660  	if err != nil {
   661  		panic(PanicMsg(err.Error()))
   662  	}
   663  	selection, _, _ = MassCenter(selection, selection, nil) //Centrate the selection as well
   664  	plane, err := BestPlane(selection, nil)                 //I have NO idea which direction will this vector point. We might need its negative.
   665  	if err != nil {
   666  		panic(PanicMsg(err.Error()))
   667  	}
   668  	for i := thickness / 2; i <= distance; i += thickness {
   669  		maxdist := math.Tan(angle)*i + initial //this should give me the radius of the cone at this point
   670  		for j := 0; j < ar; j++ {
   671  			if isInInt(selected, j) || isInInt(neverselected, j) { //we dont scan things that we have already selected, or are too far
   672  				continue
   673  			}
   674  			atom := A.VecView(j)
   675  			proj := Projection(atom, plane)
   676  			norm := proj.Norm(2)
   677  			//Now at what side of the plane is the atom?
   678  			angle := Angle(atom, plane)
   679  			if whatcone > 0 {
   680  				if angle > math.Pi/2 {
   681  					continue
   682  				}
   683  			} else if whatcone < 0 {
   684  				if angle < math.Pi/2 {
   685  					continue
   686  				}
   687  			}
   688  			if norm > i+(thickness/2.0) || norm < (i-thickness/2.0) {
   689  				continue
   690  			}
   691  			proj.Sub(proj, atom)
   692  			projnorm := proj.Norm(2)
   693  			if projnorm <= maxdist {
   694  				selected = append(selected, j)
   695  			}
   696  			if projnorm >= nevercutoff {
   697  				neverselected = append(neverselected, j)
   698  			}
   699  		}
   700  	}
   701  	return selected
   702  }