File Coverage

lib/SmotifTF/GeometricalCalculations.pm
Criterion Covered Total %
statement 50 843 5.9
branch 0 246 0.0
condition 0 23 0.0
subroutine 16 60 26.6
pod n/a
total 66 1172 5.6


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