File Coverage

lib/SmotifTF/GeometricalCalculations.pm
Criterion Covered Total %
statement 49 840 5.8
branch 0 248 0.0
condition 0 20 0.0
subroutine 16 58 27.5
pod n/a
total 65 1166 5.5


line stmt bran cond sub pod time code
1             package SmotifTF::GeometricalCalculations;
2              
3 1     1   14 use 5.8.8;
  1         3  
4 1     1   5 use strict;
  1         1  
  1         25  
5 1     1   4 use warnings;
  1         1  
  1         30  
6             BEGIN {
7 1     1   5 use Exporter ();
  1         1  
  1         122  
8 1     1   2 our ( $VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
9 1         2 $VERSION = "0.01";
10              
11 1         9 @ISA = qw(Exporter);
12              
13             # name of the functions to export
14 1         11 @EXPORT_OK = qw(
15             calc_geom
16             exist_pdb_code_on_obsoletes
17             exist_pdb_code_on_uncompressed
18             exist_pdb_code_on_alternate
19             get_full_path_name_for_pdb_code
20             get_full_path_name_for_pdb_code_alternate
21             _getReadingFileHandle
22             get_from_file
23             translate
24             convert
25             COM
26             COM2
27             calculate_axis
28             get_axis
29             calc_I
30             calc_evec
31             projectpoint
32             det
33             find_eigs
34             matmul
35             matvec
36             vecvec
37             find_roots
38             rotateaxis
39             rotate
40             cross
41             dot
42             unit
43             norm
44             vecadd
45             max
46             min
47             dbin
48             angbin
49             pearson
50             find_rmsd
51             superpose
52             dihedral
53             findcb
54             findo
55             pointsonsphere
56             norm2
57             );
58              
59 1         40 @EXPORT = qw(
60             ); # symbols to export on request
61             }
62             our @EXPORT_OK;
63             our $DEBUG = 0;
64              
65 1     1   3806 use Math::Trig;
  1         33219  
  1         196  
66 1     1   1102 use Storable qw(dclone);
  1         3605  
  1         75  
67 1     1   7 use Carp;
  1         1  
  1         62  
68 1     1   1965 use IO::Uncompress::Gunzip qw(gunzip);
  1         59371  
  1         72  
69 1     1   19 use File::Spec::Functions qw(catfile);
  1         2  
  1         48  
70              
71 1     1   1103 use Config::Simple;
  1         19987  
  1         15  
72             # accessing a block of an ini-file;
73             my $config_file = $ENV{'SMOTIFTF_CONFIG_FILE'};
74             croak "Environmental variable SMOTIFTF_CONFIG_FILE should be set" unless $config_file;
75              
76             my $cfg = new Config::Simple($config_file);
77             my $PDB = $cfg->param( -block => 'pdb' );
78             my $PDB_DIR = $PDB->{'uncompressed'};
79             my $PDB_OBSOLETES = $PDB->{'obsoletes'};
80             my $USER_SPECIFIC_PDB_PATH = $PDB->{'user_specific_pdb_path'};
81              
82             my $host = "";
83              
84             =head1 NAME
85              
86             GeometricalCalculations
87              
88             =head1 VERSION
89              
90             Version 0.01
91              
92             =cut
93              
94             our $VERSION = '0.01';
95              
96              
97             =head1 SYNOPSIS
98              
99             This module consists of subroutines that carry out geometrical calculations
100             using the atomic coordinates obtained from a protein structure.
101              
102             =head2 calc_geom
103            
104             subroutine to calculate delta, theta, and rho angles for an smotif
105            
106             Input 1: $lvec - vector between the anchor points of the bracing secondary structures
107             Input 2: $e1 - axis vector for 1st secondary structure
108             Input 3: $e2 - axis vector for 2nd secondary structure
109              
110             =cut
111             sub calc_geom {
112 0     0     my ( $lvec, $e1, $e2 ) = @_;
113 0           my $rad = 180 / 3.14159265;
114 0           my $delta = ( Math::Trig::acos( dot( @$e1, @$lvec ) ) ) * $rad;
115 0           my $theta = ( Math::Trig::acos( dot( @$e1, @$e2 ) ) ) * $rad;
116 0           my $rho = 0;
117 0           my @normal = unit( cross( @$lvec, @$e1 ) );
118 0           my @target = cross( @$e1, @normal );
119 0           my $check = norm(@target);
120 0 0         if ( $check ne 0 ) {
121 0           my $proj = dot( @$e1, @$e2 );
122 0           my @proj = (
123             $$e2[0] - $proj * $$e1[0],
124             $$e2[1] - $proj * $$e1[1],
125             $$e2[2] - $proj * $$e1[2]
126             );
127 0           my $dproj = norm(@proj);
128 0 0         if ( $dproj ne 0 ) {
129 0           $rho = Math::Trig::acos( dot( @proj, @normal ) / $dproj ) * $rad;
130 0 0         if ( dot( @proj, @target ) < 0 ) { $rho = 360 - $rho }
  0            
131             }
132             }
133 0           return ( $delta, $theta, $rho );
134             }
135              
136             =head2 _getReadingFileHandle
137             subroutine to get a reading file handle
138             for a file.
139              
140             input: file path name to the file
141             =cut
142             sub _getReadingFileHandle {
143 1     1   414 use IO::Uncompress::Gunzip qw(gunzip);
  1         2  
  1         861  
144              
145 0     0     my $file = shift;
146 0 0         die "path_name files is required" unless $file;
147              
148 0           my $fh;
149 0 0         if ( $file =~ /\.gz$/ ) {
150 0   0       $fh = new IO::Uncompress::Gunzip $file || die("Cannot open $file");
151             }
152             else {
153 0 0         open( $fh, "< $file" ) || die("Cannot open $file");
154             }
155 0           return $fh;
156             }
157              
158             =head2 exist_pdb_code_on_obsoletes
159             check if a four-letters pdb code exist in PDB obsoletes folder
160             it will return the full path name if a match is found.
161             return undef if not file was found
162            
163             exist_pdb_code_on_obsoletes
164             input : pdb_code (4fab), chain_id
165             return : full path name
166              
167             =cut
168             sub exist_pdb_code_on_obsoletes {
169 0     0     my ($pdb_code, $chain) = @_;
170 0 0         croak "exist_pdb_code_on_obsoletes: pdb_code is required" unless $pdb_code;
171 0 0         croak "exist_pdb_code_on_obsoletes: chain is required" unless defined $chain;
172              
173 0           chomp $pdb_code;
174 0 0         croak "$pdb_code does not look like a four-letter PDB_CODE"
175             unless $pdb_code =~ /[A-z0-9]{4}/;
176              
177 0           my $pdb_file_name = "pdb" . $pdb_code . '.ent';
178 0           my $pdb_file_name_gz = "pdb" . $pdb_code . '.ent.gz';
179              
180 0           my $pdb_file_name_ch = 'pdb' . $pdb_code . $chain . '.ent';
181 0           my $pdb_file_name_ch_gz = 'pdb' . $pdb_code . $chain . '.ent.gz';
182              
183 0           my $uncompressed = catfile ($PDB_OBSOLETES, $pdb_file_name);
184 0           my $compressed = catfile ($PDB_OBSOLETES, $pdb_file_name_gz);
185              
186 0           my $uncompressed_ch = catfile ($PDB_OBSOLETES, $pdb_file_name_ch);
187 0           my $compressed_ch = catfile ($PDB_OBSOLETES, $pdb_file_name_ch_gz);
188              
189             # try uncompressed pdb filename existence
190 0 0         if ( -e $uncompressed ) {
    0          
    0          
    0          
191 0           return $uncompressed;
192             }
193             # try compressed file
194             elsif ( -e $compressed ) {
195 0           return $compressed;
196             }
197             # try uncompressed file with chain
198             elsif ( -e $uncompressed_ch ) {
199 0           return $uncompressed_ch;
200             }
201             # try compressed file with chain
202             elsif ( -e $compressed_ch ) {
203 0           return $compressed_ch;
204             }
205             else {
206 0           return undef;
207             }
208             }
209              
210             =head2 exist_pdb_code_on_uncompressed
211              
212             check if a four-letters pdb code exist in uncompressed PDB folder
213             it will return the full path name if a match is found.
214             Return undef if not file was found
215            
216             input : pdb_code (4fab), chain_id
217             return : full path name
218              
219             =cut
220             sub exist_pdb_code_on_uncompressed {
221 0     0     my ($pdb_code, $chain) = @_;
222 0 0         croak "exist_pdb_code_on_uncompressed: pdb_code is required" unless $pdb_code;
223 0 0         croak "exist_pdb_code_on_uncompressed: chain is required" unless defined $chain;
224              
225 0           chomp $pdb_code;
226 0 0         croak "$pdb_code does not look like a four-letter PDB_CODE"
227             unless $pdb_code =~ /[A-z0-9]{4}/;
228              
229 0           my $pdb_file_name = "pdb" . $pdb_code . '.ent';
230 0           my $pdb_file_name_gz = "pdb" . $pdb_code . '.ent.gz';
231              
232 0           my $pdb_file_name_ch = 'pdb' . $pdb_code . $chain . '.ent';
233 0           my $pdb_file_name_ch_gz = 'pdb' . $pdb_code . $chain . '.ent.gz';
234              
235 0           my $uncompressed = catfile ($PDB_DIR, $pdb_file_name);
236 0           my $compressed = catfile ($PDB_DIR, $pdb_file_name_gz);
237              
238 0           my $uncompressed_ch = catfile ($PDB_DIR, $pdb_file_name_ch);
239 0           my $compressed_ch = catfile ($PDB_DIR, $pdb_file_name_ch_gz);
240              
241             # try uncompressed pdb filename existence
242 0 0         if ( -e $uncompressed ) {
    0          
    0          
    0          
243 0           return $uncompressed;
244             }
245             # try compressed file
246             elsif ( -e $compressed ) {
247 0           return $compressed;
248             }
249             # try uncompressed file with chain
250             elsif ( -e $uncompressed_ch ) {
251 0           return $uncompressed_ch;
252             }
253             #try compressed file with chain
254             elsif ( -e $compressed_ch ) {
255 0           return $compressed_ch;
256             }
257             else {
258 0           return undef;
259             }
260             }
261              
262             =head2 exist_pdb_code_on_alternate
263              
264             check if a four-letters pdb code exist in user defined alternate
265             PDB folder
266             it will return the full path name if a match is found.
267             Return undef if not file was found
268            
269             input : pdb_code (4fab), chain_id
270             return : full path name
271              
272             =cut
273             sub exist_pdb_code_on_alternate {
274 0     0     my ($pdb_code, $chain) = @_;
275 0 0         croak "exist_pdb_code_on_alternate: pdb_code is required" unless $pdb_code;
276 0 0         croak "exist_pdb_code_on_alternate: chain is required" unless defined $chain;
277              
278 0           chomp $pdb_code;
279 0 0         croak "$pdb_code does not look like a four-letter PDB_CODE"
280             unless $pdb_code =~ /[A-z0-9]{4}/;
281              
282 0           my $pdb_file_name = 'pdb' . $pdb_code . '.ent';
283 0           my $pdb_file_name_gz = 'pdb' . $pdb_code . '.ent.gz';
284            
285 0           my $pdb_file_name_ch = 'pdb' . $pdb_code . $chain . '.ent';
286 0           my $pdb_file_name_ch_gz = 'pdb' . $pdb_code . $chain . '.ent.gz';
287              
288 0           my $uncompressed = catfile ($USER_SPECIFIC_PDB_PATH, $pdb_file_name);
289 0           my $compressed = catfile ($USER_SPECIFIC_PDB_PATH, $pdb_file_name_gz);
290              
291 0           my $uncompressed_ch = catfile ($USER_SPECIFIC_PDB_PATH, $pdb_file_name_ch);
292 0           my $compressed_ch = catfile ($USER_SPECIFIC_PDB_PATH, $pdb_file_name_ch_gz);
293              
294             # try uncompressed pdb filename existence
295 0 0         if ( -e $uncompressed ) {
    0          
    0          
    0          
296 0           return $uncompressed;
297             }
298             # try compressed file
299             elsif ( -e $compressed ) {
300 0           return $compressed;
301             }
302             #try uncompressed file with chain
303             elsif ( -e $uncompressed_ch ) {
304 0           return $uncompressed_ch;
305             }
306             #try compressed file with chain
307             elsif ( -e $compressed_ch ) {
308 0           return $compressed_ch;
309             }
310             else {
311 0           return undef;
312             }
313             }
314              
315             =head2 get_full_path_name_for_pdb_code_alternate
316             Return a fullpath name for a given four-letter PDB code
317             It will look in USER_SPECIFIC PDB directory
318              
319             die if not match found
320              
321             =cut
322             sub get_full_path_name_for_pdb_code_alternate {
323 1     1   2852 use Data::Dumper;
  1         6296  
  1         269  
324 0     0     my ($pdb_code, $chain) = @_;
325            
326 0 0         if ($DEBUG){
327 0           my ($package, $filename, $line) = caller;
328 0           print "package = $package\n";
329 0           print "filename = $filename\n";
330 0           print "line = $line\n";
331 0           print Dumper(\@_);
332             }
333              
334 0 0         croak "get_full_path_name_for_pdb_code_alternate: pdb_code is required" unless $pdb_code;
335 0 0         croak "get_full_path_name_for_pdb_code_alternate: Chain ID is required" unless $chain;
336              
337 0           chomp $pdb_code;
338 0 0         croak "$pdb_code does not look like a four-letter PDB_CODE"
339             unless $pdb_code =~ /[A-z0-9]{4}/;
340              
341 0           my $path;
342            
343 0           $path = exist_pdb_code_on_alternate($pdb_code, $chain);
344 0 0         return $path if $path;
345              
346 0 0         croak "Could not find $pdb_code $chain on $USER_SPECIFIC_PDB_PATH"
347             unless $path;
348             }
349              
350             =head2 get_full_path_name_for_pdb_code
351             Return a fullpath name for a given four-letter PDB code
352             It will look in obsoletes and uncompressed directories
353              
354             die if not match found
355              
356             =cut
357             sub get_full_path_name_for_pdb_code {
358 1     1   7 use Data::Dumper;
  1         2  
  1         404  
359 0     0     my ($pdb_code, $chain) = @_;
360              
361 0 0         if ($DEBUG){
362 0           my ($package, $filename, $line) = caller;
363 0           print "package = $package\n";
364 0           print "filename = $filename\n";
365 0           print "line = $line\n";
366 0           print Dumper(\@_);
367             }
368              
369 0 0         croak "get_full_path_name_for_pdb_code: pdb_code is required" unless $pdb_code;
370 0 0         croak "get_full_path_name_for_pdb_code: pdb_code is required" unless defined $chain;
371              
372 0           chomp $pdb_code;
373 0 0         croak "$pdb_code does not look like a four-letter PDB_CODE"
374             unless $pdb_code =~ /[A-z0-9]{4}/;
375              
376 0           my $path;
377 0           $path = exist_pdb_code_on_uncompressed($pdb_code, $chain);
378 0 0         return $path if $path;
379              
380 0           $path = exist_pdb_code_on_obsoletes($pdb_code, $chain);
381 0 0         return $path if $path;
382              
383 0           $path = exist_pdb_code_on_alternate($pdb_code, $chain);
384 0 0         return $path if $path;
385              
386 0 0         croak "Could not find $pdb_code on $PDB_DIR or $PDB_OBSOLETES or $USER_SPECIFIC_PDB_PATH"
387             unless $path;
388             }
389              
390             =head2 get_from_file
391              
392             subroutine to read PDB file and obtain coordinates of backbone atoms.
393            
394             Input 1: @$data - smotif info (pdb ID, chain ID, start residue, ss1 length, ss2 length, loop length)
395             Input/Output 2: @$landmarks - output array with smotif landmarks (0, loop start, ss2 start, ss2 end)
396             Input 3: $string - backbone atom (CA, CB, C, N, or O)
397             Input/Output 4: $seq - smotif sequence
398            
399             $VAR1 = [
400             '/usr/local/databases/pdb//uncompressed/pdb2kl8.ent.gz',
401             'A',
402             2,
403             7,
404             17,
405             3,
406             0,
407             'EH'
408             ];
409              
410              
411             =cut
412             sub get_from_file {
413 0     0     my ( $data, $landmarks, $string, $seq ) = @_;
414              
415             # data contains pdbid, chain, start, ss1, ss2
416 1     1   7 use Data::Dumper;
  1         2  
  1         43  
417 1     1   5 use File::Basename;
  1         2  
  1         12346  
418             #print Dumper($data);
419              
420 0           my $chain = $$data[1];
421 0           my $startres = $$data[2];
422 0           my $endres = $$data[2] + $$data[5] + $$data[3] + $$data[4] - 1;
423 0           @$landmarks =
424             ( $$data[3], $$data[3] + $$data[5], $$data[3] + $$data[5] + $$data[4] );
425              
426             # $$data[0] has values like this:
427             # 2kl8/pdb2kl8.ent
428 0           my $pdb_code;
429            
430 0 0         if ( $$data[0] =~ /\// ) {
431             # $pdb_code = ( split( /\//, $$data[0] ) )[0];
432 0           my $filename = basename $$data[0];
433 0 0         if ( $filename =~ /pdb(\w{4}).*/ ) {
    0          
434 0           $pdb_code = $1;
435             }
436             elsif ( $filename =~ /(\w{4}).*/ ) {
437 0           $pdb_code = $1;
438             }
439             else {}
440             }
441             else {
442 0           $pdb_code = $$data[0];
443 0           $pdb_code =~ s/^pdb//g; # removing leading pdb string
444 0           $pdb_code =~ s/\.(\w+)$//g; # removing file extension
445             }
446            
447             #unless ( $$data[0] ) {
448             # use Data::Dumper;
449             # print "pdb_code $pdb_code\n";
450             # print Dumper($data);
451             # #croak "get_from_file: pdb_code is required";
452             #}
453              
454              
455 0           my $full_path_name = get_full_path_name_for_pdb_code($pdb_code, $chain);
456 0           my $fh = _getReadingFileHandle($full_path_name);
457              
458 0           my $done = 0;
459 0           my @coords = ();
460 0           my $prevres = 0;
461 0           my $len = length($string);
462 0           my $id = $prevres;
463 0 0         if ( $string ne 'CB' )
464             { #certain residues (GLY) will not have CBs, so this case has to be treated separately
465 0   0       while ( defined( my $line = <$fh> ) and ( $done == 0 ) ) {
466 0           chomp($line);
467 0 0         if ( $line =~
468             /ATOM\s+-*\d+\s+$string\s+(\w+)\s$chain\s*(-*\d+\w?\s)/ )
469             {
470 0           $id = $2;
471 0           my $amino = $1;
472 0 0         if ( $id =~ /(-*\d+)\D/ ) { $id = $1 }
  0            
473 0 0         if ( $id ne $prevres ) {
474 0 0         if ( $id > $endres ) {
    0          
475 0           $done = 1;
476             }
477             elsif ( $id >= $startres ) {
478 0           push(
479             @coords,
480             [
481             substr( $line, 30, 8 ),
482             substr( $line, 38, 8 ),
483             substr( $line, 46, 8 )
484             ]
485             );
486              
487             #print "$line\t$amino\n";
488 0           $$seq = $$seq . translate($amino);
489              
490             #print "$line\n";
491             }
492             }
493 0           $prevres = $id;
494             }
495             }
496             }
497             else {
498 0           $$seq = '';
499 0           my $prevline = <$fh>;
500 0   0       while ( defined( my $line = <$fh> ) and ( $done == 0 ) ) {
501 0           chomp($line);
502 0 0         if ( $line =~ /ATOM\s+-*\d+\s+CB\s+(\w+)\s$chain\s*(-*\d+\w?\s)/ ) {
    0          
503 0           $id = $2;
504 0           my $amin = substr( $1, -3, 3 );
505 0 0         if ( $id =~ /(-*\d+)\D/ ) { $id = $1 }
  0            
506 0 0         if ( $id ne $prevres ) {
507 0 0         if ( $id > $endres ) {
    0          
508 0           $done = 1;
509             }
510             elsif ( $id >= $startres ) {
511 0           push(
512             @coords,
513             [
514             substr( $line, 30, 8 ),
515             substr( $line, 38, 8 ),
516             substr( $line, 46, 8 )
517             ]
518             );
519 0           $$seq = $$seq . translate($amin);
520              
521             #print "$line\t$startres\t$endres\n";
522             }
523             }
524 0           $prevres = $id;
525             }
526             elsif (
527             $prevline =~ /ATOM\s+-*\d+\s+O\s+(\w+)\s$chain\s*(-*\d+\w?\s)/ )
528             {
529 0           $id = $2;
530 0           my $amin = substr( $1, -3, 3 );
531 0 0         if ( $id =~ /(-*\d+)\D/ ) { $id = $1 }
  0            
532 0 0         if ( $id ne $prevres ) {
533 0 0         if ( $id > $endres ) {
    0          
534 0           $done = 1;
535             }
536             elsif ( $id >= $startres ) {
537 0           push( @coords, 0 );
538 0           $$seq = $$seq . translate($amin);
539             }
540             }
541 0           $prevres = $id;
542             }
543 0           $prevline = $line;
544             }
545             }
546 0           close($fh);
547 0           return @coords;
548             }
549              
550             =head2
551              
552             translate
553             subroutine to convert from 3-letter codes to 1-letter codes
554              
555             =cut
556             sub translate {
557 0     0     my ($inp) = @_;
558 0           my $out;
559 0 0         if ( $inp eq 'ALA' ) { $out = 'A' }
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
560 0           elsif ( $inp eq 'CYS' ) { $out = 'C' }
561 0           elsif ( $inp eq 'ASP' ) { $out = 'D' }
562 0           elsif ( $inp eq 'GLU' ) { $out = 'E' }
563 0           elsif ( $inp eq 'PHE' ) { $out = 'F' }
564 0           elsif ( $inp eq 'GLY' ) { $out = 'G' }
565 0           elsif ( $inp eq 'HIS' ) { $out = 'H' }
566 0           elsif ( $inp eq 'ILE' ) { $out = 'I' }
567 0           elsif ( $inp eq 'LYS' ) { $out = 'K' }
568 0           elsif ( $inp eq 'LEU' ) { $out = 'L' }
569 0           elsif ( $inp eq 'MET' ) { $out = 'M' }
570 0           elsif ( $inp eq 'ASN' ) { $out = 'N' }
571 0           elsif ( $inp eq 'PRO' ) { $out = 'P' }
572 0           elsif ( $inp eq 'GLN' ) { $out = 'Q' }
573 0           elsif ( $inp eq 'ARG' ) { $out = 'R' }
574 0           elsif ( $inp eq 'SER' ) { $out = 'S' }
575 0           elsif ( $inp eq 'THR' ) { $out = 'T' }
576 0           elsif ( $inp eq 'VAL' ) { $out = 'V' }
577 0           elsif ( $inp eq 'TRP' ) { $out = 'W' }
578 0           else { $out = 'Y' }
579 0           return $out;
580             }
581              
582             =head2 convert
583              
584             subroutine to convert from 1-letter codes to 3-letter codes
585              
586             =cut
587             sub convert {
588 0     0     my ($aa) = @_;
589 0 0         if ( $aa eq 'A' ) { return 'ALA' }
  0            
590 0 0         if ( $aa eq 'C' ) { return 'CYS' }
  0            
591 0 0         if ( $aa eq 'D' ) { return 'ASP' }
  0            
592 0 0         if ( $aa eq 'E' ) { return 'GLU' }
  0            
593 0 0         if ( $aa eq 'F' ) { return 'PHE' }
  0            
594 0 0         if ( $aa eq 'G' ) { return 'GLY' }
  0            
595 0 0         if ( $aa eq 'H' ) { return 'HIS' }
  0            
596 0 0         if ( $aa eq 'I' ) { return 'ILE' }
  0            
597 0 0         if ( $aa eq 'K' ) { return 'LYS' }
  0            
598 0 0         if ( $aa eq 'L' ) { return 'LEU' }
  0            
599 0 0         if ( $aa eq 'M' ) { return 'MET' }
  0            
600 0 0         if ( $aa eq 'N' ) { return 'ASN' }
  0            
601 0 0         if ( $aa eq 'P' ) { return 'PRO' }
  0            
602 0 0         if ( $aa eq 'Q' ) { return 'GLN' }
  0            
603 0 0         if ( $aa eq 'R' ) { return 'ARG' }
  0            
604 0 0         if ( $aa eq 'S' ) { return 'SER' }
  0            
605 0 0         if ( $aa eq 'T' ) { return 'THR' }
  0            
606 0 0         if ( $aa eq 'V' ) { return 'VAL' }
  0            
607 0 0         if ( $aa eq 'W' ) { return 'TRP' }
  0            
608 0 0         if ( $aa eq 'Y' ) { return 'TYR' }
  0            
609             }
610              
611             =head2 COM
612              
613             subroutine to find the centre of mass of a set of points, presented in 3 vectors
614              
615             =cut
616             sub COM {
617 0     0     my ( $start, $end, $ca, $n, $c ) = @_;
618 0           my @tots = ( 0, 0, 0 );
619 0           my $count = 3 * ( $end - $start );
620 0           for ( my $a = $start ; $a < $end ; $a++ ) {
621              
622             # print $$ca[$a][0], "\n";
623 0           $tots[0] = $tots[0] + $$ca[$a][0] + $$n[$a][0] + $$c[$a][0];
624 0           $tots[1] = $tots[1] + $$ca[$a][1] + $$n[$a][1] + $$c[$a][1];
625 0           $tots[2] = $tots[2] + $$ca[$a][2] + $$n[$a][2] + $$c[$a][2];
626             }
627 0           return ( $tots[0] / $count, $tots[1] / $count, $tots[2] / $count );
628             }
629              
630             =head2 COM2
631              
632             subroutine to find the centre of mass of a set of points, presented as a single vector
633              
634             =cut
635             sub COM2 {
636 0     0     my ( $start, $end, $c ) = @_;
637 0           my @tots = ( 0, 0, 0 );
638 0           my $count = $end - $start;
639 0           for ( my $a = $start ; $a < $end ; $a++ ) {
640 0           $tots[0] += $$c[$a][0];
641 0           $tots[1] += $$c[$a][1];
642 0           $tots[2] += $$c[$a][2];
643             }
644 0           return ( $tots[0] / $count, $tots[1] / $count, $tots[2] / $count );
645             }
646              
647             =head2 calculate_axis
648              
649             subroutine to calculate the principal axis of a set of points
650             (corresponds to the principal eigenvector for the moment matrix)
651              
652             =cut
653             sub calculate_axis {
654 0     0     my ( $type, $ca, $n, $c ) = @_;
655 0           my @ca2 = @$ca;
656 0           my @n2 = @$n;
657 0           my @c2 = @$c;
658 0           my $count = scalar(@ca2);
659 0           my @com = COM( 0, $count, \@ca2, \@n2, \@c2 );
660 0           for ( my $a = 0 ; $a < $count ; $a++ ) {
661 0           $ca2[$a][0] -= $com[0];
662 0           $ca2[$a][1] -= $com[1];
663 0           $ca2[$a][2] -= $com[2];
664 0           $n2[$a][0] -= $com[0];
665 0           $n2[$a][1] -= $com[1];
666 0           $n2[$a][2] -= $com[2];
667 0           $c2[$a][0] -= $com[0];
668 0           $c2[$a][1] -= $com[1];
669 0           $c2[$a][2] -= $com[2];
670             }
671 0           my @p = ( [@ca2], [@n2], [@c2] );
672              
673             #find moment of inertia matrix
674 0           my @I = calc_I( $type, $count, @p );
675              
676             #find principal eigenvector
677 0           my @eigs = find_eigs(@I);
678 0           my @evec = calc_evec( $eigs[2], @I );
679              
680             #check direction of axis by looking at max eigenvec, difference
681 0           my $i = 2;
682 0 0 0       if ( ( abs( $evec[0] ) >= abs( $evec[1] ) )
    0          
683             and ( abs( $evec[0] ) >= abs( $evec[2] ) ) )
684             { #x-coord is max
685 0           $i = 0;
686             }
687             elsif ( abs( $evec[1] ) >= abs( $evec[2] ) ) { #y-coord is max
688 0           $i = 1;
689             }
690 0 0         if ( ( $evec[$i] * ( $p[0][-1][$i] - $p[0][0][$i] ) ) < 0 ) {
691 0           return ( -$evec[0], -$evec[1], -$evec[2] );
692             }
693             else {
694 0           return @evec;
695             }
696             }
697              
698             =head2 get_axis
699             subroutine to find the axis vector of a secondary structure,
700             using at least 4 residues for a helix or 2 for a strand
701            
702             Input 1: Type of secondary structure (H=Helix, E=strand)
703             Input 2: Option to indicate whether the secondary structure is the first (1) or second (2) part of the smotif
704             Input 3: Initial residue number of the secondary structure
705             Input 4: Final residue number of the secondar structure
706             Input 5-7: Vectors containing the coordinates of C-alpha atoms, N atoms, and C atoms of the smotif
707              
708             =cut
709             sub get_axis {
710 0     0     my ( $type, $ss, $first, $last, $ca, $n, $c ) = @_;
711 0           my $ang = 0;
712 0           my @newaxis;
713             my @oldaxis;
714 0           my $j;
715 0           my @use_ca;
716 0           my @use_n;
717 0           my @use_c;
718 0           my $count = $last - $first;
719 0           my $beg = 0;
720 0           my $end = 0;
721 0           my $lim = 5 * 3.14159265 / 180;
722 0           my $term = 8;
723 0           my $stop = 3;
724              
725 0 0         if ( $type eq 'H' ) {
726 0           $stop = 9;
727 0           $term = 100;
728             }
729 0 0         if ( $ss == 1 )
730             { #first ss of motif, measure axis from the loop side (end of ss)
731 0           $beg = max( $last - $stop, $first );
732 0           $end = $last;
733             }
734             else {
735 0           $beg = $first
736             ; #second ss of motif, measure axis from the loop side (beginning of ss)
737 0           $end = min( $first + $stop, $last );
738             }
739 0           for ( my $aa = $beg ; $aa < $end ; $aa++ ) {
740 0           push( @use_ca, [ ( $$ca[$aa][0], $$ca[$aa][1], $$ca[$aa][2] ) ] );
741 0           push( @use_n, [ ( $$n[$aa][0], $$n[$aa][1], $$n[$aa][2] ) ] );
742 0           push( @use_c, [ ( $$c[$aa][0], $$c[$aa][1], $$c[$aa][2] ) ] );
743             }
744 0           @newaxis = calculate_axis( $type, \@use_ca, \@use_n, \@use_c );
745 0           $j = $stop;
746 0           @oldaxis = @newaxis;
747 0   0       while ( ( $j < $count ) and ( $ang < $lim ) and ( $j < $term ) )
      0        
748             { #so long as the ss does not curve too much, keep measuring axis
749 0           $j++;
750 0           @use_ca = ();
751 0           @use_n = ();
752 0           @use_c = ();
753              
754             #extend ss from the appropriate side
755 0 0         if ( $ss == 1 ) {
756 0           $beg = max( $last - $j, $first );
757 0           $end = $last;
758             }
759             else {
760 0           $beg = $first;
761 0           $end = min( $first + $j, $last );
762             }
763 0           for ( my $aa = $beg ; $aa < $end ; $aa++ ) {
764 0           push( @use_ca, [ ( $$ca[$aa][0], $$ca[$aa][1], $$ca[$aa][2] ) ] );
765 0           push( @use_n, [ ( $$n[$aa][0], $$n[$aa][1], $$n[$aa][2] ) ] );
766 0           push( @use_c, [ ( $$c[$aa][0], $$c[$aa][1], $$c[$aa][2] ) ] );
767             }
768 0           @oldaxis = @newaxis;
769 0           @newaxis = calculate_axis( $type, \@use_ca, \@use_n, \@use_c );
770 0           $ang = Math::Trig::acos( dot( @newaxis, @oldaxis ) );
771             }
772 0           return @oldaxis;
773             }
774              
775             =head2 calc_I
776              
777             subroutine to calculate the matrix of moments from a set of backbone coordinates
778             Input 1: Secondary structure type
779             Input 2: Number of backbone atoms;
780             Input 3: 3-D vector containing the coordinates of C-alpha, N, and C-atoms
781              
782             =cut
783             sub calc_I {
784 0     0     my ( $type, $count, @p ) = @_;
785 0           my @I = ( [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] );
786 0           my @pt;
787 0 0         if ( $type eq 'H' ) { #helix, calculate based on each point
788 0           for ( my $atom = 0 ; $atom < 3 ; $atom++ ) {
789 0           for ( my $a = 0 ; $a < $count ; $a++ ) {
790 0           @pt = ( $p[$atom][$a][0], $p[$atom][$a][1], $p[$atom][$a][2] );
791 0           $I[0][0] = $I[0][0] + ( $pt[1]**2 ) + ( $pt[2]**2 );
792 0           $I[1][1] = $I[1][1] + ( $pt[0]**2 ) + ( $pt[2]**2 );
793 0           $I[2][2] = $I[2][2] + ( $pt[0]**2 ) + ( $pt[1]**2 );
794 0           $I[0][1] = $I[0][1] - $pt[0] * $pt[1];
795 0           $I[0][2] = $I[0][2] - $pt[0] * $pt[2];
796 0           $I[1][2] = $I[1][2] - $pt[1] * $pt[2];
797             }
798             }
799             }
800             else { #strand, calculate based on midpts of successive atoms
801 0           for ( my $atom = 0 ; $atom < 3 ; $atom++ ) {
802 0           for ( my $a = 0 ; $a < $count - 1 ; $a++ ) {
803 0           @pt = (
804             0.5 * ( $p[$atom][$a][0] + $p[$atom][ $a + 1 ][0] ),
805             0.5 * ( $p[$atom][$a][1] + $p[$atom][ $a + 1 ][1] ),
806             0.5 * ( $p[$atom][$a][2] + $p[$atom][ $a + 1 ][2] )
807             );
808 0           $I[0][0] = $I[0][0] + ( $pt[1]**2 ) + ( $pt[2]**2 );
809 0           $I[1][1] = $I[1][1] + ( $pt[0]**2 ) + ( $pt[2]**2 );
810 0           $I[2][2] = $I[2][2] + ( $pt[0]**2 ) + ( $pt[1]**2 );
811 0           $I[0][1] = $I[0][1] - $pt[0] * $pt[1];
812 0           $I[0][2] = $I[0][2] - $pt[0] * $pt[2];
813 0           $I[1][2] = $I[1][2] - $pt[1] * $pt[2];
814             }
815             }
816             }
817 0           $I[1][0] = $I[0][1];
818 0           $I[2][0] = $I[0][2];
819 0           $I[2][1] = $I[1][2];
820 0           return @I;
821             }
822              
823             =head2 calc_evec
824              
825             subroutine to calculate the eigenvector of a 3x3 matrix for a given eigenvalue
826             Input 1: Eigenvalue
827             Input 2: 3x3 matrix
828              
829             =cut
830             sub calc_evec {
831 0     0     my ( $eval, @M ) = @_;
832              
833             #subtract off diagonals
834 0           my @I = @{ Storable::dclone( \@M ) };
  0            
835 0           $I[0][0] = $I[0][0] - $eval;
836 0           $I[1][1] = $I[1][1] - $eval;
837 0           $I[2][2] = $I[2][2] - $eval;
838 0           my @evec = ( 0, 0, 0 );
839              
840             #Find co-factors of first row
841 0           $evec[0] = $I[1][1] * $I[2][2] - $I[1][2] * $I[2][1];
842 0           $evec[1] = $I[1][2] * $I[2][0] - $I[1][0] * $I[2][2];
843 0           $evec[2] = $I[1][0] * $I[2][1] - $I[1][1] * $I[2][0];
844 0 0         if ( $evec[0]**2 + $evec[1]**2 + $evec[2]**2 < 0.01 ) {
845              
846             #Find co-factors of second row
847 0           $evec[0] = $I[0][2] * $I[2][1] - $I[0][1] * $I[2][2];
848 0           $evec[1] = $I[0][0] * $I[2][2] - $I[0][2] * $I[2][0];
849 0           $evec[2] = $I[0][1] * $I[2][0] - $I[0][0] * $I[2][1];
850 0 0         if ( $evec[0]**2 + $evec[1]**2 + $evec[2]**2 < 0.01 ) {
851 0           $evec[0] = $I[0][1] * $I[1][2] - $I[0][2] * $I[1][1];
852 0           $evec[1] = $I[0][2] * $I[1][0] - $I[0][0] * $I[1][2];
853 0           $evec[2] = $I[0][0] * $I[1][1] - $I[0][1] * $I[1][0];
854             }
855             }
856 0           return unit(@evec);
857             }
858              
859             =head2
860              
861             subroutine to project a point onto an axis, given a point on the line
862             Input 1: Cooridnates of the point
863             Input 2: Vector representing the axis
864             Input 3: A point on the line
865              
866             =cut
867             sub projectpoint {
868 0     0     my ( $p, $v, $c ) = @_;
869 0           my @newp = ( 0, 0, 0 );
870 0           my @padj = ( $$p[0] - $$c[0], $$p[1] - $$c[1], $$p[2] - $$c[2] );
871 0           my $proj = dot( @padj, @$v );
872 0           $newp[0] = $proj * $$v[0] + $$c[0];
873 0           $newp[1] = $proj * $$v[1] + $$c[1];
874 0           $newp[2] = $proj * $$v[2] + $$c[2];
875 0           return @newp;
876             }
877              
878             =head2 det
879              
880             subroutine to find the determinant of a 3x3 matrix
881              
882             =cut
883             sub det {
884 0     0     my (@m) = @_;
885 0           my $d =
886             $m[0][0] * ( $m[1][1] * $m[2][2] - $m[1][2] * $m[2][1] ) -
887             $m[0][1] * ( $m[1][0] * $m[2][2] - $m[1][2] * $m[2][0] ) +
888             $m[0][2] * ( $m[1][0] * $m[2][1] - $m[1][1] * $m[2][0] );
889 0           return $d;
890             }
891              
892             =head2 find_eigs
893              
894             subroutine to find the eigenvalues (ordered) of a 3x3 matrix
895             =cut
896             sub find_eigs {
897              
898             #return eigenvalues
899 0     0     my (@I) = @_;
900 0           my $a = -1 * ( $I[0][0] + $I[1][1] + $I[2][2] );
901 0           my $b =
902             $I[0][0] * $I[2][2] +
903             $I[1][1] * $I[2][2] +
904             $I[0][0] * $I[1][1] -
905             $I[0][2] * $I[2][0] -
906             $I[1][2] * $I[2][1] -
907             $I[0][1] * $I[1][0];
908 0           my $c = -1 * det(@I);
909 0           my @eigs = find_roots( $a, $b, $c );
910 0           my @temp;
911 0 0 0       if ( ( $eigs[0] > $eigs[1] ) and ( $eigs[0] > $eigs[2] ) ) {
    0          
912 0           $temp[0] = $eigs[0];
913 0 0         if ( $eigs[1] > $eigs[2] ) {
914 0           $temp[1] = $eigs[1];
915 0           $temp[2] = $eigs[2];
916             }
917             else {
918 0           $temp[1] = $eigs[2];
919 0           $temp[2] = $eigs[1];
920             }
921             }
922             elsif ( $eigs[1] > $eigs[2] ) {
923 0           $temp[0] = $eigs[1];
924 0 0         if ( $eigs[0] > $eigs[2] ) {
925 0           $temp[1] = $eigs[0];
926 0           $temp[2] = $eigs[2];
927             }
928             else {
929 0           $temp[1] = $eigs[2];
930 0           $temp[2] = $eigs[0];
931             }
932             }
933             else {
934 0           $temp[0] = $eigs[2];
935 0 0         if ( $eigs[0] > $eigs[1] ) {
936 0           $temp[1] = $eigs[0];
937 0           $temp[2] = $eigs[1];
938             }
939             else {
940 0           $temp[1] = $eigs[1];
941 0           $temp[2] = $eigs[0];
942             }
943             }
944 0           return @temp;
945             }
946              
947             =head2 matmul
948              
949             subroutine to multiply two matrices
950             Input 1: Matrix 1
951             Input 2: Matrix 2
952             Input 3: Number of rows in matrix 1
953             Input 4: Number of columns in matrix 1, also the number of rows in matrix 2
954             Input 5: Numer of columns in matrix 2
955              
956             =cut
957             sub matmul {
958 0     0     my ( $m, $n, $dim1, $dim2, $dim3 ) = @_;
959 0           my @o;
960             my @row;
961 0           for ( my $aa = 0 ; $aa < $dim3 ; $aa++ ) {
962 0           push( @row, 0 );
963             }
964 0           for ( my $aa = 0 ; $aa < $dim1 ; $aa++ ) {
965 0           push( @o, [@row] );
966             }
967 0           for ( my $aa = 0 ; $aa < $dim1 ; $aa++ ) {
968 0           for ( my $bb = 0 ; $bb < $dim3 ; $bb++ ) {
969 0           for ( my $cc = 0 ; $cc < $dim2 ; $cc++ ) {
970 0           $o[$aa][$bb] += $$m[$aa][$cc] * $$n[$cc][$bb];
971             }
972             }
973             }
974 0           return @o;
975             }
976              
977             =head2 matvec
978            
979             subroutine to multiply a vector by a matrix
980            
981             Input 1: Matrix
982             Input 2: Vector
983             Input 3: Number of rows in the matrix
984             Input 4: Number of columns in the matrix, also the number of elements in the vector
985              
986             =cut
987             sub matvec {
988 0     0     my ( $m, $n, $dim1, $dim2 ) = @_;
989 0           my @o;
990 0           for ( my $aa = 0 ; $aa < $dim1 ; $aa++ ) {
991 0           push( @o, 0 );
992             }
993 0           for ( my $aa = 0 ; $aa < $dim1 ; $aa++ ) {
994 0           for ( my $cc = 0 ; $cc < $dim2 ; $cc++ ) {
995 0           $o[$aa] += $$m[$aa][$cc] * $$n[$cc];
996             }
997             }
998 0           return @o;
999             }
1000              
1001             =head2 vecvec
1002            
1003             subroutine to find the outer product of two vectors of the same length
1004             Input 1: Vector 1
1005             Input 2: Vector 2
1006             Input 3: Dimension of vector 1, also dimension of vector 2
1007              
1008             =cut
1009             sub vecvec {
1010 0     0     my ( $m, $n, $dim1 ) = @_;
1011 0           my @o;
1012             my @row;
1013 0           for ( my $aa = 0 ; $aa < $dim1 ; $aa++ ) {
1014 0           push( @row, 0 );
1015             }
1016 0           for ( my $aa = 0 ; $aa < $dim1 ; $aa++ ) {
1017 0           push( @o, [@row] );
1018             }
1019 0           for ( my $aa = 0 ; $aa < $dim1 ; $aa++ ) {
1020 0           for ( my $bb = 0 ; $bb < $dim1 ; $bb++ ) {
1021 0           $o[$aa][$bb] = $$m[$aa] * $$n[$bb];
1022             }
1023             }
1024 0           return @o;
1025             }
1026              
1027             =head2 find_roots
1028              
1029             subroutine to find the roots of a cubic equation of the form x^3+ax^2+bx+cx=0
1030              
1031             =cut
1032             sub find_roots {
1033 0     0     my ( $a, $b, $c ) = @_;
1034 0           my $p = $b - ( $a**2 ) / 3;
1035 0           my $q = $c + ( 2 * $a**3 - 9 * $a * $b ) / 27;
1036 0           my $urad = ( ( $q**2 ) / 4 + ( $p**3 ) / 27 );
1037 0           my $mag = sqrt( 0.25 * ( $q**2 ) - $urad );
1038 0           my $newmag = $mag**( 1 / 3 );
1039 0           my $ang = Math::Trig::acos( -0.5 * $q / $mag );
1040 0           my $m = abs( cos( $ang / 3 ) );
1041 0           my $n = abs( sin( $ang / 3 ) * ( 3**(0.5) ) );
1042 0           my $x1 = 2 * $newmag * $m - ( $a / 3 );
1043 0           my $x2 = -1 * $newmag * ( $m + $n ) - ( $a / 3 );
1044 0           my $x3 = -1 * $newmag * ( $m - $n ) - ( $a / 3 );
1045 0           return ( $x1, $x2, $x3 );
1046             }
1047              
1048             =head2 rotateaxis
1049              
1050             subroutine to rotate a point (pt) around an axis (vec) by a given angle (ang)
1051             =cut
1052             sub rotateaxis {
1053 0     0     my ( $pt, $vec, $ang ) = @_;
1054 0           my @res = ( $$pt[0], $$pt[1], $$pt[2] );
1055 0           my $cos = cos( $$ang * 3.1415926 / 180 );
1056 0           my $sin = sin( $$ang * 3.1415926 / 180 );
1057 0           my $uv = $$vec[0]**2 + $$vec[1]**2;
1058 0           my $vw = $$vec[1]**2 + $$vec[2]**2;
1059 0           my $uw = $$vec[0]**2 + $$vec[2]**2;
1060 0           $res[0] =
1061             ( $$vec[0] * dot( @$pt, @$vec ) ) -
1062             ( $$vec[0] * ( $$vec[1] * $$pt[1] + $$vec[2] * $$pt[2] ) - $$pt[0] * $vw )
1063             * $cos +
1064             ( $$vec[1] * $$pt[2] - $$vec[2] * $$pt[1] ) * $sin;
1065 0           $res[1] =
1066             ( $$vec[1] * dot( @$pt, @$vec ) ) -
1067             ( $$vec[1] * ( $$vec[0] * $$pt[0] + $$vec[2] * $$pt[2] ) - $$pt[1] * $uw )
1068             * $cos +
1069             ( $$vec[2] * $$pt[0] - $$vec[0] * $$pt[2] ) * $sin;
1070 0           $res[2] =
1071             ( $$vec[2] * dot( @$pt, @$vec ) ) -
1072             ( $$vec[2] * ( $$vec[0] * $$pt[0] + $$vec[1] * $$pt[1] ) - $$pt[2] * $uv )
1073             * $cos +
1074             ( $$vec[0] * $$pt[1] - $$vec[1] * $$pt[0] ) * $sin;
1075 0           return @res;
1076             }
1077              
1078             =head2 rotate
1079              
1080             subroutine to rotate a point (pt) such that one axis (u) aligns with another (v)
1081              
1082             =cut
1083             sub rotate {
1084 0     0     my ( $pt, $u, $v ) = @_;
1085              
1086             #rotate a point (pt) such that vector u aligns with vector v
1087 0           my @res = ( $$pt[0], $$pt[1], $$pt[2] );
1088 0           my $cos = dot( @$u, @$v ) / ( norm(@$u) * norm(@$v) );
1089 0 0         if ( $cos ne 1 ) { #if vectors are already aligned
1090 0           my @n = unit( cross( @$u, @$v ) );
1091 0           my $ocos = 1 - $cos;
1092 0           my $sin = sin( Math::Trig::acos($cos) );
1093 0           $res[0] =
1094             ( ( $ocos * $n[0] * $n[0] ) + $cos ) * $$pt[0] +
1095             ( ( $ocos * $n[0] * $n[1] ) - $n[2] * $sin ) * $$pt[1] +
1096             ( ( $ocos * $n[0] * $n[2] ) + $n[1] * $sin ) * $$pt[2];
1097 0           $res[1] =
1098             ( ( $ocos * $n[0] * $n[1] ) + $n[2] * $sin ) * $$pt[0] +
1099             ( ( $ocos * $n[1] * $n[1] ) + $cos ) * $$pt[1] +
1100             ( ( $ocos * $n[1] * $n[2] ) - $n[0] * $sin ) * $$pt[2];
1101 0           $res[2] =
1102             ( ( $ocos * $n[0] * $n[2] ) - $n[1] * $sin ) * $$pt[0] +
1103             ( ( $ocos * $n[1] * $n[2] ) + $n[0] * $sin ) * $$pt[1] +
1104             ( ( $ocos * $n[2] * $n[2] ) + $cos ) * $$pt[2];
1105             }
1106 0           return @res;
1107             }
1108              
1109             =head2 cross
1110              
1111             subroutine to find the cross product of two 3-d vectors
1112             Input 1: Concatenated vector whose first three elements represent vector 1 and whose last three represent vector 2
1113              
1114             =cut
1115             sub cross {
1116 0     0     my (@v) = @_;
1117 0           my @res = ( 0, 0, 0 );
1118 0           $res[0] = $v[1] * $v[5] - $v[2] * $v[4];
1119 0           $res[1] = $v[2] * $v[3] - $v[0] * $v[5];
1120 0           $res[2] = $v[0] * $v[4] - $v[1] * $v[3];
1121 0           return @res;
1122             }
1123              
1124             =head2 dot2
1125              
1126             subroutine to find the dot product of two vectors
1127             Input 1: Concatenated vector whose first half represents vector 1 and whose second half represents vector 2
1128              
1129             =cut
1130             sub dot {
1131 0     0     my (@v) = @_;
1132 0           my $max = scalar(@v) / 2;
1133 0           my $res = 0;
1134 0           for ( my $aa = 0 ; $aa < $max ; $aa++ ) {
1135 0           $res += $v[$aa] * $v[ $aa + $max ];
1136             }
1137 0           return $res;
1138             }
1139              
1140             =head2 unit
1141              
1142             subroutine to return the unit vector corresponding to a given vector
1143             =cut
1144             sub unit {
1145 0     0     my (@v) = @_;
1146 0           my $norm = norm(@v);
1147 0           my $max = scalar(@v);
1148 0           for ( my $a = 0 ; $a < $max ; $a++ ) {
1149 0           $v[$a] = $v[$a] / $norm;
1150             }
1151 0           return @v;
1152             }
1153              
1154             =head2 norm
1155            
1156             subroutine to return the 2-norm of a given vector
1157              
1158             =cut
1159             sub norm {
1160 0     0     my (@v) = @_;
1161 0           my $res = 0;
1162 0           foreach (@v) {
1163 0           $res += $_**2;
1164             }
1165 0           return sqrt($res);
1166             }
1167              
1168             =head2 vecadd
1169              
1170             subroutine to add two vectors
1171             Input 1: Scalar factor to multiply the second vector
1172             Input 2: Concatenated vector whose first half represents the first vector
1173             and whose second half represents the second vector
1174              
1175             =cut
1176             sub vecadd {
1177 0     0     my ( $scale, @v1 ) = @_;
1178              
1179             #v1+scale*v2
1180 0           my $dim = scalar(@v1) / 2;
1181 0           my @res;
1182 0           for ( my $aa = 0 ; $aa < $dim ; $aa++ ) {
1183 0           push( @res, $v1[$aa] + $scale * $v1[ $dim + $aa ] );
1184             }
1185 0           return @res;
1186             }
1187              
1188             =head2 max
1189              
1190             subroutine to find the maximum of an array
1191              
1192             =cut
1193             sub max {
1194 0     0     my (@a) = @_;
1195 0           my $m = $a[0];
1196 0           foreach (@a) {
1197 0 0         if ( $_ > $m ) { $m = $_ }
  0            
1198             }
1199 0           return $m;
1200             }
1201              
1202             =head2 min
1203              
1204             subroutine to find the minimum of an array
1205              
1206             =cut
1207             sub min {
1208 0     0     my (@a) = @_;
1209 0           my $m = $a[0];
1210 0           foreach (@a) {
1211 0 0         if ( $_ < $m ) { $m = $_ }
  0            
1212             }
1213 0           return $m;
1214             }
1215              
1216             =head2 dbin
1217            
1218             subroutine to Fbin a value from 0-40 using a given binsize
1219              
1220             =cut
1221             sub dbin {
1222 0     0     my ( $val, $binsize ) = @_;
1223 0           my $binval = 41;
1224 0 0         if ( $val <= 40 ) {
1225 0           $binval = $binsize * ( int( $val / $binsize ) + 1 );
1226             }
1227 0           return $binval;
1228             }
1229              
1230             =head2 angbin
1231              
1232             subroutine to bin a value using a given bin size
1233              
1234             =cut
1235             sub angbin {
1236 0     0     my ( $val, $binsize ) = @_;
1237 0           my $binval = $binsize * ( int( $val / $binsize ) + 1 );
1238 0           return $binval;
1239             }
1240              
1241             =head2 pearson
1242              
1243             subroutine to find the Pearson correlation coefficient between two data sets
1244              
1245             =cut
1246             sub pearson {
1247 0     0     my ( $x, $y ) = @_;
1248 0           my $xm = 0;
1249 0           my $ym = 0;
1250 0           my $xsd = 0;
1251 0           my $ysd = 0;
1252 0           my $count = scalar(@$x);
1253 0           foreach (@$x) {
1254 0           $xm += $_;
1255 0           $xsd += $_ * $_;
1256             }
1257 0           foreach (@$y) {
1258 0           $ym += $_;
1259 0           $ysd += $_ * $_;
1260             }
1261 0           $xm /= $count;
1262 0           $ym /= $count;
1263 0           $xsd = sqrt( ( $xsd / $count ) - ( $xm * $xm ) );
1264 0           $ysd = sqrt( ( $ysd / $count ) - ( $ym * $ym ) );
1265 0           my $r = 0;
1266 0           for ( my $aa = 0 ; $aa < $count ; $aa++ ) {
1267 0           $r += ( ( $$x[$aa] - $xm ) * ( $$y[$aa] - $ym ) / ( $xsd * $ysd ) );
1268             }
1269 0           $r /= $count;
1270 0           return $r;
1271             }
1272              
1273             =head2 find_rmsd
1274              
1275             subroutine to find the RMSD between two sets of points,
1276             using the method described by Theobald
1277            
1278             Input 1: Number of points to consider
1279             Input 2: Vector with the first set of coordinates
1280             Input 3: Vector with the second set of coordinates
1281              
1282             =cut
1283             sub find_rmsd {
1284 0     0     my ( $c, $x, $y ) = @_;
1285 0           my @R = ( [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] );
1286 0           my @xcom = COM2( 0, $c, \@$x );
1287 0           my @ycom = COM2( 0, $c, \@$y );
1288 0           my $e0 = 0;
1289 0           for ( my $aa = 0 ; $aa < $c ; $aa++ ) {
1290 0           for ( my $bb = 0 ; $bb < 3 ; $bb++ ) {
1291 0           for ( my $cc = 0 ; $cc < 3 ; $cc++ ) {
1292 0           $R[$bb][$cc] +=
1293             ( $$x[$aa][$bb] - $xcom[$bb] ) *
1294             ( $$y[$aa][$cc] - $ycom[$cc] );
1295             }
1296 0           $e0 +=
1297             ( ( $$x[$aa][$bb] - $xcom[$bb] )**2 +
1298             ( $$y[$aa][$bb] - $ycom[$bb] )**2 );
1299             }
1300             }
1301              
1302             #calculate cross-coefficients
1303 0           my $xx = $R[0][0]**2;
1304 0           my $xy = $R[0][1]**2;
1305 0           my $xz = $R[0][2]**2;
1306 0           my $yx = $R[1][0]**2;
1307 0           my $yy = $R[1][1]**2;
1308 0           my $yz = $R[1][2]**2;
1309 0           my $zx = $R[2][0]**2;
1310 0           my $zy = $R[2][1]**2;
1311 0           my $zz = $R[2][2]**2;
1312 0           my $D = ( $xy + $xz - $yx - $zx )**2;
1313 0           my $temp = -$xx + $yy + $zz + $yz + $zy;
1314 0           my $temp2 = 2 * ( $R[1][1] * $R[2][2] - $R[1][2] * $R[2][1] );
1315 0           my $E = ( $temp - $temp2 ) * ( $temp + $temp2 );
1316 0           my $xzpzx = $R[0][2] + $R[2][0];
1317 0           my $xzmzx = $R[0][2] - $R[2][0];
1318 0           my $yzpzy = $R[1][2] + $R[2][1];
1319 0           my $yzmzy = $R[1][2] - $R[2][1];
1320 0           my $xypyx = $R[0][1] + $R[1][0];
1321 0           my $xymyx = $R[0][1] - $R[1][0];
1322 0           my $xmymz = $R[0][0] - $R[1][1] - $R[2][2];
1323 0           my $xmypz = $R[0][0] - $R[1][1] + $R[2][2];
1324 0           my $xpymz = $R[0][0] + $R[1][1] - $R[2][2];
1325 0           my $xpypz = $R[0][0] + $R[1][1] + $R[2][2];
1326 0           my $F =
1327             ( -( $xzpzx * $yzmzy ) + ( $xymyx * $xmymz ) ) *
1328             ( -( $xzmzx * $yzpzy ) + ( $xymyx * $xmypz ) );
1329 0           my $G =
1330             ( -( $xzpzx * $yzpzy ) - ( $xypyx * $xpymz ) ) *
1331             ( -( $xzmzx * $yzmzy ) - ( $xypyx * $xpypz ) );
1332 0           my $H =
1333             ( ( $xypyx * $yzpzy ) + ( $xzpzx * $xmypz ) ) *
1334             ( -( $xymyx * $yzmzy ) + ( $xzpzx * $xpypz ) );
1335 0           my $I =
1336             ( ( $xypyx * $yzmzy ) + ( $xzmzx * $xmymz ) ) *
1337             ( -( $xymyx * $yzpzy ) + ( $xzmzx * $xpymz ) );
1338 0           my $c0 = $D + $E + $F + $G + $H + $I;
1339 0           my $c1 =
1340             8 *
1341             ( ( $R[0][0] * ( $R[1][2] * $R[2][1] - $R[1][1] * $R[2][2] ) ) +
1342             ( $R[0][1] * ( $R[1][0] * $R[2][2] - $R[1][2] * $R[2][0] ) ) +
1343             ( $R[0][2] * ( $R[1][1] * $R[2][0] - $R[1][0] * $R[2][1] ) ) );
1344 0           my $c2 = -2 * ( $xx + $xy + $xz + $yx + $yy + $yz + $zx + $zy + $zz );
1345 0           my $lam = 0.5 * $e0;
1346 0           my $prec = 0.00001;
1347 0           my $lamold = $lam + 1;
1348              
1349             #Use Newton's method to find the largest eigenvalue
1350 0           while ( abs( $lam - $lamold ) > $prec ) {
1351 0           $lamold = $lam;
1352 0           my $t = 4 * ( $lam**3 ) + 2 * $c2 * $lam + $c1;
1353 0 0         if ( $t ne 0 ) {
1354 0           $lam -=
1355             ( $lam**4 + $c2 * ( $lam**2 ) + $c1 * $lam + $c0 ) /
1356             ( 4 * ( $lam**3 ) + 2 * $c2 * $lam + $c1 );
1357             }
1358             }
1359 0           my $rms = sqrt( abs( ( $e0 - 2 * $lam ) / $c ) );
1360 0           return $rms;
1361             }
1362              
1363             =head2 superpose
1364              
1365             subroutine to superpose one set of points onto another
1366              
1367             Input 1: Number of points to superpose
1368             Input 2: Vector of points to be used as the superposition template
1369             Input 3: Vector of points to be aligned and superposed
1370             Input 4: Vector of extra points to be superposed (not aligned, since they are "carried along")
1371              
1372             =cut
1373             sub superpose {
1374 0     0     my ( $c, $x, $y, $extra ) = @_;
1375 0           my @R = ( [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] );
1376 0           my @xcom = COM2( 0, $c, \@$x );
1377 0           my @ycom = COM2( 0, $c, \@$y );
1378 0           my $e0 = 0;
1379 0           for ( my $aa = 0 ; $aa < $c ; $aa++ ) {
1380 0           for ( my $bb = 0 ; $bb < 3 ; $bb++ ) {
1381 0           for ( my $cc = 0 ; $cc < 3 ; $cc++ ) {
1382 0           $R[$bb][$cc] +=
1383             ( $$x[$aa][$bb] - $xcom[$bb] ) *
1384             ( $$y[$aa][$cc] - $ycom[$cc] );
1385             }
1386 0           $e0 +=
1387             ( ( $$x[$aa][$bb] - $xcom[$bb] )**2 +
1388             ( $$y[$aa][$bb] - $ycom[$bb] )**2 );
1389             }
1390             }
1391              
1392             #calculate cross-coefficients
1393 0           my $xx = $R[0][0]**2;
1394 0           my $xy = $R[0][1]**2;
1395 0           my $xz = $R[0][2]**2;
1396 0           my $yx = $R[1][0]**2;
1397 0           my $yy = $R[1][1]**2;
1398 0           my $yz = $R[1][2]**2;
1399 0           my $zx = $R[2][0]**2;
1400 0           my $zy = $R[2][1]**2;
1401 0           my $zz = $R[2][2]**2;
1402 0           my $D = ( $xy + $xz - $yx - $zx )**2;
1403 0           my $temp = -$xx + $yy + $zz + $yz + $zy;
1404 0           my $temp2 = 2 * ( $R[1][1] * $R[2][2] - $R[1][2] * $R[2][1] );
1405 0           my $E = ( $temp - $temp2 ) * ( $temp + $temp2 );
1406 0           my $xzpzx = $R[0][2] + $R[2][0];
1407 0           my $xzmzx = $R[0][2] - $R[2][0];
1408 0           my $yzpzy = $R[1][2] + $R[2][1];
1409 0           my $yzmzy = $R[1][2] - $R[2][1];
1410 0           my $xypyx = $R[0][1] + $R[1][0];
1411 0           my $xymyx = $R[0][1] - $R[1][0];
1412 0           my $xmymz = $R[0][0] - $R[1][1] - $R[2][2];
1413 0           my $xmypz = $R[0][0] - $R[1][1] + $R[2][2];
1414 0           my $xpymz = $R[0][0] + $R[1][1] - $R[2][2];
1415 0           my $xpypz = $R[0][0] + $R[1][1] + $R[2][2];
1416 0           my $F =
1417             ( -( $xzpzx * $yzmzy ) + ( $xymyx * $xmymz ) ) *
1418             ( -( $xzmzx * $yzpzy ) + ( $xymyx * $xmypz ) );
1419 0           my $G =
1420             ( -( $xzpzx * $yzpzy ) - ( $xypyx * $xpymz ) ) *
1421             ( -( $xzmzx * $yzmzy ) - ( $xypyx * $xpypz ) );
1422 0           my $H =
1423             ( ( $xypyx * $yzpzy ) + ( $xzpzx * $xmypz ) ) *
1424             ( -( $xymyx * $yzmzy ) + ( $xzpzx * $xpypz ) );
1425 0           my $I =
1426             ( ( $xypyx * $yzmzy ) + ( $xzmzx * $xmymz ) ) *
1427             ( -( $xymyx * $yzpzy ) + ( $xzmzx * $xpymz ) );
1428 0           my $c0 = $D + $E + $F + $G + $H + $I;
1429 0           my $c1 =
1430             8 *
1431             ( ( $R[0][0] * ( $R[1][2] * $R[2][1] - $R[1][1] * $R[2][2] ) ) +
1432             ( $R[0][1] * ( $R[1][0] * $R[2][2] - $R[1][2] * $R[2][0] ) ) +
1433             ( $R[0][2] * ( $R[1][1] * $R[2][0] - $R[1][0] * $R[2][1] ) ) );
1434 0           my $c2 = -2 * ( $xx + $xy + $xz + $yx + $yy + $yz + $zx + $zy + $zz );
1435 0           my $lam = 0.5 * $e0;
1436 0           my $prec = 0.00001;
1437 0           my $lamold = $lam + 1;
1438              
1439             #Use Newton's method to find largest eigenvalue
1440 0           while ( abs( $lam - $lamold ) > $prec ) {
1441 0           $lamold = $lam;
1442 0           my $t = 4 * ( $lam**3 ) + 2 * $c2 * $lam + $c1;
1443 0 0         if ( $t ne 0 ) {
1444 0           $lam -=
1445             ( $lam**4 + $c2 * ( $lam**2 ) + $c1 * $lam + $c0 ) /
1446             ( 4 * ( $lam**3 ) + 2 * $c2 * $lam + $c1 );
1447             }
1448             }
1449 0           my $rms = sqrt( abs( ( $e0 - 2 * $lam ) / $c ) );
1450 0 0         if ( $rms > 0 ) {
1451              
1452             #find eigenvector of 4x4 quaternion matrix for largest eigenvalue
1453 0           my @evec = ( 0, 0, 0, 0 );
1454 0           my @M;
1455 0           push( @M, [ ( $xpypz - $lam, $yzmzy, -$xzmzx, $xymyx ) ] );
1456 0           push( @M, [ ( $yzmzy, $xmymz - $lam, $xypyx, $xzpzx ) ] );
1457 0           push( @M, [ ( -$xzmzx, $xypyx, -$xmypz - $lam, $yzpzy ) ] );
1458 0           push( @M, [ ( $xymyx, $xzpzx, $yzpzy, -$xpymz - $lam ) ] );
1459              
1460             #Find co-factors of first row
1461             $evec[0] = det(
1462             (
1463 0           [ @{ $M[1] }[ 1 .. 3 ] ],
1464 0           [ @{ $M[2] }[ 1 .. 3 ] ],
1465 0           [ @{ $M[3] }[ 1 .. 3 ] ]
  0            
1466             )
1467             );
1468             $evec[1] = -1 * det(
1469             (
1470 0           [ ( $M[1][0], @{ $M[1] }[ 2 .. 3 ] ) ],
1471 0           [ ( $M[2][0], @{ $M[2] }[ 2 .. 3 ] ) ],
1472 0           [ ( $M[3][0], @{ $M[3] }[ 2 .. 3 ] ) ]
  0            
1473             )
1474             );
1475             $evec[2] = det(
1476             (
1477 0           [ ( @{ $M[1] }[ 0 .. 1 ], $M[1][3] ) ],
1478 0           [ ( @{ $M[2] }[ 0 .. 1 ], $M[2][3] ) ],
1479 0           [ ( @{ $M[3] }[ 0 .. 1 ], $M[3][3] ) ]
  0            
1480             )
1481             );
1482             $evec[3] = -1 * det(
1483             (
1484 0           [ @{ $M[1] }[ 0 .. 2 ] ],
1485 0           [ @{ $M[2] }[ 0 .. 2 ] ],
1486 0           [ @{ $M[3] }[ 0 .. 2 ] ]
  0            
1487             )
1488             );
1489 0 0         if (
1490             abs( $evec[0] ) +
1491             abs( $evec[1] ) +
1492             abs( $evec[2] ) +
1493             abs( $evec[3] ) < 0.001 )
1494             {
1495              
1496             #Find co-factors of second row
1497             $evec[0] = -1 * det(
1498             (
1499 0           [ @{ $M[0] }[ 1 .. 3 ] ],
1500 0           [ @{ $M[2] }[ 1 .. 3 ] ],
1501 0           [ @{ $M[3] }[ 1 .. 3 ] ]
  0            
1502             )
1503             );
1504             $evec[1] = det(
1505             (
1506 0           [ ( $M[0][0], @{ $M[0] }[ 2 .. 3 ] ) ],
1507 0           [ ( $M[2][0], @{ $M[2] }[ 2 .. 3 ] ) ],
1508 0           [ ( $M[3][0], @{ $M[3] }[ 2 .. 3 ] ) ]
  0            
1509             )
1510             );
1511             $evec[2] = -1 * det(
1512             (
1513 0           [ ( @{ $M[0] }[ 0 .. 1 ], $M[0][3] ) ],
1514 0           [ ( @{ $M[2] }[ 0 .. 1 ], $M[2][3] ) ],
1515 0           [ ( @{ $M[3] }[ 0 .. 1 ], $M[3][3] ) ]
  0            
1516             )
1517             );
1518             $evec[3] = det(
1519             (
1520 0           [ @{ $M[0] }[ 0 .. 2 ] ],
1521 0           [ @{ $M[2] }[ 0 .. 2 ] ],
1522 0           [ @{ $M[3] }[ 0 .. 2 ] ]
  0            
1523             )
1524             );
1525 0 0         if ( $evec[0]**2 + $evec[1]**2 + $evec[2]**2 + $evec[3]**2 < 0.001 )
1526             {
1527              
1528             #Find co-factors of third row
1529             $evec[0] = det(
1530             (
1531 0           [ @{ $M[0] }[ 1 .. 3 ] ],
1532 0           [ @{ $M[1] }[ 1 .. 3 ] ],
1533 0           [ @{ $M[3] }[ 1 .. 3 ] ]
  0            
1534             )
1535             );
1536             $evec[1] = -1 * det(
1537             (
1538 0           [ ( $M[0][0], @{ $M[0] }[ 2 .. 3 ] ) ],
1539 0           [ ( $M[1][0], @{ $M[1] }[ 2 .. 3 ] ) ],
1540 0           [ ( $M[3][0], @{ $M[3] }[ 2 .. 3 ] ) ]
  0            
1541             )
1542             );
1543             $evec[2] = det(
1544             (
1545 0           [ ( @{ $M[0] }[ 0 .. 1 ], $M[0][3] ) ],
1546 0           [ ( @{ $M[1] }[ 0 .. 1 ], $M[1][3] ) ],
1547 0           [ ( @{ $M[3] }[ 0 .. 1 ], $M[3][3] ) ]
  0            
1548             )
1549             );
1550             $evec[3] = -1 * det(
1551             (
1552 0           [ @{ $M[0] }[ 0 .. 2 ] ],
1553 0           [ @{ $M[1] }[ 0 .. 2 ] ],
1554 0           [ @{ $M[3] }[ 0 .. 2 ] ]
  0            
1555             )
1556             );
1557 0 0         if ( $evec[0]**2 + $evec[1]**2 + $evec[2]**2 + $evec[3]**2 <
1558             0.001 )
1559             {
1560              
1561             #Find co-factors of fourth row
1562             $evec[0] = -1 * det(
1563             (
1564 0           [ @{ $M[0] }[ 1 .. 3 ] ],
1565 0           [ @{ $M[1] }[ 1 .. 3 ] ],
1566 0           [ @{ $M[2] }[ 1 .. 3 ] ]
  0            
1567             )
1568             );
1569             $evec[1] = det(
1570             (
1571 0           [ ( $M[0][0], @{ $M[0] }[ 2 .. 3 ] ) ],
1572 0           [ ( $M[1][0], @{ $M[1] }[ 2 .. 3 ] ) ],
1573 0           [ ( $M[2][0], @{ $M[2] }[ 2 .. 3 ] ) ]
  0            
1574             )
1575             );
1576             $evec[2] = -1 * det(
1577             (
1578 0           [ ( @{ $M[0] }[ 0 .. 1 ], $M[0][3] ) ],
1579 0           [ ( @{ $M[1] }[ 0 .. 1 ], $M[1][3] ) ],
1580 0           [ ( @{ $M[2] }[ 0 .. 1 ], $M[2][3] ) ]
  0            
1581             )
1582             );
1583             $evec[3] = det(
1584             (
1585 0           [ @{ $M[0] }[ 0 .. 2 ] ],
1586 0           [ @{ $M[1] }[ 0 .. 2 ] ],
1587 0           [ @{ $M[2] }[ 0 .. 2 ] ]
  0            
1588             )
1589             );
1590             }
1591             }
1592             }
1593 0           my @quat = unit(@evec);
1594 0           my $scal = $quat[0];
1595 0           my @vec = @quat[ 1 .. 3 ];
1596 0           foreach (@vec) { $_ = -1 * $_ }
  0            
1597              
1598             #Form rotation matrix
1599 0           foreach my $coord (@$y) {
1600             my @new = (
1601 0           ${$coord}[0] - $ycom[0],
1602 0           ${$coord}[1] - $ycom[1],
1603 0           ${$coord}[2] - $ycom[2]
  0            
1604             );
1605 0           my $s1 = -1 * dot( @vec, @new );
1606 0           my @svec = vecadd( $scal, cross( @vec, @new ), @new );
1607 0           my @rotpt =
1608             vecadd( -1 * $s1, vecadd( $scal, cross( @vec, @svec ), @svec ),
1609             @vec );
1610 0           $coord = [ vecadd( 1, @xcom, @rotpt ) ];
1611             }
1612 0           foreach my $coord (@$extra) {
1613             my @new = (
1614 0           ${$coord}[0] - $ycom[0],
1615 0           ${$coord}[1] - $ycom[1],
1616 0           ${$coord}[2] - $ycom[2]
  0            
1617             );
1618 0           my $s1 = -1 * dot( @vec, @new );
1619 0           my @svec = vecadd( $scal, cross( @vec, @new ), @new );
1620 0           my @rotpt =
1621             vecadd( -1 * $s1, vecadd( $scal, cross( @vec, @svec ), @svec ),
1622             @vec );
1623 0           $coord = [ vecadd( 1, @xcom, @rotpt ) ];
1624             }
1625             }
1626             else {
1627 0           @$y = @{ Storable::dclone( \@$x ) };
  0            
1628             }
1629 0           return $rms;
1630             }
1631              
1632             =head2 dihedral
1633              
1634             subroutine to calculate the dihedral angle generated by four atoms
1635              
1636             =cut
1637             sub dihedral {
1638 0     0     my ( $c1, $c2, $c3, $c4 ) = @_;
1639 0           my @v21 = unit( vecadd( -1, @$c1, @$c2 ) );
1640 0           my @v23 = unit( vecadd( -1, @$c3, @$c2 ) );
1641 0           my @v34 = unit( vecadd( -1, @$c4, @$c3 ) );
1642 0           my @v21orth = unit( vecadd( -1 * dot( @v21, @v23 ), @v21, @v23 ) );
1643 0           my @v34orth = unit( vecadd( -1 * dot( @v34, @v23 ), @v34, @v23 ) );
1644 0           my $ang = Math::Trig::acos( dot( @v21orth, @v34orth ) ) * 180 / 3.14159265;
1645 0           my @dirvec = cross( @v21orth, @v34orth );
1646 0 0         if ( dot( @v23, @dirvec ) < 0 ) { $ang *= -1 }
  0            
1647 0           return $ang;
1648             }
1649              
1650             =head2 findcb
1651              
1652             subroutine to calculate the coordinates of a beta-carbon, given statistically observed tetrahedral geometry######
1653            
1654             Input 1: C-alpha coordinates
1655             Input 2: C coordinates
1656             Input 3: N coordinates
1657             =cut
1658             sub findcb {
1659 0     0     my ( $ca, $c, $n ) = @_;
1660 0           my @v3 = unit( vecadd( -1, @$c, @$n ) );
1661 0           my @v1 = unit( vecadd( -1, @$ca, @$c ) );
1662 0           my @v2 = unit( vecadd( -1, @$ca, @$n ) );
1663 0           my @v4 = unit( vecadd( 1, @v1, @v2 ) );
1664 0           my $ang = 53.2;
1665 0           my @dist = rotateaxis( \@v4, \@v3, \$ang );
1666 0           @dist = ( $dist[0] * 1.53, $dist[1] * 1.53, $dist[2] * 1.53 );
1667 0           my @cb = vecadd( 1, @$ca, @dist );
1668 0           return @cb;
1669             }
1670              
1671             =head2 findo
1672              
1673             =cut
1674             sub findo {
1675 0     0     my ( $ca, $c, $n ) = @_;
1676 0           my @c_to_ca = unit( vecadd( -1, @$ca, @$c ) );
1677 0           my @c_to_n = unit( vecadd( -1, @$n, @$c ) );
1678 0           my @normal = unit( cross( @c_to_n, @c_to_ca ) );
1679              
1680             #rotate c-ca bond around normal counterclockwise 121 degrees
1681 0           my $ang = 121;
1682 0           my @out = rotateaxis( \@c_to_ca, \@normal, \$ang );
1683 0           my @o = vecadd( 1.23, @$c, @out );
1684 0           return @o;
1685             }
1686              
1687             =head2 pointsonsphere
1688              
1689             subroutine to distribute points evenly on a sphere
1690             Input 1: Number of points to distribute
1691             Input 2: Radius of sphere
1692              
1693             =cut
1694             sub pointsonsphere {
1695 0     0     my ( $n, $rad ) = @_;
1696 0           my @pts;
1697 0           my $increment = 3.14159265359 * ( 3 - sqrt(5) );
1698 0           my $offset = 2 / $n;
1699 0           for ( my $aa = 0 ; $aa < $n ; $aa++ ) {
1700 0           my $y = $aa * $offset - 1 + 0.5 * $offset;
1701 0           my $r = sqrt( 1 - $y * $y );
1702 0           my $phi = $aa * $increment;
1703 0           push( @pts,
1704             [ ( $rad * $r * cos($phi), $rad * $y, $rad * $r * sin($phi) ) ] );
1705             }
1706 0           return @pts;
1707             }
1708              
1709             =head2 norm2
1710              
1711             subroutine to find the square of the 2-norm of a vector
1712             =cut
1713             sub norm2 {
1714 0     0     my (@v) = @_;
1715 0           my $res = 0;
1716 0           foreach (@v) {
1717 0           $res += $_**2;
1718             }
1719 0           return $res;
1720             }
1721              
1722             =head1 AUTHORS
1723              
1724             Fiserlab Members , C<< >>
1725              
1726             =head1 BUGS
1727              
1728             Please report any bugs or feature requests to C, or through
1729             the web interface at L.
1730              
1731             =head1 SUPPORT
1732              
1733             You can find documentation for this module with the perldoc command.
1734              
1735             perldoc GeometricalCalculations
1736              
1737             =over 4
1738              
1739             =item * RT: CPAN's request tracker (report bugs here)
1740              
1741             L
1742              
1743             =item * AnnoCPAN: Annotated CPAN documentation
1744              
1745             L
1746              
1747             =item * CPAN Ratings
1748              
1749             L
1750              
1751             =item * Search CPAN
1752              
1753             L
1754              
1755             =back
1756              
1757             =head1 LICENSE AND COPYRIGHT
1758              
1759             Copyright 2015 Fiserlab Members .
1760              
1761             This program is free software; you can redistribute it and/or modify it
1762             under the terms of the the Artistic License (2.0). You may obtain a
1763             copy of the full license at:
1764              
1765             L
1766              
1767             Any use, modification, and distribution of the Standard or Modified
1768             Versions is governed by this Artistic License. By using, modifying or
1769             distributing the Package, you accept this license. Do not use, modify,
1770             or distribute the Package, if you do not accept this license.
1771              
1772             If your Modified Version has been derived from a Modified Version made
1773             by someone other than you, you are nevertheless required to ensure that
1774             your Modified Version complies with the requirements of this license.
1775              
1776             This license does not grant you the right to use any trademark, service
1777             mark, tradename, or logo of the Copyright Holder.
1778              
1779             This license includes the non-exclusive, worldwide, free-of-charge
1780             patent license to make, have made, use, offer to sell, sell, import and
1781             otherwise transfer the Package with respect to any patent claims
1782             licensable by the Copyright Holder that are necessarily infringed by the
1783             Package. If you institute patent litigation (including a cross-claim or
1784             counterclaim) against any party alleging that the Package constitutes
1785             direct or contributory patent infringement, then this Artistic License
1786             to you shall terminate on the date that such litigation is filed.
1787              
1788             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
1789             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
1790             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
1791             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
1792             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
1793             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
1794             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
1795             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1796              
1797              
1798             =cut
1799              
1800             1; # End of GeometricalCalculations