File Coverage

blib/lib/HackaMol/Roles/PhysVecMVRRole.pm
Criterion Covered Total %
statement 143 143 100.0
branch 42 42 100.0
condition 3 3 100.0
subroutine 28 28 100.0
pod 19 22 86.3
total 235 238 98.7


line stmt bran cond sub pod time code
1             package HackaMol::Roles::PhysVecMVRRole;
2             $HackaMol::Roles::PhysVecMVRRole::VERSION = '0.051';
3             # ABSTRACT: Provides the core of HackaMol Atom and Molecule classes.
4 20     20   15546 use Math::Vector::Real;
  20         46344  
  20         1313  
5 20     20   3860 use Math::Trig;
  20         95283  
  20         2917  
6              
7             #use Moose::Util::TypeConstraints;
8 20     20   157 use Moose::Role;
  20         43  
  20         193  
9 20     20   112717 use Carp;
  20         68  
  20         41745  
10             #use Data::Structure::Util qw (unbless);
11             #use MooseX::Storage;
12              
13             #with Storage( 'format' => 'JSON', 'io' => 'File' );
14              
15             requires qw(_build_mass charge);
16              
17             #MooseX::Storage::Engine->add_custom_type_handler(
18             # 'Math::Vector::Real' => (
19             # expand => sub {my $v = shift; Math::Vector::Real->new(@{$v})},
20             # collapse => sub {my $mvr = shift; return (unbless($mvr)) },
21             # )
22             #);
23             #use Data::Dumper;
24             #MooseX::Storage::Engine->add_custom_type_handler(
25             # 'ArrayRef[Math::Vector::Real]' => (
26             # expand => sub {print Dumper $_; my @vs = map {Math::Vector::Real->new(@{$_}); print $_} @$_; return [@vs] },
27             # collapse => sub {my $a_rf = shift; my @vs = map {unbless($_)} @$a_rf; return \@vs },
28             # )
29             #);
30              
31              
32             has 't', is => 'rw', isa => 'Int|ScalarRef', default => 0;
33              
34             has "$_" => (
35             traits => ['Array'],
36             is => 'ro',
37             isa => 'ArrayRef[Math::Vector::Real]',
38             default => sub { [] },
39             handles => {
40             "push_$_" => 'push',
41             "get_$_" => 'get',
42             "delete_$_" => 'delete',
43             "set_$_" => 'set',
44             "all_$_" => 'elements',
45             "clear_$_" => 'clear',
46             "count_$_" => 'count',
47             },
48             lazy => 1,
49             ) for qw(coords forces);
50              
51             has "$_" => (
52             traits => ['Array'],
53             is => 'ro',
54             isa => 'ArrayRef[Num]',
55             default => sub { [] },
56             predicate => 'has_charges',
57             handles => {
58             "push_$_" => 'push',
59             "get_$_" => 'get',
60             "delete_$_" => 'delete',
61             "set_$_" => 'set',
62             "all_$_" => 'elements',
63             "clear_$_" => 'clear',
64             "count_$_" => 'count',
65             },
66             lazy => 1,
67             ) for qw(charges);
68              
69             has 'units', is => 'rw', isa => 'Str'; #flag for future use [SI]
70              
71             has 'origin' => (
72             is => 'rw',
73             isa => 'Math::Vector::Real',
74             default => sub { V( 0, 0, 0 ) },
75             lazy => 1,
76             );
77              
78             has 'xyzfree' => (
79             is => 'rw',
80             isa => 'ArrayRef[Int]',
81             default => sub { [ 1, 1, 1 ] },
82             lazy => 1,
83             trigger => \&_freedom,
84             clearer => 'clear_xyzfree',
85             );
86              
87             has 'mass' => (
88             is => 'rw',
89             isa => 'Num',
90             lazy => 1,
91             clearer => 'clear_mass',
92             builder => '_build_mass',
93             );
94              
95             sub _freedom {
96 3     3   8 my ( $self, $new, $old ) = @_;
97 3 100       4 if ( grep { $_ == 0 } @{$new} ) {
  9         23  
  3         7  
98 2         58 $self->is_fixed(1);
99             }
100             else {
101 1         33 $self->is_fixed(0);
102             }
103             }
104              
105             has 'is_fixed' => (
106             is => 'rw',
107             isa => 'Bool',
108             lazy => 1,
109             default => 0,
110             );
111              
112             #intraobject methods
113             sub intra_dcharges {
114 5     5 1 202 my $self = shift;
115 5 100       42 croak "intra_dcharges> pass initial and final time" unless ( @_ == 2 );
116 2         4 my $ti = shift;
117 2         3 my $tf = shift;
118 2         76 return ( $self->get_charges($tf) - $self->get_charges($ti) );
119             }
120              
121             sub mean_charges {
122 2     2 1 5 my $self = shift;
123 2         83 my @tcharges = $self->all_charges;
124 2         5 my $sum = 0;
125 2         9 $sum += $_ foreach @tcharges;
126 2         70 return ( $sum / $self->count_charges );
127             }
128              
129             sub msd_charges {
130 1     1 1 4 my $self = shift;
131 1         3 my $avg = $self->mean_charges;
132 1         35 my @tcharges = $self->all_charges;
133 1         2 my $sum = 0;
134 1         11 $sum += ( $_ - $avg )**2 foreach @tcharges;
135 1         35 return ( $sum / $self->count_charges );
136             }
137              
138             sub intra_dcoords {
139              
140             #M::V::R makes much simpler
141 5     5 1 212 my $self = shift;
142 5 100       39 croak "intra_dcoords> pass initial and final time" unless ( @_ == 2 );
143 2         4 my $ti = shift;
144 2         3 my $tf = shift;
145 2         75 return ( $self->get_coords($tf) - $self->get_coords($ti) );
146             }
147              
148             sub mean_coords {
149 2     2 1 701 my $self = shift;
150 2         86 my @tcoords = $self->all_coords;
151 2         11 my $sum = V( 0, 0, 0 );
152 2         18 $sum += $_ foreach @tcoords;
153 2         71 return ( $sum / $self->count_coords );
154             }
155              
156             sub msd_coords {
157              
158             # returns scalar
159 1     1 1 4 my $self = shift;
160 1         4 my $avg = $self->mean_coords;
161 1         51 my @tcoords = $self->all_coords;
162 1         3 my $sum = 0;
163 1         3 foreach my $c (@tcoords) {
164 10         24 my $dc = $c - $avg;
165 10         28 $sum += $dc * $dc;
166             }
167 1         36 return ( $sum / $self->count_coords );
168             }
169              
170             sub intra_dforces {
171              
172             #M::V::R makes much simpler
173 4     4 1 204 my $self = shift;
174 4 100       36 croak "intra_dforces> pass initial and final time" unless ( @_ == 2 );
175 1         2 my $ti = shift;
176 1         3 my $tf = shift;
177 1         36 return ( $self->get_forces($tf) - $self->get_forces($ti) );
178             }
179              
180             sub mean_forces {
181 2     2 1 5 my $self = shift;
182 2         84 my @tforces = $self->all_forces;
183 2         10 my $sum = V( 0, 0, 0 );
184 2         18 $sum += $_ foreach @tforces;
185 2         71 return ( $sum / $self->count_forces );
186             }
187              
188             sub msd_forces {
189              
190             # returns scalar
191 1     1 1 4 my $self = shift;
192 1         4 my $avg = $self->mean_forces;
193 1         39 my @tforces = $self->all_forces;
194 1         3 my $sum = 0;
195 1         3 foreach my $c (@tforces) {
196 10         25 my $dc = $c - $avg;
197 10         26 $sum += $dc * $dc;
198             }
199 1         36 return ( $sum / $self->count_forces );
200             }
201              
202             #interobject methods
203             sub distance {
204 1664     1664 1 3246 my $self = shift;
205 1664 100       3068 my $obj2 = shift or croak "need to pass another obj that does PhysVec";
206 1663         40778 my ( $ts, $t2 ) = ( $self->t, $obj2->t );
207 1663 100       3427 carp "comparing objects with different times" unless ( $ts == $t2 );
208 1663         53470 my $vs = $self->get_coords($ts);
209 1663         53562 my $v2 = $obj2->get_coords($t2);
210 1663         6132 return ( $vs->dist($v2) );
211             }
212              
213             sub angle_rad {
214              
215             # obj2 obj3
216             # \ Ang /
217             # \ /
218             # self
219             #
220             # returns in degrees
221 220     220 1 399 my ( $self, $obj2, $obj3 ) = @_;
222 220 100       525 croak "need to pass two objects that do PhysVecMVR" unless ( @_ == 3 );
223 218         468 my $v1 = $self->inter_dcoords($obj2);
224 218         437 my $v2 = $self->inter_dcoords($obj3);
225 218 100 100     1236 return (0) if ( abs($v1) == 0 or abs($v2) == 0 );
226 215         603 return ( atan2( $v1, $v2 ) );
227             }
228              
229             sub angle_deg {
230 210     210 1 3099 my $self = shift;
231 210         439 my $angle = $self->angle_rad(@_);
232 208         3906 return ( rad2deg($angle) );
233             }
234              
235             sub dihedral_rad {
236              
237             # self obj4
238             # \ /
239             # \ Ang /
240             # obj2---obj3
241             #
242             # returns in degrees
243 155     155 1 344 my ( $self, $obj2, $obj3, $obj4 ) = @_;
244 155 100       386 croak "need to pass three objects that do PhysVecMVR" unless ( @_ == 4 );
245              
246 151         374 my $v1 = $self->inter_dcoords($obj2);
247 151         314 my $v2 = $obj2->inter_dcoords($obj3);
248 151         342 my $v3 = $obj3->inter_dcoords($obj4);
249 151         548 my $v3_x_v2 = $v3 x $v2;
250 151         379 my $v2_x_v1 = $v2 x $v1;
251 151         350 my $sign = $v1 * $v3_x_v2;
252              
253 151         466 my $dihe = atan2( $v3_x_v2, $v2_x_v1 );
254 151 100       2952 $dihe *= -1 if ( $sign > 0 );
255 151         509 return $dihe;
256             }
257              
258             sub dihedral_deg {
259 153     153 1 5012 my $self = shift;
260 153         345 my $dihe = $self->dihedral_rad(@_);
261 149         473 return ( rad2deg($dihe) );
262             }
263              
264             sub inter_dcharges {
265 3     3 0 130 my $self = shift;
266 3 100       19 my $obj2 = shift or croak "need to pass another obj that does PhysVec";
267 2         64 my ( $ts, $t2 ) = ( $self->t, $obj2->t );
268 2 100       16 carp "comparing objects with different times" unless ( $ts == $t2 );
269 2         504 my $dvec = $obj2->get_charges($t2) - $self->get_charges($ts);
270 2         12 return ($dvec);
271             }
272              
273             sub inter_dcoords {
274 1009     1009 0 1577 my $self = shift;
275 1009 100       1914 my $obj2 = shift or croak "need to pass another obj that does PhysVec";
276 1008         25825 my ( $ts, $t2 ) = ( $self->t, $obj2->t );
277 1008 100       2126 carp "comparing objects with different times" unless ( $ts == $t2 );
278 1008         32495 my $dvec = $obj2->get_coords($t2) - $self->get_coords($ts);
279 1008         2489 return ($dvec);
280             }
281              
282             sub inter_dforces {
283 3     3 0 112 my $self = shift;
284 3 100       18 my $obj2 = shift or croak "need to pass another obj that does PhysVec";
285 2         66 my ( $ts, $t2 ) = ( $self->t, $obj2->t );
286 2 100       16 carp "comparing objects with different times" unless ( $ts == $t2 );
287 2         505 my $dvec = $obj2->get_forces($t2) - $self->get_forces($ts);
288 2         20 return ($dvec);
289             }
290              
291             sub xyz {
292 361     361 1 593 my $self = shift;
293 361 100       683 carp "xyz> takes no arguments. returns get_coords(t)" if (@_);
294 361         9691 return ( $self->get_coords( $self->t ) );
295             }
296              
297             sub clone_xyz {
298              
299             # returns a new MVR
300             # optionally takes t
301 4     4 1 20 my $self = shift;
302 4         6 my $t = shift;
303 4 100       58 $t = $self->t unless ( defined($t) );
304 4         5 return ( V( @{ $self->get_coords($t) } ) );
  4         133  
305             }
306              
307             sub force {
308 11     11 1 69 my $self = shift;
309 11 100       40 carp "force> takes no arguments. returns get_forces(t)" if (@_);
310 11         824 return ( $self->get_forces( $self->t ) );
311             }
312              
313             sub clone_force {
314              
315             # returns a new MVR
316             # optionally takes t
317 4     4 1 19 my $self = shift;
318 4         6 my $t = shift;
319 4 100       57 $t = $self->t unless ( defined($t) );
320 4         16 return ( V( @{ $self->get_forces($t) } ) );
  4         133  
321             }
322              
323             sub copy_ref_from_t1_through_t2 {
324 7 100   7 1 234 croak "need to pass [charges|coords|forces] t and tf" unless @_ == 4;
325 4         11 my ( $self, $qcf, $t, $tf ) = @_;
326 4         9 my ( $get_qcf, $set_qcf ) = map { $_ . $qcf } qw(get_ set_);
  8         26  
327 4         149 my $qcf_at_t = $self->$get_qcf($t);
328 4         151 $self->$set_qcf( $_, $qcf_at_t ) foreach ( $t + 1 .. $tf );
329             }
330              
331 20     20   182 no Moose::Role;
  20         64  
  20         127  
