File Coverage

blib/lib/HackaMol/Molecule.pm
Criterion Covered Total %
statement 109 112 97.3
branch 20 20 100.0
condition 2 4 50.0
subroutine 21 22 95.4
pod 9 12 75.0
total 161 170 94.7


line stmt bran cond sub pod time code
1             $HackaMol::Molecule::VERSION = '0.052';
2             #ABSTRACT: Molecule class for HackaMol
3             use 5.008;
4 12     12   191316 use Moose;
  12         47  
5 12     12   546 use namespace::autoclean;
  12         281988  
  12         95  
6 12     12   71512 use Carp;
  12         7282  
  12         110  
7 12     12   988 use Math::Trig;
  12         34  
  12         824  
8 12     12   561 use Scalar::Util qw(refaddr);
  12         12007  
  12         2045  
9 12     12   95 use MooseX::StrictConstructor;
  12         31  
  12         496  
10 12     12   488  
  12         18655  
  12         88  
11             #use MooseX::Storage;
12              
13             with 'HackaMol::Roles::PhysVecMVRRole',
14             'HackaMol::Roles::BondsAnglesDihedralsRole', 'HackaMol::Roles::QmMolRole'; #,
15             # 'HackaMol::Roles::SelectionRole';
16              
17             #, Storage( 'format' => 'JSON', 'io' => 'File' );
18              
19             extends 'HackaMol::AtomGroup';
20              
21             has 'groups' => (
22             traits => ['Array'],
23             is => 'ro',
24             isa => 'ArrayRef[HackaMol::AtomGroup]',
25             default => sub { [] },
26             lazy => 1,
27             handles => {
28             has_groups => 'count',
29             push_groups => 'push',
30             get_groups => 'get',
31             set_groups => 'set',
32             all_groups => 'elements',
33             sort_groups => 'sort',
34             insert_groups => 'insert',
35             count_groups => 'count',
36             delete_groups => 'delete',
37             clear_groups => 'clear',
38             select_groups => 'grep',
39             map_groups => 'map',
40             },
41             );
42              
43             # an array to map t to some other label (e.g. model number from pdb)
44             has 'model_ids' => (
45             traits => ['Array'],
46             is => 'ro',
47             isa => 'ArrayRef[Str]',
48             default => sub { [] },
49             predicate => 'has_models',
50             handles => {
51             "push_model_ids" => 'push',
52             "get_model_id" => 'get',
53             "set_model_id" => 'set',
54             "all_model_ids" => 'elements',
55             "count_model_ids" => 'count',
56             },
57             lazy => 1,
58             );
59              
60             my $self = shift;
61             foreach my $bond ( $self->all_bonds ) {
62 24     24 0 57 $_->inc_bond_count foreach $bond->all_atoms;
63 24         965 }
64 9         237  
65             #all the molecule to be build from groups or atoms
66             return if $self->has_atoms;
67              
68 24 100       868 if ( $self->has_groups ) {
69             $self->push_atoms( $self->map_groups( sub { $_->all_atoms } ) );
70 4 100       130 }
71 2     2   68 return;
  2         67  
