File Coverage

blib/lib/HackaMol/Atom.pm
Criterion Covered Total %
statement 66 66 100.0
branch 12 12 100.0
condition 3 3 100.0
subroutine 22 22 100.0
pod 2 4 50.0
total 105 107 98.1


line stmt bran cond sub pod time code
1             package HackaMol::Atom;
2             $HackaMol::Atom::VERSION = '0.051';
3             #ABSTRACT: HackaMol Atom Class
4 19     19   1573009 use 5.008;
  19         146  
5 19     19   3790 use Moose;
  19         2401725  
  19         156  
6 19     19   122464 use namespace::autoclean;
  19         58929  
  19         169  
7 19     19   1712 use Carp;
  19         47  
  19         1355  
8 19     19   3410 use MooseX::StrictConstructor;
  19         159380  
  19         140  
9             #use MooseX::Storage;
10             #with Storage('format' => 'JSON', 'io' => 'File');
11              
12             with 'HackaMol::Roles::NameRole',
13             'HackaMol::Roles::PhysVecMVRRole',
14             'HackaMol::Roles::PdbRole',
15             'HackaMol::Roles::QmAtomRole';
16             use HackaMol::PeriodicTable
17 19     19   124814 qw(@ELEMENTS %ELEMENTS %ATOMIC_MASSES @COVALENT_RADII @VDW_RADII %ATOM_MULTIPLICITY);
  19         87  
  19         20065  