332              
333             1;
334              
335             __END__
336              
337             =pod
338              
339             =head1 NAME
340              
341             HackaMol::Roles::PhysVecMVRRole - Provides the core of HackaMol Atom and Molecule classes.
342              
343             =head1 VERSION
344              
345             version 0.051
346              
347             =head1 SYNOPSIS
348              
349             # instance of class that consumes the PhysVecRol
350             use HackaMol::Molecule;
351             my $obj = HackaMol::Molecule->new(
352             name => 'foo',
353             t => 0 ,
354             charges => [0.1],
355             coords => [ V(0,1,2) ]
356             );
357              
358             # add some charges
359              
360             $obj->push_charges($_) foreach ( 0.3, 0.2, 0.1, -0.1, -0.2, -0.36 );
361              
362             my $sum_charges = 0;
363              
364             $sum_charges += $_ foreach $obj->all_charges;
365             print "average charge: ", $sum_charges / $obj->count_charges;
366              
367             # add some coordinates
368              
369             $obj->push_coords($_) foreach ( V(0,0,0), V(1,1,1), V(-1.0,2.0,-4.0) ) );
370              
371             print $obj->mean_charges . "\n";
372             print $obj->msd_charges . "\n";
373             printf ("%10.3f %10.3f %10.3f \n", @{$obj->mean_coords};
374             print $obj->msd_coords . "\n";
375              
376             =head1 DESCRIPTION
377              
378             PhysVecMVR provides the core attributes and methods shared between Atom
379             and Molecule classes. Consuming this role gives Classes a place to store
380             coordinates, forces, and charges, perhaps, over the course of a simulation
381             or for a collection of configurations for which all other object metadata (name,
382             mass, etc) remains fixed. As such, the 't' attribute, described below, is
383             important to understand. The PhysVecMVR uses Math::Vector::Real (referred to as MVR),
384             which has pure Perl and XS implementations. MVR::XS is fast with many useful/powerful
385             overloaded methods. PhysVecMVR leaves many attributes rw so that they may be set and
386             reset on the fly. This seems most intuitive from the
387             perspective of carrying out computational work on molecules.
388              
389             Comparing the PhysVec within Atom and Molecule may be helpful. For both, the PhysVecRol
390             generates a little metadata (mass, name, etc.) and an array of coordinates, forces, and
391             charges. For an atom, the array of coordinates gives an atom (with fixed metadata) the ability
392             to store multiple [x,y,z] positions (as a function of time, symmetry, distribution, etc.). What
393             is the array of coordinates for Molecule? Usually, the coordinates for a molecule will likely
394             remain empty (because the atoms that Molecule contains have the more useful coordinates), but we
395             can imagine using the coordinates array to track the center of mass of the molecule if needed.
396              
397             In the following: Methods with mean_foo msd_foo intra_dfoo out front, carries out some analysis
398             within $self. Methods with inter_ out front carries out some analysis between
399             two objects of classes that consume PhysVecMVR at the $self->t and $obj->t.
400              
401             =head1 METHODS
402              
403             =head2 distance
404              
405             Takes one argument ($obj2) and calculates the distance using Math::Vector::Real
406              
407             $obj1->distance($obj2);
408              
409             =head2 angle_deg
410              
411             Takes two arguments ($obj2,$obj3) and calculates the angle (degrees) between
412             the vectors with $obj1 as orgin using Math::Vector::Real.
413              
414             $obj1->angle_deg($obj2,$obj3);
415              
416             =head2 angle_rad
417              
418             Takes two arguments ($obj2,$obj3) and calculates the angle (radians) between
419             the vectors with $obj1 as orgin using Math::Vector::Real.
420              
421             $obj1->angle_rad($obj2,$obj3);
422              
423             =head2 dihedral_deg
424              
425             Takes three arguments ($obj2,$obj3,obj4) and calculates the angle
426             (degrees) between the vectors normal to the planes containing the first three
427             and last three objects using Math::Vector::Real.
428              
429             $obj1->dihedral_deg($obj2,$obj3,$obj4);
430              
431             =head2 dihedral_rad
432              
433             Takes three arguments ($obj2,$obj3,obj4) and calculates the angle
434             (radians) between the vectors normal to the planes containing the first three
435             and last three objects using Math::Vector::Real.
436              
437             $obj1->dihedral_rad($obj2,$obj3,$obj4);
438              
439             =head2 intra_dcharges
440              
441             Calculates the change in charge from initial t ($ti) to final t ($tf). I.e.:
442              
443             $obj1->intra_dcharges( $ti, $tf );
444              
445             yields the same as:
446              
447             $self->get_charges($tf) - $self->get_charges($ti);
448              
449             =head2 mean_charges
450              
451             No arguments. Calculates the mean of all stored charges.
452              
453             =head2 msd_charges
454              
455             No arguments. Calculates the mean square deviation of all stored charges.
456              
457             =head2 intra_dcoords intra_dforces
458              
459             returns the difference (Math::Vector::Real object) from the initial t ($ti) to
460             the final t ($tf).
461              
462             $obj1->intra_dcoords($ti,$tf);
463              
464             $obj1->intra_dforces($ti,$tf);
465              
466             =head2 mean_coords mean_forces
467              
468             No arguments. Calculates the mean (Math::Vector::Real object) vector.
469              
470             =head2 msd_coords msd_forces
471              
472             No arguments. Calculates the mean (Math::Vector::Real object) dot product of the
473             vector difference of the xyz coords from the mean vector.
474              
475             =head2 charge
476              
477             called with no arguments. returns $self->get_charges($self->t);
478              
479             =head2 xyz
480              
481             called with no arguments. returns $self->get_coords($self->t);
482              
483             =head2 clone_xyz
484              
485             optionally called with t. t set to $self->t if not passed.
486             This method is intended for mapping from one atom to another, where the
487             coordinates are expected to be distinct. A copy of the xyz
488             coordinates with a new memory location is returned via
489             V( @{$self->get_coords($t)});
490              
491             my @Aus = map {
492             HackaMol::Atom->new(Z=>79, coords=>[$_->clone_xyz])
493             } grep {$_->Z == 80} @atoms;
494              
495             =head2 force
496              
497             called with no arguments. returns $self->get_forces($self->t);
498              
499             =head2 clone_force
500              
501             optionally called with t. t set to $self->t if not passed. Analagous to
502             clone_xyz.
503              
504             =head2 copy_ref_from_t1_through_t2
505              
506             called as $obj->copy_ref_from_t1_through_t2($qcf,$t,$tf); where qcf is
507             charges|coords|forces. Fills the qcf from t+1 to tf with the value at t.
508             Added this while trying to compare a dipole as a function of changing charges
509             at a single set of coordinates.
510              
511             =head1 ARRAY METHODS
512              
513             =head2 push_$_, all_$_, get_$_, set_$_, count_$_, clear_$_ foreach qw(charges coords forces)
514              
515             ARRAY traits, respectively: push, get, set, all, elements, delete, clear
516             Descriptions for charges and coords follows. forces analogous to coords.
517              
518             =head2 push_charges
519              
520             push value on to charges array
521              
522             $obj->push_charges($_) foreach (0.15, 0.17, 0.14, 0.13);
523              
524             =head2 all_charges
525              
526             returns array of all elements in charges array
527              
528             print $_ . " " foreach $obj->all_charges; # prints 0.15 0.17 0.14 0.13
529              
530             =head2 get_charges
531              
532             return element by index from charges array
533              
534             print $obj->get_charges(1); # prints 0.17
535              
536             =head2 set_charges
537              
538             set value of element by index from charges array
539              
540             $obj->set_charges(2, 1.00);
541             print $_ . " " foreach $obj->all_charges; # prints 0.15 0.17 1.00 0.13
542              
543             =head2 count_charges
544              
545             return number of elements in charges array
546              
547             print $obj->count_charges; # prints 4
548              
549             =head2 delete_charges($index)
550              
551             Removes the element at the given index from the array.
552              
553             This method returns the deleted value. Note that if no value exists, it will return undef.
554              
555             This method requires one argument.
556              
557             =head2 clear_charges
558              
559             clears charges array
560              
561             $obj->clear_charges;
562             print $_ . " " foreach $obj->all_charges; # does nothing
563             print $obj->count_charges; # prints 0
564              
565             =head2 push_coords
566              
567             push value on to coords array
568              
569             $obj->push_coords($_) foreach ([0,0,0],[1,1,1],[-1.0,2.0,-4.0], [3,3,3]);
570              
571             =head2 all_coords
572              
573             returns array of all elements in coords array
574              
575             printf ("%10.3f %10.3f %10.3f \n", @{$_}) foreach $obj->all_coords;
576              
577             my @new_coords = map {[$_->[0]+1,$_->[1]+1,$_->[2]+1]} $obj->all_coords;
578              
579             printf ("%10.3f %10.3f %10.3f \n", @{$_}) foreach @new_coords;
580              
581             =head2 get_coords
582              
583             return element by index from coords array
584              
585             printf ("%10.3f %10.3f %10.3f \n", @{$obj->get_coords(1)}); #
586              
587             =head2 set_coords
588              
589             set value of element by index from coords array
590              
591             $obj->set_coords(2, [100,100,100]);
592             printf ("%10.3f %10.3f %10.3f \n", @{$_}) foreach $obj->all_coords;
593              
594             =head2 count_coords
595              
596             return number of elements in coords array
597              
598             print $obj->count_coords; # prints 4
599              
600             =head2 delete_coords($index)
601              
602             Removes the element at the given index from the array.
603              
604             This method returns the deleted value. Note that if no value exists, it will return undef.
605              
606             This method requires one argument.
607              
608             clears coords array
609              
610             $obj->clear_coords;
611             print $_ . " " foreach $obj->all_coords; # does nothing
612             print $obj->count_coords # prints 0
613              
614             =head2 clear_coords
615              
616             clears coords array
617              
618             $obj->clear_coords;
619             print $_ . " " foreach $obj->all_coords; # does nothing
620             print $obj->count_coords # prints 0
621              
622             =head1 ATTRIBUTES
623              
624             =head2 t
625              
626             isa Int or ScalarRef that is rw with default of 0
627              
628             t is intended to describe the current "setting" of the object. Objects that
629             consume the PhysVecRol have arrays of coordinates, forces, and charges to
630             allow storage with the passing of time (hence t) or the generation alternative
631             configurations. For example, a crystal lattice can be stored as a single
632             object that consumes PhysVec (with associated metadata) along with the
633             array of 3d-coordinates resulting from lattice vector translations.
634              
635             Experimental: Setting t to a ScalarRef allows all objects to share the same t.
636             Although, to use this, a $self->t accessor that dereferences the value would
637             seem to be required. There is nothing, currently, in the core to do so. Not
638             sure yet if it is a good or bad idea to do so.
639              
640             my $t = 0;
641             my $rt = \$t;
642             $_->t($rt) for (@objects);
643             $t = 1; # change t for all objects.
644              
645             =head2 mass
646              
647             isa Num that is rw and lazy with a default of 0
648              
649             =head2 xyzfree
650              
651             isa ArrayRef that is rw and lazy with a default value [1,1,1]. Using this array
652             allows the object to be fixed for calculations that support it. For example, to
653             fix the X and Y coordinates:
654              
655             $obj->xyzfree([0,0,1]);
656              
657             fixing any coordinate will set trigger the is_fixed(1) flag.
658              
659             =head2 is_fixed
660              
661             isa Bool that is rw and lazy with a default of 0 (false).
662              
663             =head2 charges
664              
665             isa ArrayRef[Num] that is lazy with public ARRAY traits described in ARRAY_METHODS
666              
667             Gives atoms and molecules t-dependent arrays of charges. e.g. store and analyze
668             atomic charges from a quantum mechanical molecule in several intramolecular
669             configurations or a fixed configuration in varied external potentials.
670              
671             =head2 coords forces
672              
673             isa ArrayRef[Math::Vector::Real] that is lazy with public ARRAY traits described in ARRAY_METHODS
674              
675             Gives atoms and molecules t-dependent arrays of coordinates and forces,
676             for the purpose of analysis.
677              
678             =head1 SEE ALSO
679              
680             =over 4
681              
682             =item *
683              
684             L<HackaMol::Atom>
685              
686             =item *
687              
688             L<HackaMol::Molecule>
689              
690             =back
691              
692             =head1 AUTHOR
693              
694             Demian Riccardi <demianriccardi@gmail.com>
695              
696             =head1 COPYRIGHT AND LICENSE
697              
698             This software is copyright (c) 2017 by Demian Riccardi.
699              
700             This is free software; you can redistribute it and/or modify it under
701             the same terms as the Perl 5 programming language system itself.
702              
703             =cut