File Coverage

blib/lib/Bio/Tools/Run/Phylo/Hyphy/Base.pm
Criterion Covered Total %
statement 33 214 15.4
branch 8 86 9.3
condition 2 36 5.5
subroutine 10 24 41.6
pod 17 17 100.0
total 70 377 18.5


line stmt bran cond sub pod time code
1             #
2             # BioPerl module for Bio::Tools::Run::Phylo::Hyphy::Base
3             #
4             # Please direct questions and support issues to
5             #
6             # Cared for by Albert Vilella
7             #
8             # Copyright Albert Vilella
9             #
10             # You may distribute this module under the same terms as perl itself
11              
12             # POD documentation - main docs before the code
13              
14             =head1 NAME
15              
16             Bio::Tools::Run::Phylo::Hyphy::Base - Hyphy wrapping base methods
17              
18             =head1 SYNOPSIS
19              
20             FIXME
21              
22             =head1 DESCRIPTION
23              
24             HyPhy ([Hy]pothesis Testing Using [Phy]logenies) package of Sergei
25             Kosakowsky Pond, Spencer V. Muse, Simon D.W. Frost and Art Poon. See
26             http://www.hyphy.org for more information.
27              
28             =head1 FEEDBACK
29              
30             =head2 Mailing Lists
31              
32             User feedback is an integral part of the evolution of this and other
33             Bioperl modules. Send your comments and suggestions preferably to
34             the Bioperl mailing list. Your participation is much appreciated.
35              
36             bioperl-l@bioperl.org - General discussion
37             http://bioperl.org/wiki/Mailing_lists - About the mailing lists
38              
39             =head2 Support
40              
41             Please direct usage questions or support issues to the mailing list:
42              
43             I
44              
45             rather than to the module maintainer directly. Many experienced and
46             reponsive experts will be able look at the problem and quickly
47             address it. Please include a thorough description of the problem
48             with code and data examples if at all possible.
49              
50             =head2 Reporting Bugs
51              
52             Report bugs to the Bioperl bug tracking system to help us keep track
53             of the bugs and their resolution. Bug reports can be submitted via the
54             web:
55              
56             http://redmine.open-bio.org/projects/bioperl/
57              
58             =head1 AUTHOR - Albert Vilella
59              
60             Email avilella-at-gmail-dot-com
61              
62             =head1 CONTRIBUTORS
63              
64             Additional contributors names and emails here
65              
66             =head1 APPENDIX
67              
68             The rest of the documentation details each of the object methods.
69             Internal methods are usually preceded with a _
70              
71             =cut
72              
73              
74             # Let the code begin...
75              
76              
77             package Bio::Tools::Run::Phylo::Hyphy::Base;
78 1     1   9 use strict;
  1         3  
  1         35  
79 1     1   8 use Bio::Root::Root;
  1         1  
  1         19  
80 1     1   4 use Bio::AlignIO;
  1         2  
  1         27  
81 1     1   9 use Bio::TreeIO;
  1         2  
  1         27  
82 1     1   296 use Bio::Tools::Run::WrapperBase;
  1         3  
  1         26  
83 1     1   5 use base qw(Bio::Root::Root Bio::Tools::Run::WrapperBase);
  1         2  
  1         130  
