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.053';
2             #ABSTRACT: Molecule class for HackaMol
3             use 5.008;
4 12     12   177675 use Moose;
  12         48  
5 12     12   530 use namespace::autoclean;
  12         290057  
  12         91  
6 12     12   71458 use Carp;
  12         8441  
  12         108  
7 12     12   1064 use Math::Trig;
  12         27  
  12         1122  
8 12     12   731 use Scalar::Util qw(refaddr);
  12         12843  
  12         1976  
9 12     12   81 use MooseX::StrictConstructor;
  12         24  
  12         503  
10 12     12   674  
  12         23428  
  12         87  
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 61 $_->inc_bond_count foreach $bond->all_atoms;
63 24         997 }
64 9         238  
65             #all the molecule to be build from groups or atoms
66             return if $self->has_atoms;
67              
68 24 100       789 if ( $self->has_groups ) {
69             $self->push_atoms( $self->map_groups( sub { $_->all_atoms } ) );
70 4 100       130 }
71 2     2   64 return;
  2         70  
72             }
73 4         111  
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 9 my $new_q = shift;
98 4         13 $self->set_charges( $t, $new_q );
99 4 100       27 }
100 1         2 return $self->get_charges($t) || 0; # default to 0
101 1         28 }
102              
103 4   100     105 # 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   2 }
145 1         1  
146 1         30 my @atoms = shift->all_atoms;
147 1         33 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 8 #these bonds, these angles, these dihedrals
157 2     2 1 5 #this bond, this angle, this dihedral
158             my $self = shift;
159             my $these = shift;
160 1     1 1 3 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   7 push @atoms_these, $this
168 5         6 if ( grep { refaddr($atom) == refaddr($_) } @thatoms );
169 5         8 }
170 5         8 }
171 5         170 return (@atoms_these);
172 5         13 }
173 5         12  
174 321         7250 my $self = shift;
175 321         362 croak "pass Bond, trans distance (Angstroms), 1+ groups to trans"
176             unless @_ > 2;
177 513 100       490 my $t = $self->t;
  1470         2433  
178             my ( $bond, $dist ) = ( shift, shift );
179             my $vec = $bond->bond_vector;
180 5         23 my @groups = @_;
181             my $tvec = $dist * $vec->versor;
182             $_->translate( $tvec, $t ) foreach @groups;
183             }
184 2     2 1 226  
185 2 100       20 my $self = shift;
186             croak "pass Bond, trans distance (Angstroms), 1+ atoms to trans"
187 1         6 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         6 my @atoms = @_;
192 1         8 my $tvec = $dist * $vec->versor;
193             $_->set_coords( $t, $_->xyz + $tvec ) foreach @atoms;
194             }
195              
196 2     2 1 1051 my $self = shift;
197 2 100       12 croak "pass Angle, ang to rotate (degrees), 1+ groups effected"
198             unless @_ > 2;
199 1         5 my $t = $self->t;
200 1         6 my ( $angle, $dang ) = ( shift, shift );
201 1         5 my $origin = $angle->get_atoms(1)->get_coords($t);
202 1         5 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 589 croak "pass Angle, ang to rotate (degrees), 1+ groups effected"
209 3 100       19 unless @_ > 2;
210             my $t = $self->t;
211 2         6 my ( $angle, $dang ) = ( shift, shift );
212 2         11 my $origin = $angle->get_atoms(1)->get_coords($t);
213 2         52 my $rvec = $angle->ang_normvec;
214 2         9 my @atoms = @_;
215 2         6  
216 2         9 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 995 #shift origin back
221 2 100       14 $atoms[$_]->set_coords( $t, $rcor[$_] + $origin ) foreach 0 .. $#rcor;
222             }
223 1         4  
224 1         6 my $self = shift;
225 1         26 croak "pass Dihedral, rotation angle (deg), atoms to rotate" unless @_ > 2;
226 1         5 my $t = $self->t;
227 1         7 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         1455  
231 1         6 my @atoms = @_;
232             my @cor =
233             map { $_->get_coords($t) - $origin } @atoms; #shift origin too
234 1         296 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 1387  
239 64 100       112 }
240 63         141  
241 63         292 my $self = shift;
242 63         1511 croak "pass Dihedral, rotation angle (deg), atoms to rotate" unless @_ > 2;
243 63         140 my $t = $self->t;
244 63         148 my ( $dihe, $dang ) = ( shift, shift );
245 63         140 my ( $atom0, $ratom1, $ratom2, $atom3 ) = $dihe->all_atoms;
246             my $rvec = ( $ratom2->inter_dcoords($ratom1) )->versor;
247 63         90 my $origin = $ratom1->xyz;
  1024         23499  
248 63         153 my @groups = @_;
249             $_->rotate( $rvec, $dang, $origin, $t ) foreach @groups;
250              
251 63         7625 }
252              
253             __PACKAGE__->meta->make_immutable;
254              
255             1;
256 2     2 1 189  
257 2 100       20  
258 1         5 =pod
259 1         6  
260 1         25 =head1 NAME
261 1         5  
262 1         5 HackaMol::Molecule - Molecule class for HackaMol
263 1         2  
264 1         7 =head1 VERSION
265              
266             version 0.053
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