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