84              
85             =head2 Default Values
86              
87             Valid and default values are listed below. The default
88             values are always the first one listed. These descriptions are
89             essentially lifted from the python wrapper or provided by the author.
90              
91             =cut
92              
93             our $PROGRAMNAME = 'HYPHYMP';
94             our $PROGRAM;
95              
96              
97             BEGIN {
98 1 50   1   1787 if( defined $ENV{'HYPHYDIR'} ) {
99 0 0       0 $PROGRAM = Bio::Root::IO->catfile($ENV{'HYPHYDIR'},$PROGRAMNAME). ($^O =~ /mswin/i ?'.exe':'');;
100             }
101             }
102              
103             =head2 program_name
104              
105             Title : program_name
106             Usage : $factory->program_name()
107             Function: holds the program name
108             Returns: string
109             Args : None
110              
111             =cut
112              
113             sub program_name {
114 6     6 1 25 return $PROGRAMNAME;
115             }
116              
117             =head2 valid_values
118              
119             Title : valid_values
120             Usage : $factory->valid_values()
121             Function: returns the possible parameters
122             Returns: an array holding all possible parameters (this needs to be specified per child class).
123             Returns an empty array in the base class.
124             Args : None
125              
126             =cut
127              
128             sub valid_values {
129 0     0 1 0 return ();
130             }
131              
132             =head2 program_dir
133              
134             Title : program_dir
135             Usage : ->program_dir()
136             Function: returns the program directory, obtained from ENV variable.
137             Returns: string
138             Args :
139              
140             =cut
141              
142             sub program_dir {
143 3 50   3 1 12 return Bio::Root::IO->catfile($ENV{HYPHYDIR}) if $ENV{HYPHYDIR};
144             }
145              
146              
147             =head2 new
148              
149             Title : new
150             Usage : my $obj = Bio::Tools::Run::Phylo::Hyphy->new();
151             Function: Builds a new Bio::Tools::Run::Phylo::Hyphy object
152             Returns : Bio::Tools::Run::Phylo::Hyphy
153             Args : -alignment => the Bio::Align::AlignI object
154             -save_tempfiles => boolean to save the generated tempfiles and
155             NOT cleanup after onesself (default FALSE)
156             -tree => the Bio::Tree::TreeI object
157             -params => a hashref of parameters (all passed to set_parameter)
158             -executable => where the hyphy executable resides
159              
160             See also: L, L
161              
162             =cut
163              
164             sub new {
165 0     0 1 0 my($class,@args) = @_;
166 0         0 my $self = $class->SUPER::new(@args);
167 0         0 my $versionstring = $self->version();
168              
169 0         0 return $self;
170             }
171              
172              
173             =head2 prepare
174              
175             Title : prepare
176             Usage : my $rundir = $hyphy->prepare($aln);
177             Function: prepare the analysis using the default or updated parameters
178             the alignment parameter must have been set
179             Returns : value of rundir
180             Args : L object,
181             L object [optional]
182              
183             =cut
184              
185             sub prepare {
186 0     0 1 0 my ($self,$aln,$tree) = @_;
187 0 0       0 $tree = $self->tree unless $tree;
188 0 0       0 $aln = $self->alignment unless $aln;
189 0 0       0 if( ! $aln ) {
190 0         0 $self->warn("must have supplied a valid alignment file in order to run hyphy");
191 0         0 return 0;
192             }
193 0         0 my ($tempdir) = $self->tempdir();
194 0         0 my ($tempseqFH,$tempalnfile);
195 0 0 0     0 if( ! ref($aln) && -e $aln ) {
196 0         0 $tempalnfile = $aln;
197             } else {
198 0 0       0 ($tempseqFH,$tempalnfile) = $self->io->tempfile('-dir' => $tempdir, UNLINK => ($self->save_tempfiles ? 0 : 1));
199 0         0 $aln->set_displayname_flat(1);
200 0         0 my $alnout = Bio::AlignIO->new('-format' => 'fasta', '-fh' => $tempseqFH);
201 0         0 $alnout->write_aln($aln);
202 0         0 $alnout->close();
203 0         0 undef $alnout;
204 0         0 close($tempseqFH);
205             }
206 0         0 $self->{'_params'}{'tempalnfile'} = $tempalnfile;
207             # setting a new temp file to hold the run output for debugging
208 0         0 $self->{'run_output'} = "$tempdir/run_output";
209 0         0 my $outfile = $self->outfile_name;
210 0 0       0 if ($outfile eq "") {
211 0         0 $outfile = "$tempdir/results.out";
212 0         0 $self->outfile_name($outfile);
213             }
214 0         0 my ($temptreeFH,$temptreefile);
215 0 0 0     0 if( ! ref($tree) && -e $tree ) {
216 0         0 $temptreefile = $tree;
217             } else {
218 0 0       0 ($temptreeFH,$temptreefile) = $self->io->tempfile('-dir' => $tempdir, UNLINK => ($self->save_tempfiles ? 0 : 1));
219 0         0 my $treeout = Bio::TreeIO->new('-format' => 'newick', '-fh' => $temptreeFH);
220 0         0 $treeout->write_tree($tree);
221 0         0 $treeout->close();
222 0         0 close($temptreeFH);
223             }
224 0         0 $self->{'_params'}{'temptreefile'} = $temptreefile;
225 0         0 $self->create_wrapper;
226 0         0 $self->{_prepared} = 1;
227 0         0 return $tempdir;
228             }
229              
230              
231             =head2 create_wrapper
232              
233             Title : create_wrapper
234             Usage : $self->create_wrapper
235             Function: It will create the wrapper file that interfaces with the analysis bf file
236             Example :
237             Returns :
238             Args :
239              
240              
241             =cut
242              
243             sub create_wrapper {
244 0     0 1 0 my $redirect = "stdinRedirect";
245 0         0 my ($self,$batchfile) = @_;
246 0         0 my $tempdir = $self->tempdir;
247 0         0 $self->update_ordered_parameters;
248              
249             #check version of HYPHY:
250 0         0 my $versionstring = $self->version();
251 0         0 $versionstring =~ /.*?(\d+\.\d+).*/;
252 0         0 my $version = $1;
253              
254 0         0 my $wrapper = "$tempdir/wrapper.bf";
255 0 0       0 open(WRAPPER, ">", $wrapper) or $self->throw("cannot open $wrapper for writing");
256              
257 0         0 print WRAPPER qq{$redirect = {};\n\n};
258 0         0 my $counter = sprintf("%02d", 0);
259 0         0 foreach my $elem (@{ $self->{'_orderedparams'} }) {
  0         0  
260 0         0 my ($param,$val) = each %$elem;
261 0 0       0 if ($val eq "") {
262 0         0 $val = "$tempdir/$param"; # any undefined parameters must be temporary output files.
263             }
264 0         0 print WRAPPER qq{$redirect ["$counter"] = "$val";\n};
265 0         0 $counter = sprintf("%02d",$counter+1);
266             }
267             # This next line is for BatchFile:
268 0 0       0 if ((ref ($self)) =~ m/BatchFile/) {
269 0         0 print WRAPPER "\nExecuteAFile ($batchfile, $redirect);\n";
270             } else {
271             # Not exactly sure what version of HYPHY caused this change,
272             # but Github source changes suggest that it was sometime
273             # after version 0.9920060501 was required.
274 0         0 $batchfile =~ s/"//g; # remove any extra quotes in the batchfile name.
275 0 0       0 if ($version >= 0.9920060501) {
276 0         0 print WRAPPER qq{\nExecuteAFile (HYPHY_LIB_DIRECTORY + "TemplateBatchFiles" + DIRECTORY_SEPARATOR + "$batchfile", stdinRedirect);\n};
277             } else {
278 0         0 print WRAPPER qq{\nExecuteAFile (HYPHY_BASE_DIRECTORY + "TemplateBatchFiles" + DIRECTORY_SEPARATOR + "$batchfile", stdinRedirect);\n};
279             }
280             }
281              
282 0         0 close(WRAPPER);
283 0         0 $self->{'_wrapper'} = $wrapper;
284             }
285              
286              
287             =head2 run
288              
289             Title : run
290             Usage : my ($rc,$results) = $BatchFile->run();
291             Function: run the Hyphy analysis using the specified batchfile and its ordered parameters
292             Returns : Return code, Hash
293             Args : none
294              
295              
296             =cut
297              
298             sub run {
299 0     0 1 0 my ($self) = @_;
300              
301 0         0 my $aln = $self->alignment;
302 0         0 my $tree = $self->tree;
303 0 0       0 unless (defined($self->{'_prepared'})) {
304 0         0 $self->prepare($aln,$tree);
305             }
306 0         0 my $rc = 1;
307 0         0 my $results = "";
308 0         0 my $commandstring;
309 0         0 my $exe = $self->executable();
310 0 0 0     0 unless ($exe && -e $exe && -x _) {
      0        
311 0         0 $self->throw("unable to find or run executable for 'HYPHY'");
312             }
313              
314             #runs the HYPHY command
315 0         0 $commandstring = $exe . " BASEPATH=" . $self->program_dir . " " . $self->{'_wrapper'};
316 0 0       0 my $pid = open(RUN, "-|", "$commandstring") or $self->throw("Cannot open exe $exe");
317 0         0 my $waiting = waitpid $pid,0;
318             # waitpid will leave a nonzero error in $? if the HYPHY command crashes, so we should bail gracefully.
319 0         0 my $error = $? & 127;
320 0 0       0 if ($error != 0) {
321 0         0 $self->throw("Error: " . $self->program_name . " ($waiting) quit unexpectedly with signal $error");
322             }
323             #otherwise, return the results and exit with 1 so that the parent knows we were successful.
324 0         0 while (my $line = ) {
325 0         0 $results .= "$line";
326             }
327 0         0 close(RUN);
328             # process the errors from $? and set the error values.
329 0         0 $rc = $? >> 8;
330 0 0 0     0 if (($results =~ m/error/i) || ($rc == 0)) { # either the child process had an error, or HYPHY put one in the output.
331 0         0 $rc = 0;
332 0         0 $self->warn($self->program_name . " reported error $rc - see error_string for the program output");
333 0         0 $results =~ m/(error.+)/is;
334 0         0 $self->error_string($1);
335             }
336              
337             # put these run results into the temp run output file:
338 0         0 open (OUT, ">", $self->{'run_output'});
339 0         0 print OUT $results;
340 0         0 close OUT;
341              
342 0         0 return ($rc,$results);
343             }
344              
345              
346              
347             =head2 error_string
348              
349             Title : error_string
350             Usage : $obj->error_string($newval)
351             Function: Where the output from the last analysus run is stored.
352             Returns : value of error_string
353             Args : newvalue (optional)
354              
355              
356             =cut
357              
358             sub error_string {
359 0     0 1 0 my ($self,$value) = @_;
360 0 0       0 if( defined $value) {
361 0         0 $self->{'error_string'} = $value;
362             }
363 0         0 return $self->{'error_string'};
364              
365             }
366              
367             =head2 alignment
368              
369             Title : alignment
370             Usage : $hyphy->alignment($aln);
371             Function: Get/Set the L object
372             Returns : L object
373             Comment : We could potentially add support for running directly on a file
374             but we shall keep it simple
375             See also: L
376              
377             =cut
378              
379             sub alignment {
380 0     0 1 0 my ($self,$aln) = @_;
381              
382 0 0       0 if( defined $aln ) {
383 0 0 0     0 if( -e $aln ) {
    0          
384 0         0 $self->{'_alignment'} = $aln;
385             } elsif( !ref($aln) || !$aln->isa('Bio::Align::AlignI') ) {
386 0         0 $self->warn("Must specify a valid Bio::Align::AlignI object to alignment(): you specified a " . ref($aln));
387 0         0 return;
388             } else {
389 0         0 $self->{'_alignment'} = $aln;
390             }
391             }
392 0         0 return $self->{'_alignment'};
393             }
394              
395             =head2 tree
396              
397             Title : tree
398             Usage : $hyphy->tree($tree);
399             Function: Get/Set the L object
400             Returns : L
401             Args : [optional] $tree => L,
402              
403             Comment : We could potentially add support for running directly on a file
404             but we shall keep it simple
405             See also: L
406              
407             =cut
408              
409             sub tree {
410 0     0 1 0 my ($self, $tree, %params) = @_;
411 0 0       0 if( defined $tree ) {
412 0 0 0     0 if( !ref($tree) || !$tree->isa('Bio::Tree::TreeI') ) {
413 0         0 $self->warn("Must specify a valid Bio::Tree::TreeI object to tree(): you specified a " . ref($tree));
414 0         0 return;
415             } else {
416 0         0 $self->{'_tree'} = $tree;
417             }
418             }
419 0         0 return $self->{'_tree'};
420             }
421              
422             =head2 get_parameters
423              
424             Title : get_parameters
425             Usage : my %params = $self->get_parameters();
426             Function: returns the list of parameters as a hash
427             Returns : associative array keyed on parameter names
428             Args : none
429              
430              
431             =cut
432              
433             sub get_parameters {
434 0     0 1 0 my ($self) = @_;
435             # we're returning a copy of this
436 0         0 return %{ $self->{'_params'} };
  0         0  
437             }
438              
439              
440             =head2 set_parameter
441              
442             Title : set_parameter
443             Usage : $hyphy->set_parameter($param,$val);
444             Function: Sets a hyphy parameter, will be validated against
445             the valid values.
446             The checks can be ignored if one turns off param checks like this:
447             $hyphy->no_param_checks(1)
448             Returns : boolean if set was success, if verbose is set to -1
449             then no warning will be reported
450             Args : $param => name of the parameter
451             $value => value to set the parameter to
452             See also: L
453              
454             =cut
455              
456              
457              
458             sub set_parameter {
459 0     0 1 0 my ($self,$param,$value) = @_;
460             # FIXME - add validparams checking
461 0         0 $self->{'_params'}{$param} = $value;
462 0         0 return 1;
463             }
464              
465             =head2 set_default_parameters
466              
467             Title : set_default_parameters
468             Usage : $obj->set_default_parameters();
469             Function: (Re)set the default parameters from the defaults
470             (the first value in each array in the valid_values() array)
471             Returns : none
472             Args : none
473              
474              
475             =cut
476              
477              
478             sub set_default_parameters {
479 1     1 1 2 my ($self) = @_;
480 1         4 my @validvals = $self->valid_values();
481 1         3 foreach my $elem (@validvals) {
482 5         10 keys %$elem; #reset hash iterator
483 5         9 my ($param,$val) = each %$elem;
484 5 100       15 if (ref($val)=~/ARRAY/i ) {
485 2 50       7 unless (ref($val->[0])=~/HASH/i) {
486 2         2 push @{ $self->{'_orderedparams'} }, {$param, $val->[0]};
  2         13  
487             } else {
488 0         0 $val = $val->[0];
489             }
490             }
491 5 50 66     24 if ( ref($val) =~ /HASH/i ) {
    100          
492 0         0 my $prevparam;
493 0         0 while (defined($val)) {
494 0 0       0 last unless (ref($val) =~ /HASH/i);
495 0 0       0 last unless (defined($param));
496 0         0 $prevparam = $param;
497 0         0 ($param,$val) = each %{$val};
  0         0  
498 0         0 push @{ $self->{'_orderedparams'} }, {$prevparam, $param};
  0         0  
499 0 0       0 push @{ $self->{'_orderedparams'} }, {$param, $val} if (defined($val));
  0         0  
500             }
501             } elsif (ref($val) !~ /HASH/i && ref($val) !~ /ARRAY/i) {
502 3         5 push @{ $self->{'_orderedparams'} }, {$param, $val};
  3         11  
503             }
504             }
505             }
506              
507              
508             =head2 update_ordered_parameters
509              
510             Title : update_ordered_parameters
511             Usage : $hyphy->update_ordered_parameters(0);
512             Function: (Re)set the default parameters from the defaults
513             (the first value in each array in the
514             %VALIDVALUES class variable)
515             Returns : none
516             Args : boolean: keep existing parameter values
517              
518              
519             =cut
520              
521             sub update_ordered_parameters {
522 0     0 1   my ($self) = @_;
523 0           for (my $i=0; $i < scalar(@{$self->{'_orderedparams'}}); $i++) {
  0            
524 0           my ($param,$val) = each %{$self->{'_orderedparams'}[$i]};
  0            
525 0 0         if (exists $self->{'_params'}{$param}) {
526 0           $self->{'_orderedparams'}[$i] = {$param, $self->{'_params'}{$param}};
527             } else {
528 0           $self->{'_orderedparams'}[$i] = {$param, $val};
529             }
530             }
531             }
532              
533             =head2 outfile_name
534              
535             Title : outfile_name
536             Usage : my $outfile = $hyphy->outfile_name();
537             Function: Get/Set the name of the output file for this run
538             (if you wanted to do something special)
539             Returns : string
540             Args : [optional] string to set value to
541              
542              
543             =cut
544              
545             sub outfile_name {
546 0     0 1   my $self = shift;
547 0 0         if( @_ ) {
548 0           return $self->{'_params'}->{'outfile'} = shift @_;
549             }
550 0           return $self->{'_params'}->{'outfile'};
551             }
552              
553             =head2 version
554              
555             Title : version
556             Usage : $obj->version()
557             Function: Returns the version string from HYPHY
558             Returns : string
559             Args : none
560              
561              
562             =cut
563              
564             sub version {
565 0     0 1   my $self = shift;
566 0           my $tempdir = $self->tempdir;
567 0 0         if (defined $self->{'_version'}) {
568 0           return $self->{'_version'};
569             }
570             # if it's not already defined, write out a small batchfile to return the version string, then clean up.
571 0           my $versionbf = "$tempdir/version.bf";
572 0 0         open(WRAPPER, ">", $versionbf) or $self->throw("cannot open $versionbf for writing");
573 0           print WRAPPER qq{GetString (versionString, HYPHY_VERSION, 2);\nfprintf (stdout, versionString);};
574 0           close(WRAPPER);
575 0           my $exe = $self->executable();
576 0 0 0       unless ($exe && -e $exe && -x _) {
      0        
577 0           $self->throw("unable to find or run executable for 'HYPHY'");
578             }
579 0           my $commandstring = $exe . " BASEPATH=" . $self->program_dir . " " . $versionbf;
580 0 0         open(RUN, "$commandstring |") or $self->throw("Cannot open exe $exe");
581 0           my $output = ;
582 0           close(RUN);
583 0           unlink $versionbf;
584 0           $self->{'_version'} = $output;
585 0           return $output;
586             }
587              
588             =head2 hyphy_lib_dir
589              
590             Title : hyphy_lib_dir
591             Usage : $obj->hyphy_lib_dir()
592             Function: Returns the HYPHY_LIB_DIRECTORY from HYPHY
593             Returns : string
594             Args : none
595              
596              
597             =cut
598              
599             sub hyphy_lib_dir {
600 0     0 1   my $self = shift;
601 0 0         if (defined $self->{'_hyphylibdir'}) {
602 0           return $self->{'_hyphylibdir'};
603             }
604             # if it's not already defined, write out a small batchfile to return the version string, then clean up.
605 0           my $hyphylibdirbf = $self->io->catfile($self->tempdir,"hyphylibdir.bf");
606 0 0         open(WRAPPER, ">", $hyphylibdirbf) or $self->throw("cannot open $hyphylibdirbf for writing");
607 0           print WRAPPER qq{fprintf (stdout, HYPHY_LIB_DIRECTORY);};
608 0           close(WRAPPER);
609 0           my $exe = $self->executable();
610 0 0 0       unless ($exe && -e $exe && -x _) {
      0        
611 0           $self->throw("unable to find or run executable for 'HYPHY'");
612             }
613 0           my $commandstring = $exe . " BASEPATH=" . $self->program_dir . " " . $hyphylibdirbf;
614 0 0         open(RUN, "$commandstring |") or $self->throw("Cannot open exe $exe");
615 0           my $output = ;
616 0           close(RUN);
617 0           unlink $hyphylibdirbf;
618 0           $self->{'_hyphylibdir'} = $output;
619 0           return $output;
620             }
621              
622              
623             1;