File Coverage

blib/lib/Bio/PDB/Structure.pm
Criterion Covered Total %
statement 538 659 81.6
branch 80 150 53.3
condition 0 3 0.0
subroutine 34 42 80.9
pod n/a
total 652 854 76.3


line stmt bran cond sub pod time code
1             package Bio::PDB::Structure::Atom;
2              
3 1     1   30467 use Math::Trig;
  1         41392  
  1         1679  
4              
5             #define an atom object containing all the fields of a pdb file
6             sub new
7             {
8 179     179   274 my $self = {};
9 179         357 $self->{TYPE} = undef;
10 179         237 $self->{NUMBER} = undef;
11 179         249 $self->{NAME} = undef;
12 179         212 $self->{RESIDUE_NAME} = undef;
13 179         285 $self->{CHAIN} = undef;
14 179         223 $self->{RESIDUE_NUMBER} = undef;
15 179         217 $self->{X} = undef;
16 179         380 $self->{Y} = undef;
17 179         233 $self->{Z} = undef;
18 179         232 $self->{OCCUPANCY} = undef;
19 179         221 $self->{BETA} = undef;
20 179         226 $self->{ALT} = undef;
21 179         216 $self->{INSERTION_CODE} = undef;
22 179         190 bless($self);
23 179         281 return $self;
24             }
25              
26             #accesor methods for atom objects
27             sub type
28             {
29 239     239   323 my $self= shift;
30 239 100       438 if (@_)
31             {
32 179         213 my $val = shift;
33 179         269 $self->{TYPE} = $val;
34 179         282 return;
35             }
36 60         141 return $self->{TYPE};
37             }
38              
39             sub number
40             {
41 239     239   276 my $self= shift;
42 239 100       435 if (@_)
43             {
44 179         186 my $val = shift;
45 179         264 $self->{NUMBER} = $val;
46 179         289 return;
47             }
48 60         127 return $self->{NUMBER};
49             }
50              
51             sub name
52             {
53 270     270   307 my $self= shift;
54 270 100       634 if (@_)
55             {
56 179         190 my $val = shift;
57 179         247 $self->{NAME} = $val;
58 179         240 return;
59             }
60 91         541 return $self->{NAME};
61             }
62              
63             sub chain
64             {
65 239     239   247 my $self= shift;
66 239 100       415 if (@_)
67             {
68 179         182 my $val = shift;
69 179         259 $self->{CHAIN} = $val;
70 179         219 return;
71             }
72 60         127 return $self->{CHAIN};
73             }
74              
75             sub residue_number
76             {
77 239     239   247 my $self= shift;
78 239 100       391 if (@_)
79             {
80 179         205 my $val = shift;
81 179         262 $self->{RESIDUE_NUMBER} = $val;
82 179         245 return;
83             }
84 60         126 return $self->{RESIDUE_NUMBER};
85             }
86              
87             sub residue_name
88             {
89 241     241   250 my $self= shift;
90 241 100       460 if (@_)
91             {
92 179         211 my $val = shift;
93 179         268 $self->{RESIDUE_NAME} = $val;
94 179         222 return;
95             }
96 62         140 return $self->{RESIDUE_NAME};
97             }
98              
99             sub x
100             {
101 785     785   881 my $self= shift;
102 785 100       1407 if (@_)
103             {
104 295         297 my $val = shift;
105 295         398 $self->{X} = $val;
106 295         377 return;
107             }
108 490         1321 return $self->{X};
109             }
110              
111             sub y
112             {
113 785     785   1037 my $self= shift;
114 785 100       1315 if (@_)
115             {
116 295         291 my $val = shift;
117 295         368 $self->{Y} = $val;
118 295         400 return;
119             }
120 490         1107 return $self->{Y};
121             }
122              
123             sub z
124             {
125 785     785   824 my $self= shift;
126 785 100       1293 if (@_)
127             {
128 295         300 my $val = shift;
129 295         378 $self->{Z} = $val;
130 295         535 return;
131             }
132 490         1232 return $self->{Z};
133             }
134              
135             sub occupancy
136             {
137 235     235   263 my $self= shift;
138 235 100       405 if (@_)
139             {
140 175         171 my $val = shift;
141 175         223 $self->{OCCUPANCY} = $val;
142 175         200 return;
143             }
144 60         110 return $self->{OCCUPANCY};
145             }
146              
147             sub beta
148             {
149 235     235   225 my $self= shift;
150 235 100       441 if (@_)
151             {
152 175         177 my $val = shift;
153 175         220 $self->{BETA} = $val;
154 175         206 return;
155             }
156 60         108 return $self->{BETA};
157             }
158              
159             sub alt
160             {
161 0     0   0 my $self= shift;
162 0 0       0 if (@_)
163             {
164 0         0 my $val = shift;
165 0         0 $self->{ALT} = $val;
166 0         0 return;
167             }
168 0         0 return $self->{ALT};
169             }
170              
171             sub insertion_code
172             {
173 0     0   0 my $self= shift;
174 0 0       0 if (@_)
175             {
176 0         0 my $val = shift;
177 0         0 $self->{INSERTION_CODE} = $val;
178 0         0 return;
179             }
180 0         0 return $self->{INSERTION_CODE};
181             }
182              
183             #compute usefull stuff for atom objects
184             sub distance
185             {
186 3     3   5 shift;
187 3         4 my $n = @_;
188 3 50       9 die "Error in distance calculation: need two atoms as argument" if ($n != 2);
189 3         4 my $obj1 = shift;
190 3         4 my $obj2 = shift;
191 3         8 my $dist = ($obj1 -> x - $obj2 ->x )**2;
192 3         9 $dist += ($obj1 ->y - $obj2 ->y )**2;
193 3         7 $dist += ($obj1 -> z - $obj2 ->z )**2;
194 3         30 $dist = sqrt($dist);
195 3         13 return $dist;
196             }
197              
198             #given three atoms a1-a2-a3 , compute the angle betwen bonds a1-a2 and a2-a3
199             sub angle
200             {
201 1     1   1 shift;
202 1         3 my $n = @_;
203 1 50       5 die "Error in angle calculation: need three atoms as argument" if ($n != 3);
204 1         3 my $obj1 = shift;
205 1         1 my $obj2 = shift;
206 1         2 my $obj3 = shift;
207 1         4 my $d1 = $obj1->distance($obj1,$obj2); #so the class is not hard coded
208 1         6 my $d2 = $obj1->distance($obj2,$obj3);
209 1         4 my $dx1 = ($obj1 -> x - $obj2 ->x)/$d1;
210 1         4 my $dy1 = ($obj1 -> y - $obj2 ->y)/$d1;
211 1         4 my $dz1 = ($obj1 -> z - $obj2 ->z)/$d1;
212 1         3 my $dx2 = ($obj3 -> x - $obj2 ->x)/$d2;
213 1         3 my $dy2 = ($obj3 -> y - $obj2 ->y)/$d2;
214 1         3 my $dz2 = ($obj3 -> z - $obj2 ->z)/$d2;
215 1         4 my $dot = $dx1*$dx2 + $dy1*$dy2 + $dz1*$dz2;
216 1         7 return 180.0/pi * acos( $dot );
217             }
218              
219             #given four atoms a1-a2-a3-a4, compute the dihedral that lies between a2 and a3
220             sub dihedral
221             {
222 1     1   2 shift;
223 1         3 my $n = @_;
224 1 50       4 die "Error in dihedral calculation: need four atoms as argument" if ($n != 4);
225 1         2 my @object;
226 1         4 for (my $i=0; $i <4; $i++)
227             {
228 4         10 $object[$i] = shift;
229             }
230 1         1 my @v;
231 1         5 for (my $j=0; $j < 4; $j++)
232             {
233 4         23 $v[0][$j] = $object[$j] ->x;
234 4         11 $v[1][$j] = $object[$j] ->y;
235 4         9 $v[2][$j] = $object[$j] ->z;
236             }
237 1         2 my @b;
238 1         4 for(my $i=0;$i<3;$i++)
239             {
240 3         11 $b[$i][0] = $v[$i][0] - $v[$i][1];
241 3         15 $b[$i][1] = $v[$i][2] - $v[$i][1];
242 3         12 $b[$i][2] = $v[$i][3] - $v[$i][2];
243             }
244 1         2 my @t1;
245             my @t2;
246             #cross product b[][1] x b[][0]
247 1         5 $t1[0] =$b[1][1]*$b[2][0] - $b[2][1]*$b[1][0];
248 1         4 $t1[1] =$b[2][1]*$b[0][0] - $b[0][1]*$b[2][0];
249 1         4 $t1[2] =$b[0][1]*$b[1][0] - $b[1][1]*$b[0][0];
250             #cross product b[][1] x b[][2]
251 1         3 $t2[0] =$b[1][1]*$b[2][2] - $b[2][1]*$b[1][2];
252 1         11 $t2[1] =$b[2][1]*$b[0][2] - $b[0][1]*$b[2][2];
253 1         3 $t2[2] =$b[0][1]*$b[1][2] - $b[1][1]*$b[0][2];
254 1         5 my $norm1 = sqrt($t1[0]**2 + $t1[1]**2 + $t1[2]**2);
255 1         3 my $norm2 = sqrt($t2[0]**2 + $t2[1]**2 + $t2[2]**2);
256 1         2 my $dot1=0;
257 1         1 my $dot2=0;
258 1         11 for (my $i=0; $i < 3; $i++)
259             {
260 3         4 $t1[$i] /= $norm1;
261 3         4 $t2[$i] /= $norm2;
262 3         5 $dot1 += $t1[$i] * $t2[$i];
263 3         8 $dot2 += $b[$i][2] * $t1[$i];
264             }
265 1         4 my $dihedral = 180.0/pi * acos($dot1);
266 1 50       15 $dihedral *= -1.0 if ( $dot2 < 0.0);
267 1         8 return $dihedral;
268             }
269              
270              
271              
272             package Bio::PDB::Structure::Molecule;
273              
274 1     1   31 use 5.012003;
  1         5  
  1         34  