72             }
73 4         120  
74             after 'push_groups' => sub {
75              
76             # if you push a group onto a molecule, the atoms should be added unless they
77             # exist!
78             my $self = shift;
79             my @groups = @_;
80             foreach my $group (@groups) {
81             foreach my $atom ( $group->all_atoms ) {
82             unless ( grep { $atom == $_ } $self->all_atoms ) {
83             $self->push_atoms($atom);
84             }
85              
86             # debug
87             # else {
88             # print "found it\n $atom \n" if grep {$atom == $_} $self->all_atoms;
89             # }
90             }
91             }
92             };
93              
94             my $self = shift;
95             my $t = $self->t;
96             if (@_) {
97 4     4 0 11 my $new_q = shift;
98 4         14 $self->set_charges( $t, $new_q );
99 4 100       23 }
100 1         2 return $self->get_charges($t) || 0; # default to 0
101 1         33 }
102              
103 4   100     117 # need to increase atom bond_count when push
104             after 'push_bonds' => sub {
105             my $self = shift;
106             foreach my $bond (@_) {
107             $_->inc_bond_count foreach $bond->all_atoms;
108             }
109             };
110              
111             # need to reduce atom bond_count when set,delete, or clear
112             before 'delete_bonds' => sub {
113             my $self = shift;
114             my $bond = $self->get_bonds(@_);
115             $_->dec_bond_count foreach $bond->all_atoms;
116             };
117              
118             around 'set_bonds' => sub {
119             my ( $orig, $self, $index, $bond ) = @_;
120             my $oldbond = $self->get_bonds($index);
121             if ( defined($oldbond) ) {
122             $_->dec_bond_count foreach $oldbond->all_atoms;
123             }
124             $_->inc_bond_count foreach $bond->all_atoms;
125             $self->$orig( $index, $bond );
126             };
127              
128             before 'clear_bonds' => sub {
129             my $self = shift;
130             foreach my $bond ( $self->all_bonds ) {
131             $_->dec_bond_count foreach $bond->all_atoms;
132             }
133             };
134              
135             after 't' => sub {
136             my $self = shift;
137             $self->gt(@_) if (@_); # set t for all in group
138             };
139              
140             my $self = shift;
141             my $mass = 0;
142             $mass += $_->mass foreach $self->all_atoms;
143             return ($mass);
144 1     1   3 }
145 1         2  
146 1         34 my @atoms = shift->all_atoms;
147 1         59 my $offset = shift || 1;
148             $atoms[$_]->{serial} = $_ + $offset foreach ( 0 .. $#atoms );
149             }
150              
151 0     0 0 0  
152 0   0     0 return ( shift->_all_these_atoms( 'dihedrals', @_ ) );
153 0         0 }
154              
155              
156 2     2 1 9 #these bonds, these angles, these dihedrals
157 2     2 1 6 #this bond, this angle, this dihedral
158             my $self = shift;
159             my $these = shift;
160 1     1 1 5 my @atoms = @_;
161             my $method = "all_$these";
162             my @all_these = $self->$method;
163             my @atoms_these;
164             foreach my $this (@all_these) {
165             my @thatoms = $this->all_atoms;
166             foreach my $atom (@atoms) {
167 5     5   6 push @atoms_these, $this
168 5         6 if ( grep { refaddr($atom) == refaddr($_) } @thatoms );
169 5         9 }
170 5         11 }
171 5         176 return (@atoms_these);
172 5         17 }
173 5         9  
174 321         8554 my $self = shift;
175 321         407 croak "pass Bond, trans distance (Angstroms), 1+ groups to trans"
176             unless @_ > 2;
177 513 100       584 my $t = $self->t;
  1470         2823  
178             my ( $bond, $dist ) = ( shift, shift );
179             my $vec = $bond->bond_vector;
180 5         25 my @groups = @_;
181             my $tvec = $dist * $vec->versor;
182             $_->translate( $tvec, $t ) foreach @groups;
183             }
184 2     2 1 237  
185 2 100       22 my $self = shift;
186             croak "pass Bond, trans distance (Angstroms), 1+ atoms to trans"
187 1         8 unless @_ > 2;
188 1         8 my $t = $self->t;
189 1         5 my ( $bond, $dist ) = ( shift, shift );
190 1         3 my $vec = $bond->bond_vector;
191 1         10 my @atoms = @_;
192 1         8 my $tvec = $dist * $vec->versor;
193             $_->set_coords( $t, $_->xyz + $tvec ) foreach @atoms;
194             }
195              
196 2     2 1 1483 my $self = shift;
197 2 100       14 croak "pass Angle, ang to rotate (degrees), 1+ groups effected"
198             unless @_ > 2;
199 1         4 my $t = $self->t;
200 1         6 my ( $angle, $dang ) = ( shift, shift );
201 1         4 my $origin = $angle->get_atoms(1)->get_coords($t);
202 1         6 my $rvec = $angle->ang_normvec;
203 1         6 my @groups = @_;
204 1         6 $_->rotate( $rvec, $dang, $origin, $t ) foreach @groups;
205             }
206              
207             my $self = shift;
208 3     3 1 620 croak "pass Angle, ang to rotate (degrees), 1+ groups effected"
209 3 100       16 unless @_ > 2;
210             my $t = $self->t;
211 2         8 my ( $angle, $dang ) = ( shift, shift );
212 2         23 my $origin = $angle->get_atoms(1)->get_coords($t);
213 2         68 my $rvec = $angle->ang_normvec;
214 2         9 my @atoms = @_;
215 2         6  
216 2         10 my @cor =
217             map { $_->get_coords($t) - $origin } @atoms; #shift origin
218             my @rcor = $rvec->rotate_3d( deg2rad($dang), @cor );
219              
220 2     2 1 1019 #shift origin back
221 2 100       15 $atoms[$_]->set_coords( $t, $rcor[$_] + $origin ) foreach 0 .. $#rcor;
222             }
223 1         4  
224 1         7 my $self = shift;
225 1         29 croak "pass Dihedral, rotation angle (deg), atoms to rotate" unless @_ > 2;
226 1         4 my $t = $self->t;
227 1         9 my ( $dihe, $dang ) = ( shift, shift );
228             my ( $atom0, $ratom1, $ratom2, $atom3 ) = $dihe->all_atoms;
229             my $rvec = ( $ratom2->inter_dcoords($ratom1) )->versor;
230 1         2 my $origin = $ratom1->xyz;
  62         1596  
