File Coverage

blib/lib/Chemistry/File/InternalCoords.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Chemistry::File::InternalCoords;
2              
3 1     1   58015 use warnings;
  1         2  
  1         32  
4 1     1   5 use strict;
  1         1  
  1         58  
5              
6             our $VERSION = '0.03';
7              
8 1     1   5 use base qw(Chemistry::File);
  1         7  
  1         5518  
9              
10 1     1   59285 use Chemistry::Mol;
  1         83519  
  1         81  
11 1     1   533 use Chemistry::InternalCoords::Builder 'build_zmat';
  0            
  0            
12              
13             my $EXT = 'zmat';
14             Chemistry::Mol->register_format( zmat => __PACKAGE__ );
15              
16             sub name_is {
17             my ($class, $fname) = @_;
18             $fname =~ /\.$EXT$/i;
19             }
20              
21             sub file_is {
22             my ($class, $fname) = @_;
23             $fname =~ /\.$EXT$/i;
24             }
25              
26             sub parse_string {
27             my ($class, $s, %opts) = @_;
28              
29             my $mol_class = $opts{mol_class} || 'Chemistry::Mol';
30             my ($s_atoms, $s_vars) = split /^\s*$/m, $s;
31              
32             my $mol = $mol_class->new();
33              
34             my @lines = split /(?:\n|\r\n?)/, $s_vars;
35             my %vars;
36             foreach (@lines){
37             next unless m/
38             (\w+) # $1: variable name
39             (?:
40             \s+ # whitespace
41             | # OR
42             \s*=\s* # equals with optional whitespace
43             )
44             ( # $2
45             [-+]? # optional sign
46             \d+ # whole number part
47             \.? # optional decimal place
48             \d* # optional decimal part
49             )
50             /x;
51             $vars{$1} = $2;
52             }
53              
54             @lines = split /(?:\n|\r\n?)/, $s_atoms;
55             foreach my $i (0..$#lines){
56             my ($elem, @internal_coords) = split ' ', $lines[$i];
57             $_ = $vars{$_} for @internal_coords[ grep { $_ <= $#internal_coords } 1,3,5 ];
58             my $atom = $mol->new_atom(
59             parent => $mol,
60             id => $i+1,
61             ($elem =~ /^\d+$/ ? "Z" : "symbol") => $elem,
62             internal_coords => \@internal_coords,
63             );
64             $atom->internal_coords->add_cartesians;
65             $mol->new_bond(
66             id => "b$i",
67             atoms => [ $atom, $mol->atoms($internal_coords[0]) ],
68             length => $internal_coords[1],
69             ) if $internal_coords[0];
70             }
71              
72             return $mol;
73             }
74              
75             sub write_string {
76             my ($class, $mol, %opts) = @_;
77              
78             %opts = (symbol => 1, vars => 1, vars_sep => 0, bfs => 0, sort => 1, skip_build=>0, %opts);
79              
80             build_zmat($mol, %opts) unless $opts{skip_build};
81              
82             my $s = ''; # final output string
83              
84             # these store the variables (if vars is on). e.g. B1 is $bonds[0], A3 is $angles[2], D2 is $dihedrals[1]
85             my ( @bonds, @angles, @dihedrals );
86              
87             my %index; # used to map the atom's id to atom number
88              
89             foreach my $i ( 1 .. scalar $mol->atoms ){
90             my $atom = $mol->atoms($i);
91             $index{ $atom->id } = $i;
92             my $ic = $atom->internal_coords; # Chemistry::InternalCoords object
93              
94             # gets an array of 0, 2, 4 or 6 elements (usually 6)
95             my @ic = (
96             $ic->distance, # (atom,distance)
97             $ic->angle, # (atom,angle)
98             $ic->dihedral, # (atom,dihedral)
99             );
100             pop @ic while @ic && !defined $ic[-1]; # remove trailing undef's
101              
102             if( $opts{vars} ){
103             SWITCH: { # need this since not all atoms have bond/angle/dihedral info
104             last SWITCH unless @ic > 0;
105             push @bonds, $ic[1]; # store value
106             $ic[1] = 'B'.scalar(@bonds); # rewrite as var name
107              
108             last SWITCH unless @ic > 2;
109             push @angles, $ic[3]; # store value
110             $ic[3] = 'A'.scalar(@angles); # rewrite as var name
111              
112             last SWITCH unless @ic > 4;
113             push @dihedrals, $ic[5]; # store value
114             $ic[5] = 'D'.scalar(@dihedrals); # rewrite as var name
115             }
116             }else{
117             # number-format each of the values
118             $_ = sprintf "%.8f", $_ for @ic[ grep {$_<@ic} 1,3,5 ];
119             }
120              
121             # change all atom names/ids into atom number
122             $_ = $index{ $_->id } for @ic[ grep {$_<@ic} 0,2,4 ];
123              
124             # build the atom's output line
125             $s .= sprintf "%-2s" . " %5d %15s" x int(@ic/2) . "\n",
126             $opts{symbol} ? $atom->symbol : $atom->Z,
127             @ic,
128             ;
129             }
130             if( $opts{vars} ){
131             # provide the variable definitions
132             $s .= "\n";
133             my $fmt = " %s%-4d" . ($opts{vars_sep}?'=':' ') . "%25.8f\n";
134             $s .= join "", map { sprintf $fmt, 'B', $_+1, $bonds[$_] } 0..$#bonds;
135             $s .= join "", map { sprintf $fmt, 'A', $_+1, $angles[$_] } 0..$#angles;
136             $s .= join "", map { sprintf $fmt, 'D', $_+1, $dihedrals[$_] } 0..$#dihedrals;
137             }
138              
139             return $s;
140             }
141              
142             1;
143              
144             =pod
145              
146             =head1 NAME
147              
148             Chemistry::File::InternalCoords - Internal coordinates (z-matrix) molecule format reader/writer
149              
150             =head1 VERSION
151              
152             Version 0.03
153              
154             =head1 SYNOPSIS
155              
156             This module is not intended for direct use -- it is intended for use via L.
157              
158             use Chemistry::File qw/ InternalCoords XYZ /;
159             my $mol = Chemistry::Mol->read("foo.zmat");
160             warn $mol->print;
161             $mol->write(\*STDOUT, format => 'zmat');
162             $mol->write('foo.xyz', format => 'xyz');
163              
164             =head1 DESCRIPTION
165              
166             This is a subclass of L for reading and writing the zmatriz (aka Z-matrix aka InternalCoords) molecule data format. It registers the 'zmat' file extension with L.
167              
168             For example, here is hydrogen:
169              
170             H
171             H 1 B1
172            
173             B1 0.70000000
174              
175             and water:
176              
177             O
178             H 1 B1
179             H 1 B2 2 A1
180            
181             B1 0.96659987
182             B2 0.96659987
183             A1 107.67114171
184              
185              
186             =head1 METHODS
187              
188             This class inherits from L. The following methods are overloaded:
189              
190             =over 2
191              
192             =item name_is
193              
194             Checks if the filename extension is 'zmat'.
195              
196             =item file_is
197              
198             Checks if the filename extension is 'zmat'.
199              
200             =item parse_string
201              
202             Expects a plain zmatrix format. Variables are support. No special options.
203              
204             =item write_string
205              
206             Creates a plain zmatrix formatted string. Any options are also passed to L's I function (defaults to bfs off and sort on). Also recognizes these options that affect the output:
207              
208             =over 2
209              
210             =item symbol
211              
212             If on (default) uses the element instead of the atomic number
213              
214             =item vars
215              
216             if on (default) uses variables for the bond lengths & angles) options, which affect the output.
217              
218             =item vars_sep
219              
220             if on (defaults to off) with put an '=' between variable names and values. only used when I option is enabled.
221              
222             =item skip_build
223              
224             if on (defaults to off) it will assume that the internal_coords for all the atoms are already set, and will NOT call I to generate everything.
225              
226             =back
227              
228             =back
229              
230             =head1 PREREQUISITES
231              
232             =over 4
233              
234             =item *
235              
236             L
237              
238             =item *
239              
240             L
241              
242             =item *
243              
244             L
245              
246             =item *
247              
248             L
249              
250             =back
251              
252             =head1 SEE ALSO
253              
254             L
255              
256             =head1 AUTHOR
257              
258             David Westbrook (davidrw), C<< >>
259              
260             =head1 BUGS
261              
262             Please report any bugs or feature requests to
263             C, or through the web interface at
264             L.
265             I will be notified, and then you'll automatically be notified of progress on
266             your bug as I make changes.
267              
268             I'm also available by email or via '/msg davidrw' on L.
269              
270             =head1 SUPPORT
271              
272             You can find documentation for this module with the perldoc command.
273              
274             perldoc Chemistry::File::InternalCoords
275              
276             You can also look for information at:
277              
278             =over 4
279              
280             =item * AnnoCPAN: Annotated CPAN documentation
281              
282             L
283              
284             =item * CPAN Ratings
285              
286             L
287              
288             =item * RT: CPAN's request tracker
289              
290             L
291              
292             =item * Search CPAN
293              
294             L
295              
296             =back
297              
298             =head1 ACKNOWLEDGEMENTS
299              
300             =head1 COPYRIGHT & LICENSE
301              
302             Copyright 2006 David Westbrook, all rights reserved.
303              
304             This program is free software; you can redistribute it and/or modify it
305             under the same terms as Perl itself.
306              
307             =cut
308