18              
19             my @delta_attrs = qw(Z symbol mass vdw_radius covalent_radius);
20              
21             has 'is_dirty' => (
22              
23             # when attributes change, the Atom gets dirty. change_symbol, change_Z
24             # generally, classes that have Atom should decide whether to clean Atom
25             is => 'rw',
26             isa => 'Bool',
27             lazy => 1,
28             default => 0, # anytime called, the atom becomes dirty forever!
29             );
30              
31             has 'bond_count' => (
32             traits => ['Counter'],
33             is => 'ro',
34             isa => 'Num',
35             default => 0,
36             handles => {
37             inc_bond_count => 'inc',
38             dec_bond_count => 'dec',
39             reset_bond_count => 'reset',
40             },
41             );
42              
43             has 'symbol' => (
44             is => 'rw',
45             isa => 'Str',
46             predicate => 'has_symbol',
47             clearer => 'clear_symbol',
48             lazy => 1,
49             builder => '_build_symbol',
50             );
51              
52             sub _build_symbol {
53 1173     1173   1822 my $self = shift;
54 1173         23019 return ( _Z_to_symbol( $self->Z ) );
55             }
56              
57             has 'Z' => (
58             is => 'rw',
59             isa => 'Int',
60             predicate => 'has_Z',
61             clearer => 'clear_Z',
62             lazy => 1,
63             builder => '_build_Z',
64             );
65              
66             sub _build_Z {
67 3853     3853   5331 my $self = shift;
68 3853         87585 return ( _symbol_to_Z( $self->symbol ) );
69             }
70              
71             has $_ => (
72             is => 'rw',
73             isa => 'Num',
74             predicate => "has_$_",
75             clearer => "clear_$_",
76             lazy => 1,
77             builder => "_build_$_",
78             ) foreach (qw(covalent_radius vdw_radius));
79              
80             sub _build_covalent_radius {
81 83     83   124 my $self = shift;
82 83         1880 return ( _Z_to_covalent_radius( $self->Z ) );
83             }
84              
85             sub _build_vdw_radius {
86 1     1   3 my $self = shift;
87 1         21 return ( _Z_to_vdw_radius( $self->Z ) );
88             }
89              
90             sub change_Z {
91 2     2 1 118 my $self = shift;
92 2 100       15 my $Z = shift or croak "pass argument Z to change_Z method";
93 1         4 $self->_clean_atom;
94 1         22 $self->Z($Z);
95             }
96              
97             sub change_symbol {
98 2     2 1 103 my $self = shift;
99 2 100       17 my $symbol = shift or croak "pass argument symbol to change_Z method";
100 1         4 $self->_clean_atom;
101 1         3 $self->symbol( _fix_symbol($symbol) );
102             }
103              
104             sub charge {
105 12     12 0 421 my $self = shift;
106 12 100       37 carp "charge> takes no arguments. returns get_charges(t)" if (@_);
107 12 100       887 if ($self->has_charges){
108 11         267 return ( $self->get_charges( $self->t ) );
109             }
110             else {
111 1         6 return 0;
112             }
113             }
114              
115             sub _clean_atom {
116 2     2   5 my $self = shift;
117 2         4 foreach my $clearthis ( map { "clear_$_" } @delta_attrs ) {
  10         21  
118 10         255 $self->$clearthis;
119             }
120 2         28 carp "cleaning atom attributes for in place change. setting atom->is_dirty";
121 2         1112 $self->is_dirty(1);
122             }
123              
124             sub BUILD {
125 6794     6794 0 10725 my $self = shift;
126              
127 6794 100 100     216269 unless ( $self->has_Z or $self->has_symbol ) {
128 1         10 croak "Either Z or Symbol must be set when calling Atom->new()";
129             }
130              
131 6793 100       167476 if ( $self->has_Z ) {
132              
133             #clear out the symbol if Z is passed. Z is faster and takes precedence
134 1219         29736 $self->clear_symbol;
135 1219         24676 return;
136             }
137              
138 5574         127885 $self->symbol( _fix_symbol( $self->symbol ) );
139 5574         131308 return;
140             }
141              
142             sub _build_mass {
143 1459     1459   1933 my $self = shift;
144 1459         30832 return ( _symbol_to_mass( $self->symbol ) );
145             }
146              
147             sub _symbol_to_Z {
148 3853     3853   5823 my $symbol = shift;
149 3853         6353 $symbol = ucfirst( lc($symbol) );
150 3853         84208 return $ELEMENTS{$symbol};
151             }
152              
153             sub _Z_to_symbol {
154 1173     1173   1806 my $Z = shift;
155 1173         24287 return $ELEMENTS[$Z];
156             }
157              
158             sub _symbol_to_mass {
159 1459     1459   2117 my $symbol = shift;
160 1459         33519 return $ATOMIC_MASSES{$symbol};
161             }
162              
163             sub _fix_symbol {
164 5575     5575   135813 return ucfirst( lc(shift) );
165             }
166              
167             sub _Z_to_covalent_radius {
168 83     83   132 my $Z = shift;
169              
170             # index 1 for single bond length..
171 83         2066 return $COVALENT_RADII[$Z][1] / 100;
172             }
173              
174             sub _Z_to_vdw_radius {
175 1     1   3 my $Z = shift;
176 1         22 return $VDW_RADII[$Z][1] / 100;
177             }
178              
179             __PACKAGE__->meta->make_immutable;
180              
181             1;
182              
183             __END__
184              
185             =pod
186              
187             =head1 NAME
188              
189             HackaMol::Atom - HackaMol Atom Class
190              
191             =head1 VERSION
192              
193             version 0.051
194              
195             =head1 SYNOPSIS
196              
197             use HackaMol::Atom;
198             use Math::Vector::Real;
199            
200            
201             my $atom1 = HackaMol::Atom->new(
202             name => 'Zinc',
203             coords => [ V( 2.05274, 0.01959, -0.07701 ) ],
204             Z => 30,
205             );
206             print $atom->symbol ; #prints "Zn"
207            
208             print "clean " unless $atom->is_dirty; #prints clean
209            
210             $atom->change_symbol("Hg");
211             print $atom->Z ; #prints 80
212            
213             print "dirty " if $atom->is_dirty; #prints dirty
214              
215             =head1 DESCRIPTION
216              
217             Central to HackaMol, the Atom class provides methods and attributes for a
218             given atom. The Atom class consumes L<HackaMol::PhysVecMVRRole>,
219             L<HackaMol::PdbRole>, and L<HackaMol::QmAtomRole>. See the documentation
220             of those roles for details. The Atom class adds attributes (such as I<symbol>,
221             I<Z>,
222             I<covalent_radius>) and methods (such as I<change_symbol>) specific to atoms.
223             Creating an instance of an Atom object requires either the atomic number (I<Z>)
224             or symbol (I<symbol>). The other attributes are lazily built when needed. The
225             Atom class is flexible. The atom type can be changed in place (e.g. convert
226             a zinc atom to a mercury atom, see SYNOPSIS), but changing the type of atom
227             will set the is_dirty flag so that other objects using the atom have the
228             ability to know whether atom-type dependent attributes need to be updated
229             (e.g. forcefield parameters, etc.). Atom data is generated from the PeriodicTable
230             module that borrows data from PerlMol. The PeriodicTable module is for data and
231             will be dumped into a YAML file in the future.
232              
233             =head1 METHODS
234              
235             =head2 change_Z
236              
237             no arguments. Changes the atom type using I<Z>. I<change_Z> calls
238             I<_clean_atom> which clears all attributes and sets calls I<is_dirty(1)>.
239              
240             =head2 change_symbol
241              
242             no arguments. Changes the atom type using symbol and is analogous to
243             I<change_Z>.
244              
245             =head1 ATTRIBUTES
246              
247             =head2 is_dirty
248              
249             isa Bool that is lazy and rw. Default is 0. $self->is_dirty(1) called
250             during the I<change_symbol> and I<change_Z methods>.
251              
252             =head2 symbol
253              
254             isa Str that is lazy and rw. I<_build_symbol> builds the default.
255              
256             Generating an atom instance with I<symbol>, will run C<ucfirst(lc ($symbol))>
257             to make sure the format is correct. Thus, creating an atom object is
258             slightly slower with symbol than with I<Z>. If I<Z> is used to generate the
259             instance of the Atom class (C<my $atom = Atom->new(Z=>1)>), the C<_build_symbol>
260             method generates the symbol from I<Z> only when the symbol attribute is read
261             (I<symbol> attribute is lazy).
262              
263             =head2 Z
264              
265             isa Int that is lazy and rw. I<_build_Z> builds the default
266              
267             I<Z> is the Atomic number.
268              
269             =head2 covalent_radius
270              
271             isa Num that is lazy and rw. I<_build_covalent_radius> builds the default.
272              
273             the covalent radii are taken from those tabulated in:
274              
275             P. Pyykkoe, M. Atsumi (2009).
276             "Molecular Single-Bond Covalent Radii for Elements 1 to 118". Chemistry: A European Journal 15: 186.
277              
278             Covalent radii for double and triple bonds, generated from the same authors, are
279             also tabulated but currently not used.
280              
281             =head2 vdw_radius
282              
283             isa Num that is lazy and rw. _build_vdw_radius builds the default.
284              
285             Atomic Van der Waals radii information will be revisited and revised. Included as
286             reminder for now. See the source of PeriodicTable.pm for more information.
287              
288             =bond_count
289              
290             isa Num that is lazy with a default of 0. The value adjusted with public Counter traits:
291              
292             inc_bond_count adds 1 by default
293             dec_bond_count subtracts 1 by default
294             reset_bond_count sets to zero
295              
296             =head1 SEE ALSO
297              
298             =over 4
299              
300             =item *
301              
302             L<HackaMol::PhysVecMVRRole>
303              
304             =item *
305              
306             L<HackaMol::PdbRole>
307              
308             =item *
309              
310             L<HackaMol::QmAtomRole>
311              
312             =item *
313              
314             L<Chemistry::Atom>
315              
316             =back
317              
318             =head1 EXTENDS
319              
320             =over 4
321              
322             =item * L<Moose::Object>
323              
324             =back
325              
326             =head1 CONSUMES
327              
328             =over 4
329              
330             =item * L<HackaMol::Roles::NameRole>
331              
332             =item * L<HackaMol::Roles::NameRole|HackaMol::Roles::PhysVecMVRRole|HackaMol::Roles::PdbRole|HackaMol::Roles::QmAtomRole>
333              
334             =item * L<HackaMol::Roles::PdbRole>
335              
336             =item * L<HackaMol::Roles::PhysVecMVRRole>
337              
338             =item * L<HackaMol::Roles::QmAtomRole>
339              
340             =back
341              
342             =head1 AUTHOR
343              
344             Demian Riccardi <demianriccardi@gmail.com>
345              
346             =head1 COPYRIGHT AND LICENSE
347              
348             This software is copyright (c) 2017 by Demian Riccardi.
349              
350             This is free software; you can redistribute it and/or modify it under
351             the same terms as the Perl 5 programming language system itself.
352              
353             =cut