231 1         4 my @atoms = @_;
232             my @cor =
233             map { $_->get_coords($t) - $origin } @atoms; #shift origin too
234 1         353 my @rcor = $rvec->rotate_3d( deg2rad($dang), @cor );
235              
236             #shift origin back
237             $atoms[$_]->set_coords( $t, $rcor[$_] + $origin ) foreach 0 .. $#rcor;
238 64     64 1 1583  
239 64 100       122 }
240 63         156  
241 63         326 my $self = shift;
242 63         1773 croak "pass Dihedral, rotation angle (deg), atoms to rotate" unless @_ > 2;
243 63         150 my $t = $self->t;
244 63         162 my ( $dihe, $dang ) = ( shift, shift );
245 63         150 my ( $atom0, $ratom1, $ratom2, $atom3 ) = $dihe->all_atoms;
246             my $rvec = ( $ratom2->inter_dcoords($ratom1) )->versor;
247 63         106 my $origin = $ratom1->xyz;
  1024         26975  
248 63         182 my @groups = @_;
249             $_->rotate( $rvec, $dang, $origin, $t ) foreach @groups;
250              
251 63         8233 }
252              
253             __PACKAGE__->meta->make_immutable;
254              
255             1;
256 2     2 1 211  
257 2 100       22  
258 1         5 =pod
259 1         7  
260 1         30 =head1 NAME
261 1         4  
262 1         5 HackaMol::Molecule - Molecule class for HackaMol
263 1         4  
264 1         6 =head1 VERSION
265              
266             version 0.052
267              
268             =head1 SYNOPSIS
269              
270             use HackaMol;
271             use Math::Vector::Real;
272            
273             my $mol = HackaMol->new
274             ->pdbid_mol('1L2Y');
275            
276             $mol->translate(-$mol->COM);
277             $mol->rotate(V(1,0,0), 180, V(10,10,10));
278            
279             $mol->print_xyz;
280             # see examples
281              
282             =head1 DESCRIPTION
283              
284             The Molecule class provides methods and attributes for collections of atoms that may be divided
285             into groups, placed into bonds, angles, and dihedrals. The Molecule class extends the AtomGroup
286             parent class, which consumes the AtomGroupRole, and consumes PhysVecMVRRole, QmRole, and
287             BondsAnglesDihedralsRole. See the documentation of those classes and roles for details.
288              
289             In addition to Bonds, Angles, and Dihedrals, which also consume the AtomGroupRole, the Molecule
290             class has the atomgroups attr. The atomgroups attr is an ArrayRef[AtomGroup] with native array
291             traits that allows all the atoms in the Molecule to be grouped and regroup at will. Thus, the
292             Molecule class provides a suite of methods and attributes that is very powerful. For example,
293             a HackaMolX extension for proteins could group the atoms by sidechains and backbones, populate
294             bonds, and then use Math::Vector::Real objects to sample alternative conformations of the
295             sidechains and backbone.
296              
297             =head1 METHODS
298              
299             =head2 t
300              
301             t is the same attr as before. Molecule modifies t. the $mol->t accessor behaves as before. The $mol->(1)
302             setter $self->gt(1) to set t for all atoms in the molecule.
303              
304             =head2 push_groups_by_atom_attr
305              
306             takes atom attribute as argument. pushes the atoms into the atomgroup array by attribute
307              
308             =head2 all_bonds_atoms
309              
310             takes array of atoms as argument, returns array of bonds that includes 1 or more of those atoms
311              
312             =head2 all_angles_atoms
313              
314             takes array of atoms as argument, returns array of angles that includes 1 or
315             more of those atoms
316              
317             =head2 all_dihedrals_atoms
318              
319             takes array of atoms as argument, returns array of dihedrals that includes 1 or
320             more of those atoms
321              
322             =head2 bond_stretch_atoms
323              
324             takes Bond object, a distance (angstroms, typically), and active atoms as arguments.
325             translates the active atoms along the bond_vector by the distance and stores coordinates
326             in place ($atom->set_coords($mol->t,$translated_coors)).
327              
328             =head2 bond_stretch_groups
329              
330             takes Bond object, a distance (angstroms, typically), and active groups as arguments.
331             translates the atoms in the active groups along the bond_vector by the distance and
332             stores coordinates in place.
333              
334             =head2 angle_bend_atoms
335              
336             takes Angle object, an angle (degrees), and active atoms as arguments. rotates the active atoms
337             about the vector normal to be angle and stores rotated coordinates in place
338             ($atom->set_coords($mol->t,$rotated_coor)).
339              
340             =head2 angle_bend_groups
341              
342             takes Angle object, an angle (degrees), and active groups as arguments. rotates the atoms
343             in the active groups about the vector normal to be angle and stores rotated coordinates
344             in place ($atom->set_coords($mol->t,$rotated_coor)).
345              
346             =head2 dihedral_rotate_atoms
347              
348             takes Dihedral object, an angle (degrees), and active atoms as arguments. rotates the active atoms
349             about the dihedral and stores rotated coordinates in place
350             ($atom->set_coords($mol->t,$rotated_coor)).
351              
352             =head2 dihedral_rotate_groups
353              
354             takes Dihedral object, an angle (degrees), and active groups as arguments. rotates atoms in
355             groups about the dihedral and stores rotated coordinates in place
356             ($atom->set_coords($mol->t,$rotated_coor)).
357              
358             =head1 ARRAY METHODS
359              
360             =head2 push_groups, get_groups, set_groups, all_groups, count_groups, delete_groups, clear_groups
361              
362             ARRAY traits for the groups attribute, respectively: push, get, set, elements, count, delete, clear
363              
364             =head2 push_groups
365              
366             push bond on to groups array
367              
368             $group->push_groups($bond1, $bond2, @othergroups);
369              
370             =head2 all_groups
371              
372             returns array of all elements in groups array
373              
374             print $_->bond_order, "\n" foreach $group->all_groups;
375              
376             =head2 get_groups
377              
378             return element by index from groups array
379              
380             print $group->get_groups(1); # returns $bond2 from that pushed above
381              
382             =head2 set_groups
383              
384             set groups array by index
385              
386             $group->set_groups(1, $bond1);
387              
388             =head2 count_groups
389              
390             return number of groups in the array
391              
392             print $group->count_groups;
393              
394             =head2 has_groups
395              
396             same as count_groups, allows clearer conditional code. i.e. doing something if $mol->has_groups;
397              
398             =head2 push_bonds, set_bonds, delete_bonds, clear_bonds
399              
400             MODIFIED ARRAY traits for the bonds attribute provided by BondsAnglesDihedralsRole
401              
402             =head2 push_bonds
403              
404             before push_bonds, bond_count is incremented for all atoms in all bonds to be pushed.
405              
406             =head2 set_bonds
407              
408             around set_bonds, bound_count decremented for all atoms in bond being replaced. Then, bond_count is
409             incremented for all atoms in new bond
410              
411             =head2 delete_bonds
412              
413             before deleting bond, bond_count decremented for all atoms in bond.
414              
415             =head2 clear_bonds
416              
417             before clearing bonds, bond_count decremented for all atoms in all bonds.
418              
419             =head1 SEE ALSO
420              
421             =over 4
422              
423             =item *
424              
425             L<HackaMol::PhysVecMVRRole>
426              
427             =item *
428              
429             L<HackaMol::BondsAnglesDihedralsRole>
430              
431             =item *
432              
433             L<HackaMol::QmMolRole>
434              
435             =item *
436              
437             L<Chemistry::Molecule>
438              
439             =back
440              
441             =head1 EXTENDS
442              
443             =over 4
444              
445             =item * L<HackaMol::AtomGroup>
446              
447             =back
448              
449             =head1 CONSUMES
450              
451             =over 4
452              
453             =item * L<HackaMol::Roles::BondsAnglesDihedralsRole>
454              
455             =item * L<HackaMol::Roles::PhysVecMVRRole>
456              
457             =item * L<HackaMol::Roles::PhysVecMVRRole|HackaMol::Roles::BondsAnglesDihedralsRole|HackaMol::Roles::QmMolRole>
458              
459             =item * L<HackaMol::Roles::QmAtomRole>
460              
461             =item * L<HackaMol::Roles::QmMolRole>
462              
463             =back
464              
465             =head1 AUTHOR
466              
467             Demian Riccardi <demianriccardi@gmail.com>
468              
469             =head1 COPYRIGHT AND LICENSE
470              
471             This software is copyright (c) 2017 by Demian Riccardi.
472              
473             This is free software; you can redistribute it and/or modify it under
474             the same terms as the Perl 5 programming language system itself.
475              
476             =cut