275 1     1   5 use strict;
  1         14  
  1         46  
276 1     1   6 use warnings;
  1         2  
  1         5688  
277              
278             our $VERSION = '0.01';
279              
280             sub new
281             {
282 6     6   663 my $self = [];
283 6         13 bless ($self);
284 6         16 return $self;
285             }
286             #count the number of atoms in a molecule object
287             sub size
288             {
289 16     16   55 my $self= shift;
290 16         21 my $n = @{ $self };
  16         24  
291 16         34 return $n;
292             }
293              
294             #return the ith atom
295             sub atom
296             {
297 421     421   1773 my $self = shift;
298 421         466 my $n = @_;
299 421 50       740 die "Error in atom: need one atom position\n" unless ($n == 1);
300 421         746 my $pos = shift;
301 421         2018 return $self ->[$pos];
302             }
303              
304             #add an atom or a molecule to a molecule
305             sub push
306             {
307 176     176   176 my $self = shift;
308 176         184 my $n = @_;
309 176 50       353 die "Eror in push: need one atom or molecule\n" unless ( $n == 1);
310 176         172 my $object = shift;
311 176 100       374 push (@{$self},$object) if (ref($object) eq "Bio::PDB::Structure::Atom");
  175         311  
312 176 100       343 if ( ref($object) eq "Bio::PDB::Structure::Molecule")
313             {
314 1         2 my $nm = $object -> size;
315 1         4 for (my $i=0; $i < $nm; $i++)
316             {
317 30         24 push (@{$self},$object->[$i]);
  30         68  
318             }
319             }
320 176         737 return;
321             }
322              
323             #return number of models in a pdb file
324             sub models
325             {
326 1     1   2 shift;
327 1         2 my $fname = shift;
328 1 50       31 open(CMDFPDB,"<$fname") or die "Error in models: File $fname not found\n";
329 1         3 my $count = 0;
330 1         22 while ()
331             {
332 60 100       200 $count++ if (/^END/);
333             }
334 1         11 close CMDFPDB;
335 1         4 return $count;
336             }
337              
338             #read a pdb file into a molecule, can specify a specific model to read
339             sub read
340             {
341 6     6   198 my $type;
342             my $anum;
343 0         0 my $aname;
344 0         0 my $altloc;
345 0         0 my $rname;
346 0         0 my $chain;
347 0         0 my $rnum;
348 0         0 my $icode;
349 0         0 my $xx;
350 0         0 my $yy;
351 0         0 my $zz;
352 0         0 my $coor;
353 0         0 my $beta;
354 0         0 my @data;
355 6         9 my $inum = 0;
356 6         7 my $record = 0;
357 6         10 my $self = shift;
358 6 50       16 die "Error in read: pdb file must be specified\n" unless (@_);
359 6         11 my $fname = shift;
360 6 50       16 $record = shift if ( @_ );
361 6 50       268 open(FPDB,"<$fname") or die "File $fname not found\n";
362 6 50       20 if ( $record > 0 )
363             {
364 0         0 my $rcount=1;
365 0         0 while ()
366             {
367 0 0       0 if (/^END/)
368             {
369 0 0       0 last if ( $rcount == $record);
370 0         0 $rcount++;
371             }
372             }
373             }
374 6         181 while()
375             {
376 180 100       371 last if (/^END/);
377 174 50       449 next unless (/^ATOM |^HETATM / );
378 174         378 my $atom = Bio::PDB::Structure::Atom -> new;
379 174         1900 ($type,$anum,$aname,$altloc,$rname,$chain,$rnum,$icode,$xx,$yy,$zz,$coor,$beta ) =(/^(.{6})(.{5})(.{5})(.{1})(.{3})(.{2})(.{4})(.{1}).{3}(.{8})(.{8})(.{8})(.{0,6})(.{0,6})/);
380             #take out unecessary spaces
381 174         946 $type =~ s/^\s*(\S+)\s*$/$1/;
382 174         534 $anum =~ s/^\s*(\S+)\s*$/$1/;
383 174         677 $aname =~s/^\s*(\S*\s*\S)\s*$/$1/;
384 174         542 $chain =~s/^\s*(\S*)\s*$/$1/;
385 174 50       367 $chain = " " if ($chain eq "");
386 174         496 $rname =~s/^\s*(\S+)\s*$/$1/;
387 174         519 $rnum =~s/^\s*(\S+)\s*$/$1/;
388 174         498 $coor =~s/^\s*(\S+)\s*$/$1/;
389 174 50       472 $coor = 1.00 unless ( $coor =~ /\d/);
390 174         482 $beta =~s/^\s*(\S+)\s*$/$1/;
391 174 50       455 $beta = 0.00 unless ( $beta =~ /\d/);
392            
393 174         334 $atom -> type($type);
394 174         339 $atom -> number($anum);
395 174         328 $atom -> name($aname);
396 174         287 $atom -> residue_name($rname);
397 174         292 $atom -> chain($chain);
398 174         281 $atom -> residue_number($rnum);
399 174         267 $atom ->x($xx);
400 174         293 $atom ->y($yy);
401 174         292 $atom ->z($zz);
402 174         276 $atom -> occupancy($coor);
403 174         264 $atom -> beta($beta);
404             #since alterate location and insertion code are seldomly used only include them for atoms that have them
405 174 50       325 $atom -> alt($altloc) if ( $altloc ne " ");
406 174 50       289 $atom -> insertion_code($icode) if ( $icode ne " ");
407 174         282 $self-> push($atom);
408             }
409 6         105 close FPDB;
410 6         25 return;
411             }
412              
413             #print out a molecule to stdout or to a file
414             sub print
415             {
416              
417             #names of all the pdb atoms with correct justification for pdb print out
418             #these are set by convention and are important for some programs to work correctly
419 2     2   1759 my %atom_name = (
420             'C' => ' C ',
421             'C1' => ' C1 ',
422             'C1A' => ' C1A',
423             'C1B' => ' C1B',
424             'C1C' => ' C1C',
425             'C1D' => ' C1D',
426             'C2' => ' C2 ',
427             'C2A' => ' C2A',
428             'C2B' => ' C2B',
429             'C2C' => ' C2C',
430             'C2D' => ' C2D',
431             'C3' => ' C3 ',
432             'C3A' => ' C3A',
433             'C3B' => ' C3B',
434             'C3C' => ' C3C',
435             'C3D' => ' C3D',
436             'C4' => ' C4 ',
437             'C4A' => ' C4A',
438             'C4A' => ' C4A',
439             'C4B' => ' C4B',
440             'C4C' => ' C4C',
441             'C4D' => ' C4D',
442             'C5' => ' C5 ',
443             'C6' => ' C6 ',
444             'CA' => ' CA ',
445             'CAA' => ' CAA',
446             'CAB' => ' CAB',
447             'CAC' => ' CAC',
448             'CAD' => ' CAD',
449             'CB' => ' CB ',
450             'CBA' => ' CBA',
451             'CBB' => ' CBB',
452             'CBC' => ' CBC',
453             'CBD' => ' CBD',
454             'CD' => ' CD ',
455             'CD1' => ' CD1',
456             'CD2' => ' CD2',
457             'CE' => ' CE ',
458             'CE1' => ' CE1',
459             'CE2' => ' CE2',
460             'CE3' => ' CE3',
461             'CG' => ' CG ',
462             'CG1' => ' CG1',
463             'CG2' => ' CG2',
464             'CGA' => ' CGA',
465             'CGD' => ' CGD',
466             'CH2' => ' CH2',
467             'CHA' => ' CHA',
468             'CHB' => ' CHB',
469             'CHC' => ' CHC',
470             'CHD' => ' CHD',
471             'CMA' => ' CMA',
472             'CMB' => ' CMB',
473             'CMC' => ' CMC',
474             'CMD' => ' CMD',
475             'CZ' => ' CZ ',
476             'CZ2' => ' CZ2',
477             'CZ3' => ' CZ3',
478             'FE' => 'FE ',
479             'H' => ' H ',
480             'HA' => ' HA ',
481             'HB' => ' HB ',
482             'HG' => ' HG ',
483             'HE' => ' HE ',
484             'HZ' => ' HZ ',
485             'HH' => ' HH ',
486             '1H' => '1H ',
487             '2H' => '2H ',
488             '3H' => '3H ',
489             '1HA' => '1HA ',
490             '1HB' => '1HB ',
491             '1HD' => '1HD ',
492             '1HE' => '1HE ',
493             '1HG' => '1HG ',
494             '1HZ' => '1HZ ',
495             '2HA' => '2HA ',
496             '2HB' => '2HB ',
497             '2HD' => '2HD ',
498             '2HE' => '2HE ',
499             '2HG' => '2HG ',
500             '1HZ' => '1HZ ',
501             '2HZ' => '2HZ ',
502             '3HB' => '3HB ',
503             '3HE' => '3HE ',
504             '3HZ' => '3HZ ',
505             'N' => ' N ',
506             'NA' => ' NA ',
507             'NB' => ' NB ',
508             'NC' => ' NC ',
509             'ND' => ' ND ',
510             'N A' => ' N A',
511             'N B' => ' N B',
512             'N C' => ' N C',
513             'N D' => ' N D',
514             'N1' => ' N1 ',
515             'N2' => ' N2 ',
516             'N3' => ' N3 ',
517             'ND1' => ' ND1',
518             'ND2' => ' ND2',
519             'NE' => ' NE ',
520             'NE1' => ' NE1',
521             'NE2' => ' NE2',
522             'NH1' => ' NH1',
523             'NH2' => ' NH2',
524             'NZ' => ' NZ ',
525             'O' => ' O ',
526             'O1' => ' O1 ',
527             'O1A' => ' O1A',
528             'O1D' => ' O1D',
529             'O2' => ' O2 ',
530             'O2A' => ' O2A',
531             'O2D' => ' O2D',
532             'O3' => ' O3 ',
533             'O4' => ' O4 ',
534             'O5' => ' O5 ',
535             'O6' => ' O6 ',
536             'OD1' => ' OD1',
537             'OD2' => ' OD2',
538             'OE1' => ' OE1',
539             'OE2' => ' OE2',
540             'OG' => ' OG ',
541             'OG1' => ' OG1',
542             'OH' => ' OH ',
543             'OXT' => ' OXT',
544             'S' => ' S ',
545             'SD' => ' SD ',
546             'SG' => ' SG '
547             );
548              
549 2         5 my $self = shift;
550 2         3 my $n = @_;
551 2         4 my $type;
552             my $anum;
553 0         0 my $aname;
554 0         0 my $rname;
555 0         0 my $chain;
556 0         0 my $rnum;
557 0         0 my $xx;
558 0         0 my $yy;
559 0         0 my $zz;
560 0         0 my $coor;
561 0         0 my $beta;
562 2         5 local *OFIL;
563 2         3 my $nmodels = 0;
564 2 50       7 if ($n == 0)
565             {
566 2         9 *OFIL = *STDOUT;
567             }
568             else
569             {
570             #$nmodels=&models(0,"$_[0]") if (-e $_[0]);
571 0 0       0 open (OFIL,">>$_[0]") or die "Error in print $!\n";
572             }
573             #$nmodels++;
574             #printf OFIL "MODEL %8i\n",$nmodels;
575 2         8 my $nm = $self -> size;
576 2         3 my $line;
577 2         8 for (my $i = 0; $i < $nm; $i++)
578             {
579 58         105 my $iatom = $self -> atom($i);
580 58         110 $type = $iatom->type;
581 58         111 $anum = $iatom->number;
582 58         119 $aname = $iatom->name;
583 58         97 $rname = $iatom->residue_name;
584 58         98 $chain = $iatom->chain;
585 58         94 $rnum = $iatom->residue_number;
586 58         127 $xx = $iatom->x;
587 58         101 $yy = $iatom->y;
588 58         99 $zz = $iatom->z;
589 58         96 $coor = $iatom->occupancy;
590 58         92 $beta = $iatom->beta;
591             #give the correct indentation to the atom name
592 58 50       145 $aname = $atom_name{$aname} if ( defined $atom_name{$aname} );
593 58         699 $line = sprintf "%-6s%5i %4s %3s %1s%4i %8.3f%8.3f%8.3f%6.2f%6.2f\n",$type,$anum,$aname,$rname,$chain,$rnum,$xx,$yy,$zz,$coor,$beta;
594 58         203 print OFIL $line;
595             }
596 2         4 print OFIL "END\n";
597 2 50       57 close OFIL if ($n >= 1);
598             }
599              
600              
601             #compute the geometric center of an atom selection
602             sub center
603             {
604 3     3   12 my $self = shift;
605 3         7 my $nrd = $self->size;
606 3         5 my $xx=0.00e0;
607 3         4 my $yy=0.00e0;
608 3         4 my $zz=0.00e0;
609 3         13 for(my $i=0;$i < $nrd; $i++)
610             {
611 87         159 my $iatom = $self -> atom($i);
612 87         156 $xx+=$iatom ->x;
613 87         173 $yy+=$iatom ->y;
614 87         145 $zz+=$iatom ->z;
615             }
616 3 50       12 $xx /= $nrd if ( $nrd > 0);
617 3 50       7 $yy /= $nrd if ( $nrd > 0);
618 3 50       7 $zz /= $nrd if ( $nrd > 0);
619 3         9 my $centroid = Bio::PDB::Structure::Atom -> new;
620 3         9 $centroid ->x($xx);
621 3         10 $centroid ->y($yy);
622 3         8 $centroid ->z($zz);
623 3         8 $centroid ->type("HETATM");
624 3         7 $centroid ->name("CM");
625 3         7 $centroid ->number(0);
626 3         6 $centroid ->residue_name("DUM");
627 3         5 $centroid ->residue_number("0");
628 3         6 $centroid ->chain(" ");
629 3         7 return $centroid;
630             }
631              
632             #compute the center of mass of an atom selection
633             sub cm
634             {
635             #These atoms usually have additional letters or numbers in their names
636 1     1   9 my %mass1=(
637             H => 1,
638             N => 14,
639             C => 12,
640             O => 16,
641             P => 15,
642             S => 32
643             );
644             #These atoms usually appear without extra characters in their names
645 1         10 my %mass2=(
646             K => 39,
647             FE => 56,
648             CO => 59,
649             CL => 35,
650             MG => 24,
651             NA => 23,
652             NI => 59,
653             CU => 64,
654             ZN => 65
655             );
656 1         2 my $self = shift;
657 1         3 my $nrd = $self -> size;
658 1         1 my $ii;
659             my $jj;
660 1         2 my $xx=0;
661 1         1 my $yy=0;
662 1         2 my $zz=0;
663 1         2 my $pre;
664             my $match;
665 0         0 my $aname;
666 1         12 my $tmass =0;
667 1         5 my @kmass1 = keys %mass1;
668              
669 1         4 for(my $i=0;$i < $nrd; $i++)
670             {
671 29         35 $pre = 0;
672 29         31 $match = 0;
673 29         220 my $iatom = $self->atom($i);
674 29         60 $aname = uc($iatom -> name);
675 29 50       250 $match = 2 if ( defined($mass2{$aname}) );
676 29 50       62 unless ($match)
677             {
678 29         36 foreach $ii (@kmass1)
679             {
680 109 100       2475 if ( $aname =~/$ii/ )
681             {
682 29         202 $match = 1;
683 29         551 $jj = $ii;
684 29         43 last;
685             }
686             }
687             }
688 29 50       52 if ($match == 1)
    0          
689             {
690 29         45 $pre = $mass1{$jj};
691             }
692             elsif ($match == 2)
693             {
694 0         0 $pre = $mass2{$aname};
695             }
696             else
697             {
698             #atom's mass is not known, assume carbonlike
699 0         0 $pre = 12;
700             }
701 29         241 $xx+= $pre * $iatom->x;
702 29         64 $yy+= $pre * $iatom->y;
703 29         205 $zz+= $pre * $iatom->z;
704 29         251 $tmass+=$pre;
705             }
706 1 50       6 $xx /= $tmass if ( $tmass > 0);
707 1 50       15 $yy /= $tmass if ( $tmass > 0);
708 1 50       4 $zz /= $tmass if ( $tmass > 0);
709 1         5 my $centroid = Bio::PDB::Structure::Atom -> new;
710 1         4 $centroid ->x($xx);
711 1         4 $centroid ->y($yy);
712 1         4 $centroid ->z($zz);
713 1         4 $centroid ->type("HETATM");
714 1         4 $centroid ->name("CM");
715 1         3 $centroid ->number(0);
716 1         4 $centroid ->residue_name("DUM");
717 1         3 $centroid ->residue_number("0");
718 1         3 $centroid ->chain(" ");
719 1         10 return $centroid;
720             }
721              
722              
723             #translate an atom selection by the specified vector
724             sub translate
725             {
726 2     2   10 my $self = shift;
727 2         4 my $n = @_;
728 2 50       9 die "Error in translate: need an atom list an x,y,z\n" unless ($n == 3);
729 2         3 my $xt = shift;
730 2         2 my $yt = shift;
731 2         4 my $zt = shift;
732 2         6 my $nrd = $self->size;
733              
734 2         8 for (my $i=0; $i < $nrd; $i++)
735             {
736 58         97 my $iatom = $self->atom($i);
737 58         114 $iatom->x(($iatom->x + $xt));
738 58         126 $iatom->y(($iatom->y + $yt));
739 58         109 $iatom->z(($iatom->z + $zt));
740             }
741             return
742 2         7 }
743              
744             #rotate an atom selection by the specified matrix, Ax = x'.
745             #the matrix is specified by a flat list of the matrix elements.
746             sub rotate
747             {
748 2     2   15 my $self = shift;
749 2         5 my $n = @_;
750 2 50       12 die "Error in rotate: need an atom list and a 9 element array\n"
751             unless ($n == 9);
752 2         5 my $u11 = shift;
753 2         3 my $u12 = shift;
754 2         3 my $u13 = shift;
755 2         3 my $u21 = shift;
756 2         3 my $u22 = shift;
757 2         3 my $u23 = shift;
758 2         3 my $u31 = shift;
759 2         2 my $u32 = shift;
760 2         3 my $u33 = shift;
761 2         4 my ($x1,$y1,$z1);
762 0         0 my ($x2,$y2,$z2);
763              
764 2         6 my $nrd = $self->size;
765            
766 2         10 for (my $i=0; $i < $nrd; $i++)
767             {
768 58         108 my $iatom = $self -> atom($i);
769 58         99 $x1 = $iatom ->x;
770 58         110 $y1 = $iatom ->y;
771 58         104 $z1 = $iatom ->z;
772 58         129 $x2 = $u11*$x1 + $u12*$y1 + $u13*$z1;
773 58         83 $y2 = $u21*$x1 + $u22*$y1 + $u23*$z1;
774 58         82 $z2 = $u31*$x1 + $u32*$y1 + $u33*$z1;
775 58         100 $iatom->x($x2);
776 58         103 $iatom->y($y2);
777 58         108 $iatom->z($z2);
778             }
779             return
780 2         6 }
781              
782             #combine rotate and translate to facilitate supperposition of structures
783             #first 9 components represent rotation 3 last components rep. translation
784             sub rotate_translate
785             {
786 1     1   7 my $self = shift;
787 1         3 my $n = @_;
788 1 50       4 die "Error in rotate_translate: need a rotation matrix and a translation vector\n" unless ($n == 12);
789 1         2 my @mt;
790             my @vt;
791 1         5 for(my $i=0; $i < 9; $i++)
792             {
793 9         20 $mt[$i] = shift;
794             }
795 1         4 for(my $i=0; $i < 3; $i++)
796             {
797 3         7 $vt[$i] = shift;
798             }
799 1         3 $self->rotate(@mt);
800 1         5 $self->translate(@vt);
801             return
802 1         5 }
803              
804             #do a superposition of an atom list to a reference (2nd argument)
805             #selections must have the same number of atoms
806             #Using method from: S. Kearsley, Acta Cryst. A45, 208-210 1989
807             sub superpose
808             {
809 1     1   590 my $self1 = shift;
810 1         4 my $n = @_;
811 1 50       5 die "Error in superpose: need an atom list (reference)\n"
812             unless ($n == 1);
813 1         4 my $self2 = shift;
814 1         4 my $nrd1 = $self1->size;
815 1         4 my $nrd2 = $self2->size;
816 1         2 my ($x1,$y1,$z1);
817 0         0 my ($x2,$y2,$z2);
818 0         0 my ($xm,$ym,$zm);
819 0         0 my ($xp,$yp,$zp);
820 0         0 my ($Sxmxm, $Sxpxp, $Symym, $Sypyp, $Szmzm, $Szpzp);
821 0         0 my ($Sxmym, $Sxmyp, $Sxpym, $Sxpyp);
822 0         0 my ($Sxmzm, $Sxmzp, $Sxpzm, $Sxpzp);
823 0         0 my ($Symzm, $Symzp, $Sypzm, $Sypzp);
824 1 50       4 die "superpose error: lists must have same number of atoms\n"
825             unless ($nrd1 == $nrd2);
826              
827             #get the geometric center of the molecules
828 1         5 my $gc1 = $self1 -> center;
829 1         5 my $gc2 = $self2 -> center;
830              
831             #construct a 4X4 matrix in the quaternion representation
832 1         6 for(my $i=0; $i<$nrd1 ; $i++)
833             {
834 29         45 my $iatom1=$self1->atom($i);
835 29         61 $x1 = $iatom1->x - $gc1->x;
836 29         52 $y1 = $iatom1->y - $gc1->y;
837 29         48 $z1 = $iatom1->z - $gc1->z;
838            
839 29         50 my $iatom2=$self2->atom($i);
840 29         47 $x2 = $iatom2->x - $gc2->x;
841 29         52 $y2 = $iatom2->y - $gc2->y;
842 29         49 $z2 = $iatom2->z - $gc2->z;
843              
844 29         32 $xm = ($x1 - $x2);
845 29         30 $xp = ($x1 + $x2);
846 29         29 $ym = ($y1 - $y2);
847 29         30 $yp = ($y1 + $y2);
848 29         29 $zm = ($z1 - $z2);
849 29         98 $zp = ($z1 + $z2);
850              
851 29         31 $Sxmxm += $xm*$xm;
852 29         31 $Sxpxp += $xp*$xp;
853 29         31 $Symym += $ym*$ym;
854 29         32 $Sypyp += $yp*$yp;
855 29         26 $Szmzm += $zm*$zm;
856 29         29 $Szpzp += $zp*$zp;
857              
858 29         33 $Sxmym += $xm*$ym;
859 29         27 $Sxmyp += $xm*$yp;
860 29         31 $Sxpym += $xp*$ym;
861 29         30 $Sxpyp += $xp*$yp;
862              
863 29         29 $Sxmzm += $xm*$zm;
864 29         29 $Sxmzp += $xm*$zp;
865 29         27 $Sxpzm += $xp*$zm;
866 29         28 $Sxpzp += $xp*$zp;
867              
868 29         25 $Symzm += $ym*$zm;
869 29         31 $Symzp += $ym*$zp;
870 29         49 $Sypzm += $yp*$zm;
871 29         75 $Sypzp += $yp*$zp;
872             }
873 1         2 my @m;
874 1         3 $m[0]= $Sxmxm + $Symym + $Szmzm;
875 1         3 $m[1]= $Sypzm - $Symzp;
876 1         3 $m[2]= $Sxmzp - $Sxpzm;
877 1         3 $m[3]= $Sxpym - $Sxmyp;
878 1         2 $m[4]= $m[1];
879 1         3 $m[5]= $Sypyp + $Szpzp + $Sxmxm;
880 1         3 $m[6]= $Sxmym - $Sxpyp;
881 1         2 $m[7]= $Sxmzm - $Sxpzp;
882 1         2 $m[8]= $m[2];
883 1         2 $m[9]= $m[6];
884 1         3 $m[10]= $Sxpxp + $Szpzp + $Symym;
885 1         2 $m[11]= $Symzm - $Sypzp;
886 1         2 $m[12]= $m[3];
887 1         3 $m[13]= $m[7];
888 1         2 $m[14]= $m[11];
889 1         3 $m[15]=$Sxpxp + $Sypyp + $Szmzm;
890             #compute the egienvectors and eigenvalues of the matrix
891 1         6 my ( $revec, $reval ) = &__diagonalize(@m);
892             #the smallest eigenvalue is the rmsd for the optimal alignment
893 1         4 my $rmsd = sqrt(abs($reval->[0])/ $nrd1 );
894             #fetch the optimal quaternion
895 1         1 my @q;
896 1         3 $q[0]=$revec->[0][0];
897 1         2 $q[1]=$revec->[1][0];
898 1         3 $q[2]=$revec->[2][0];
899 1         3 $q[3]=$revec->[3][0];
900             #construct the rotation matrix given by the quaternion
901 1         2 my @mt;
902 1         4 $mt[0] = $q[0]*$q[0] + $q[1]*$q[1] - $q[2]*$q[2] - $q[3]*$q[3];
903 1         9 $mt[1] = 2.0 * ($q[1] * $q[2] - $q[0] * $q[3]);
904 1         3 $mt[2] = 2.0 * ($q[1] * $q[3] + $q[0] * $q[2]);
905              
906              
907 1         4 $mt[3] = 2.0 * ($q[2] * $q[1] + $q[0] * $q[3]);
908 1         4 $mt[4] = $q[0]*$q[0] - $q[1]*$q[1] + $q[2]*$q[2] - $q[3]*$q[3];
909 1         4 $mt[5] = 2.0 * ($q[2] * $q[3] - $q[0] * $q[1]);
910              
911 1         3 $mt[6] = 2.0 *($q[3] * $q[1] - $q[0] * $q[2]);
912 1         4 $mt[7] = 2.0 * ($q[3] * $q[2] + $q[0] * $q[1]);
913 1         3 $mt[8] = $q[0]*$q[0] - $q[1]*$q[1] - $q[2]*$q[2] + $q[3]*$q[3];
914              
915             #compute the displacement vector
916 1         1 my @vt;
917 1         4 $vt[0] = $gc2->x - $mt[0]*$gc1->x - $mt[1]*$gc1->y - $mt[2]*$gc1->z;
918 1         4 $vt[1] = $gc2->y - $mt[3]*$gc1->x - $mt[4]*$gc1->y - $mt[5]*$gc1->z;
919 1         3 $vt[2] = $gc2->z - $mt[6]*$gc1->x - $mt[7]*$gc1->y - $mt[8]*$gc1->z;
920              
921             #return the transformation as one list rotation first
922 1         16 return ( @mt,@vt );
923             }
924              
925             #compute the rmsd between two atom selections (must have same number of atoms)
926             sub rmsd
927             {
928 1     1   7 my $self1 =shift;
929 1         3 my $n = @_;
930 1 50       4 die "Error in rmsd: an atom list\n" unless ($n == 1);
931 1         3 my $self2 = shift;
932 1         4 my $nrd1 = $self1 ->size;
933 1         3 my $nrd2 = $self2 ->size;
934 1         1 my ($dx,$dy,$dz);
935 1         3 my $rmsd=0;
936              
937 1 50       3 die "rmsd error: lists must have same number of atoms\n"
938             unless ($nrd1 == $nrd2);
939 1         7 for(my $i=0;$i<$nrd1;$i++)
940             {
941 29         49 my $iatom1 = $self1->atom($i);
942 29         45 my $iatom2 = $self2->atom($i);
943              
944 29         50 $dx = $iatom1->x - $iatom2->x;
945 29         49 $dy = $iatom1->y - $iatom2->y;
946 29         55 $dz = $iatom1->z - $iatom2->z;
947 29         102 $rmsd += $dx**2 + $dy**2 + $dz**2;
948             }
949 1 50       6 $rmsd =sqrt($rmsd /$nrd1) if ( $nrd1 > 0.0E0 );
950 1         4 return $rmsd;
951             }
952              
953             #some predefined atom lists
954             #return protein or nucleic acid atoms
955             sub protein
956             {
957 0     0   0 my $self = shift;
958 0         0 my $n = $self->size;
959 0         0 my $mol= Bio::PDB::Structure::Molecule -> new;
960 0         0 for( my $i=0; $i < $n; $i++)
961             {
962 0         0 my $iatom = $self->atom($i);
963 0 0       0 $mol -> push($iatom) if ( $iatom->type eq "ATOM");
964             }
965 0         0 return $mol;
966             }
967              
968             sub hetatoms
969             {
970 0     0   0 my $self = shift;
971 0         0 my $n = $self->size;
972 0         0 my $mol= Bio::PDB::Structure::Molecule -> new;
973 0         0 for( my $i=0; $i < $n; $i++)
974             {
975 0         0 my $iatom = $self->atom($i);
976 0 0       0 $mol -> push($iatom) if ( $iatom->type eq "HETATM");
977             }
978 0         0 return $mol;
979             }
980              
981             sub alpha
982             {
983 0     0   0 my $self = shift;
984 0         0 my $n = $self->size;
985 0         0 my $mol= Bio::PDB::Structure::Molecule -> new;
986 0         0 for( my $i=0; $i < $n; $i++)
987             {
988 0         0 my $iatom = $self->atom($i);
989 0 0 0     0 $mol -> push($iatom) if ( $iatom->type eq "ATOM" && $iatom->name eq "CA");
990             }
991 0         0 return $mol;
992             }
993              
994             sub backbone
995             {
996 0     0   0 my $self = shift;
997 0         0 my $n = $self->size;
998 0         0 my $mol= Bio::PDB::Structure::Molecule -> new;
999 0         0 my @batoms = ("N","C","CA","O","OXT");
1000 0         0 for( my $i=0; $i < $n; $i++)
1001             {
1002 0         0 my $iatom = $self->atom($i);
1003 0 0       0 next if ($iatom->type ne "ATOM");
1004 0         0 my $name = $iatom->name;
1005 0 0       0 $mol -> push($iatom) if ( grep /^$name$/,@batoms );
1006             }
1007 0         0 return $mol;
1008             }
1009              
1010             sub sidechains
1011             {
1012 0     0   0 my $self = shift;
1013 0         0 my $n = $self->size;
1014 0         0 my $mol= Bio::PDB::Structure::Molecule -> new;
1015 0         0 my @batoms = ("N","C","CA","O","H","HA","OXT");
1016 0         0 for( my $i=0; $i < $n; $i++)
1017             {
1018 0         0 my $iatom = $self->atom($i);
1019 0 0       0 next if ($iatom->type ne "ATOM");
1020 0         0 my $name = $iatom->name;
1021 0 0       0 $mol -> push($iatom) if (not grep /^$name$/,@batoms );
1022             }
1023 0         0 return $mol;
1024             }
1025              
1026             #get a list of atoms specified by user input residue_name,residue_number,
1027             #chain_name,atom_name,atom_number;
1028             sub list_atoms
1029             {
1030 0     0   0 my $self = shift;
1031 0         0 my $ni = @_;
1032 0 0       0 die "Error in list_atom: need a logical expression\n" if ($ni != 1);
1033 0         0 my $logic = shift;
1034 0         0 my $n = $self->size;
1035 0         0 my @i2n = (
1036             "RESIDUE_NAME",
1037             "RESIDUE_NUMBER",
1038             "TYPE",
1039             "NUMBER",
1040             "NAME",
1041             "CHAIN",
1042             "X",
1043             "Y",
1044             "Z",
1045             "OCCUPANCY",
1046             "BETA",
1047             "ALT",
1048             "INSERTION_CODE"
1049             );
1050 0         0 my @i2v = (
1051             '$iatom->residue_name',
1052             '$iatom->residue_number',
1053             '$iatom->type',
1054             '$iatom->number',
1055             '$iatom->name',
1056             '$iatom->chain',
1057             '$iatom->x',
1058             '$iatom->y',
1059             '$iatom->z',
1060             '$iatom->occupancy',
1061             '$iatom->beta',
1062             '$iatom->alt',
1063             '$iatom->insertion_code'
1064             );
1065             #so as to be able to write logical expressions in a natural format we have
1066             #to do a bit of extra processing
1067             #save expressions between quotes to protect them
1068 0         0 my @temp;
1069 0         0 while ($logic =~s/(\"\w+\")/!!/)
1070             {
1071 0         0 CORE::push (@temp,$1);
1072             }
1073 0         0 $logic= uc($logic);
1074             #now substitute with method calls
1075 0         0 for (my $i =0; $i<13; $i++)
1076             {
1077 0         0 $logic =~ s/$i2n[$i]/$i2v[$i]/g;
1078             }
1079 0         0 $logic= lc($logic);
1080             #restore expressions between quotes
1081 0         0 foreach my $itemp (@temp)
1082             {
1083 0         0 $logic=~s/!!/$itemp/;
1084             }
1085             #print "$logic\n";
1086 0         0 my $mol = Bio::PDB::Structure::Molecule -> new;
1087 0         0 my $iatom= $self-> atom(0);
1088 0         0 eval $logic;
1089 0 0       0 die "Error with logical expression for list_atoms call\n" if $@;
1090 0         0 for (my $i=0; $i< $n; $i++)
1091             {
1092 0         0 $iatom = $self-> atom($i);
1093 0 0       0 $mol -> push($iatom) if ( eval $logic );
1094             }
1095 0         0 return $mol;
1096             }
1097              
1098             #Jacobi diagonalizer
1099             sub __diagonalize {
1100 1     1   2 my ($onorm, $dnorm);
1101 0         0 my ($b,$dma,$q,$t,$c,$s);
1102 0         0 my ($atemp, $vtemp, $dtemp);
1103 0         0 my ($i,$j,$k,$l);
1104 0         0 my @a;
1105 0         0 my @v;
1106 0         0 my @d;
1107 1         3 my $nrot = 30; #number of sweeps
1108              
1109 1         4 for ($i = 0; $i < 4; $i++)
1110             {
1111 4         10 for ($j = 0; $j < 4; $j++)
1112             {
1113 16         28 $a[$i][$j] =$_[4*$i + $j];
1114 16         53 $v[$i][$j] = 0.0;
1115             }
1116             }
1117              
1118 1         4 for ($j = 0; $j <= 3; $j++)
1119             {
1120 4         5 $v[$j][$j] = 1.0;
1121 4         12 $d[$j] = $a[$j][$j];
1122             }
1123              
1124 1         9 for ($l = 1; $l <= $nrot; $l++)
1125             {
1126 5         6 $dnorm = 0.0;
1127 5         6 $onorm = 0.0;
1128 5         10 for ($j = 0; $j <= 3; $j++)
1129             {
1130 20         24 $dnorm += abs($d[$j]);
1131 20         44 for ($i = 0; $i <= $j - 1; $i++)
1132             {
1133 30         81 $onorm += abs($a[$i][$j]);
1134             }
1135             }
1136 5 100       12 last if(($onorm/$dnorm) <= 1.0e-12);
1137 4         10 for ($j = 1; $j <= 3; $j++)
1138             {
1139 12         24 for ($i = 0; $i <= $j - 1; $i++)
1140             {
1141 24         30 $b = $a[$i][$j];
1142 24 50       43 if(abs($b) > 0.0)
1143             {
1144 24         32 $dma = $d[$j] - $d[$i];
1145 24 100       41 if((abs($dma) + abs($b)) <= abs($dma))
1146             {
1147 2         4 $t = $b / $dma;
1148             }
1149             else
1150             {
1151 22         26 $q = 0.5 * $dma / $b;
1152 22         34 $t = 1.0/(abs($q) + sqrt(1.0+$q*$q));
1153 22 100       40 $t *= -1.0 if($q < 0.0);
1154             }
1155 24         31 $c = 1.0/sqrt($t * $t + 1.0);
1156 24         23 $s = $t * $c;
1157 24         27 $a[$i][$j] = 0.0;
1158 24         51 for ($k = 0; $k <= $i-1; $k++)
1159             {
1160 16         38 $atemp = $c * $a[$k][$i] - $s * $a[$k][$j];
1161 16         26 $a[$k][$j] = $s * $a[$k][$i] + $c * $a[$k][$j];
1162 16         34 $a[$k][$i] = $atemp;
1163             }
1164 24         45 for ($k = $i+1; $k <= $j-1; $k++)
1165             {
1166 16         30 $atemp = $c * $a[$i][$k] - $s * $a[$k][$j];
1167 16         23 $a[$k][$j] = $s * $a[$i][$k] + $c * $a[$k][$j];
1168 16         32 $a[$i][$k] = $atemp;
1169             }
1170 24         47 for ($k = $j+1; $k <= 3; $k++)
1171             {
1172 16         29 $atemp = $c * $a[$i][$k] - $s * $a[$j][$k];
1173 16         24 $a[$j][$k] = $s * $a[$i][$k] + $c * $a[$j][$k];
1174 16         125 $a[$i][$k] = $atemp;
1175             }
1176 24         48 for ($k = 0; $k <= 3; $k++)
1177             {
1178 96         144 $vtemp = $c * $v[$k][$i] - $s * $v[$k][$j];
1179 96         142 $v[$k][$j] = $s * $v[$k][$i] + $c * $v[$k][$j];
1180 96         181 $v[$k][$i] = $vtemp;
1181             }
1182 24         54 $dtemp = $c*$c*$d[$i] + $s*$s*$d[$j] - 2.0*$c*$s*$b;
1183 24         41 $d[$j] = $s*$s*$d[$i] + $c*$c*$d[$j] + 2.0*$c*$s*$b;
1184 24         69 $d[$i] = $dtemp;
1185             }
1186             }
1187             }
1188             }
1189              
1190 1         2 $nrot = $l;
1191 1         5 for ($j = 0; $j <= 2; $j++)
1192             {
1193 3         3 $k = $j;
1194 3         4 $dtemp = $d[$k];
1195 3         9 for ($i = $j+1; $i <= 3; $i++)
1196             {
1197 6 100       16 if($d[$i] < $dtemp)
1198             {
1199 2         3 $k = $i;
1200 2         4 $dtemp = $d[$k];
1201             }
1202             }
1203              
1204 3 100       7 if($k > $j)
1205             {
1206 2         11 $d[$k] = $d[$j];
1207 2         4 $d[$j] = $dtemp;
1208 2         6 for ($i = 0; $i <= 3; $i++)
1209             {
1210 8         9 $dtemp = $v[$i][$k];
1211 8         9 $v[$i][$k] = $v[$i][$j];
1212 8         19 $v[$i][$j] = $dtemp;
1213             }
1214             }
1215             }
1216            
1217 1         7 return (\@v,\@d)
1218             }
1219              
1220             1;
1221             __END__