File Coverage

blib/lib/Astro/Fluxes.pm
Criterion Covered Total %
statement 194 262 74.0
branch 60 114 52.6
condition 7 21 33.3
subroutine 17 20 85.0
pod 10 10 100.0
total 288 427 67.4


line stmt bran cond sub pod time code
1             package Astro::Fluxes;
2              
3             =head1 NAME
4              
5             Astro::Fluxes - Class for handling a collection of astronomical flux
6             quantities.
7              
8             =head1 SYNOPSIS
9              
10             use Astro::Fluxes;
11              
12             $fluxes = new Astro::Fluxes( $flux1, $flux2, $color1 );
13              
14             my $flux = $fluxes->flux( waveband => $waveband );
15              
16             =head1 DESCRIPTION
17              
18             Class for handling a collection of astronomical flux quantities.
19              
20             =cut
21              
22 2     2   3198 use 5.006;
  2         8  
  2         81  
23 2     2   12 use strict;
  2         5  
  2         56  
24 2     2   11 use warnings;
  2         4  
  2         56  
25 2     2   140 use warnings::register;
  2         3  
  2         413  
26 2     2   12 use Carp;
  2         4  
  2         167  
27              
28 2     2   56 use Astro::Flux;
  2         5  
  2         57  
29 2     2   11 use Astro::FluxColor;
  2         2  
  2         50  
30 2     2   10 use Astro::WaveBand;
  2         26  
  2         44  
31 2     2   2162 use Misc::Quality;
  2         4133  
  2         70  
32 2     2   2613 use Storable qw/ dclone /;
  2         11378  
  2         8898  
33              
34             our $VERSION = '0.01';
35              
36              
37              
38             =head1 METHODS
39              
40             =head2 CONSTRUCTOR
41              
42             =over 4
43              
44             =item B
45              
46             Create a new instance of an C object.
47              
48             $fluxes = new Astro::Fluxes( $flux1, $flux2, $color1 );
49              
50             Any number of C or C objects can
51             be passed as arguments.
52              
53             =cut
54              
55             sub new {
56 3     3 1 20 my $proto = shift;
57 3   33     16 my $class = ref( $proto ) || $proto;
58              
59 3         17 my $block = bless { FLUXES => {},
60             FLUX => [],
61             COLOR => [] }, $class;
62            
63              
64 3 50       20 $block = $block->pushfluxes( @_ ) if @_;
65 3         10 return $block;
66              
67             }
68              
69             =back
70              
71             =head2 Accessor Methods
72              
73             =over 4
74              
75             =item B
76              
77             Returns the flux for a requested waveband.
78              
79             my $flux = $fluxes->flux( waveband => 'J' );
80              
81             Arguments are passed as key-value pairs. The sole mandatory named
82             argument is 'waveband'; its value can either be an C
83             object or a string that can be used to create a new C
84             via its Filter parameter.
85              
86             Optional arguments are:
87              
88             derived - Whether or not to return fluxes that have been derived
89             from colors. Defaults to false, so that derived fluxes will not
90             be returned.
91              
92             datetime - whether we should return a flux from a specified object,
93             should be passed as a C object.
94              
95             type - which type of flux to return. If this is not given this method
96             will default to 'mag'. If you supply a type that is not
97             'mag' or 'magnitudes' and are attempting to return a derived flux,
98             this method will return undef.
99              
100             This method returns an C object.
101              
102             =cut
103              
104             sub flux {
105 25     25 1 13991 my $self = shift;
106 25         61 my %args = @_;
107              
108 25         34 my $result;
109              
110 25 50       59 if( ! defined( $args{'waveband'} ) ) {
111 0         0 croak "waveband argument must be passed to &Astro::Fluxes::flux";
112             }
113              
114 25         34 my $waveband = $args{'waveband'};
115 25 100       57 my $derived = defined( $args{'derived'} ) ? $args{'derived'} : 0;
116              
117 25 50       96 if( ! UNIVERSAL::isa( $waveband, "Astro::WaveBand" ) ) {
118             # Upgrade to a proper Astro::WaveBand object.
119 0         0 $waveband = new Astro::WaveBand( Filter => $waveband );
120             }
121              
122 25         34 my $datetime = $args{'datetime'};
123 25 50       46 if ( defined $datetime ) {
124 0 0       0 unless ( UNIVERSAL::isa( $datetime, "DateTime" ) ) {
125 0         0 croak( "Astro::Fluxes::flux() - Time must be a DateTime object\n" );
126             }
127             }
128              
129 25         33 my $type = $args{'type'};
130 25 100       50 if( ! defined( $args{'type'} ) ) {
131 24         34 $type = 'mag';
132             }
133              
134             # The key is the first character in the waveband.
135 25         69 my $key = $waveband->natural;
136              
137             # Check to see if we have a measured magnitude for this waveband.
138 25         825 foreach my $flux ( @{${$self->{FLUXES}}{$key}} ) {
  25         28  
  25         68  
139 28 100       74 if( ! defined( $flux->reference_waveband ) ) {
140 17 50 33     44 if( defined $datetime && defined $flux->datetime ) {
141 0 0       0 if( ($datetime <=> $flux->datetime()) == 0 ) {
142 0 0       0 if( lc( $type ) eq lc( $flux->type ) ) {
143 0         0 $result = $flux;
144 0         0 last;
145             }
146             }
147             } else {
148 17 100       54 if( lc( $type ) eq lc( $flux->type ) ) {
149 16         17 $result = $flux;
150 16         148 last;
151             }
152             }
153             }
154             }
155              
156 25 100       110 return $result if defined $result;
157              
158             # Return right here with undef if $derived is false.
159 9 100       29 return if ( ! $derived );
160              
161             # Return right here if we are not looking for a 'mag' or 'magnitude'.
162 6 50       20 return if ( $type !~ /^mag/i );
163              
164             # Get the reference waveband for the current flux such that the
165             # reference waveband doesn't have only a pointer back to the current
166             # one.
167              
168 6         7 my ($ref_flux, $ref_datetime);
169 6         6 my $running_total = undef;
170 6         6 my $running_error = undef;
171 6         7 foreach my $flux ( @{${$self->{FLUXES}}{$key}} ) {
  6         7  
  6         15  
172 6 50 33     13 if( defined( $flux->reference_waveband ) &&
      33        
173             ( scalar( @{${$self->{FLUXES}}{$flux->reference_waveband->natural}} > 1 ) ||
174             ${${$self->{FLUXES}}->{$flux->reference_waveband->natural}}[0]->reference_waveband != $waveband ) ) {
175 6 50       205 if ( defined $args{'datetime'} ) {
176 0 0       0 if ( defined $flux->datetime ) {
177 0         0 $running_total += $flux->quantity('mag');
178 0         0 $running_error += $flux->error('mag')*$flux->error('mag');
179 0         0 $ref_flux = ${${$self->{FLUXES}}->{$flux->reference_waveband->natural}}[0];
  0         0  
  0         0  
180 0         0 $ref_datetime = $flux->datetime();
181 0         0 last;
182             }
183             } else {
184 6         14 $running_total += $flux->quantity('mag');
185 6         16 $running_error += $flux->error('mag')*$flux->error('mag');
186 6         9 $ref_flux = ${${$self->{FLUXES}}{$flux->reference_waveband->natural}}[0];
  6         6  
  6         20  
187 6         187 last;
188             }
189             }
190             }
191              
192             # If we have a reference flux, get the magnitude from that waveband and add
193             # it to the running total.
194 6 50       15 if( defined( $ref_flux ) ) {
195 6         23 my $mag = $self->flux( waveband => $ref_flux->waveband, derived => 1 )->quantity('mag');
196 6         41 my $err = $self->flux( waveband => $ref_flux->waveband, derived => 1 )->error('mag');
197 6 50       21 if ( defined $args{'datetime'} ) {
198 0 0       0 if ( defined $ref_datetime ) {
199 0         0 $running_total += $mag;
200 0 0       0 $running_error += $err if defined $err;
201             }
202             } else {
203 6         6 $running_total += $mag;
204 6 50       16 $running_error += $err if defined $err;
205             }
206             }
207              
208 6 50       19 $running_error = sqrt( $running_error ) if defined $running_error;
209            
210             # Form a flux object with the running total and the input waveband,
211             # and return that.
212 6 50       11 if( ! defined( $running_total ) ) {
213 0         0 return undef;
214             } else {
215 6         7 my $number;
216 6 50       10 if ( defined $running_error ) {
217 6         17 $number = new Number::Uncertainty( Value => $running_total,
218             Error => $running_error );
219             } else {
220 0         0 $number = $running_total;
221             }
222            
223 6 50       401 if ( defined $args{'datetime'} ) {
224 0         0 my $returned_flux = new Astro::Flux( $number, 'mag', $waveband,
225             quality => new Misc::Quality( derived => 1 ),
226             datetime => $ref_datetime );
227 0         0 return $returned_flux;
228             } else {
229 6         21 my $returned_flux = new Astro::Flux( $number, 'mag', $waveband,
230             quality => new Misc::Quality( derived => 1 ) );
231 6         27 return $returned_flux;
232             }
233             }
234            
235             }
236              
237             =item B
238              
239             Returns the color for two requested wavebands.
240              
241             my $color = $fluxes->color( upper => new Astro::WaveBand( Filter => 'H' ),
242             lower => new Astro::WaveBand( Filter => 'J' ) );
243              
244             my $color = $fluxes->color( upper => new Astro::WaveBand( Filter => 'H' ),
245             lower => new Astro::WaveBand( Filter => 'J' ),
246             datetime => new DateTime );
247              
248             Arguments are passed as key-value pairs. The two mandatory named arguments are
249             'upper' and 'lower', denoting the upper (longer wavelength) and lower (shorter
250             wavelength) wavebands for the color. The value for either can be either an
251             C object or a string that can be used to create a new
252             C object via its Filter parameter.
253              
254             The above example will return the first H-K color in the Fluxes object. The
255             optional datetime arguement allows you to return a colour at a specific datetime
256             stamp.
257              
258             =cut
259              
260             sub color {
261 4     4 1 588 my $self = shift;
262 4         13 my %args = @_;
263              
264 4         5 my $result;
265              
266 4 50       14 if( ! defined( $args{'upper'} ) ) {
267 0         0 croak "upper waveband argument must be passed to &Astro::Fluxes::color";
268             }
269 4 50       12 if( ! defined( $args{'lower'} ) ) {
270 0         0 croak "lower waveband argument must be passed to &Astro::Fluxes::color";
271             }
272              
273 4         6 my $upper = $args{'upper'};
274 4         7 my $lower = $args{'lower'};
275              
276             # Upgrade the wavebands to proper Astro::WaveBand objects if necessary.
277 4 50       26 if( ! UNIVERSAL::isa( $upper, "Astro::WaveBand" ) ) {
278 0         0 $upper = new Astro::WaveBand( Filter => $upper );
279             }
280 4 50       16 if( ! UNIVERSAL::isa( $lower, "Astro::WaveBand" ) ) {
281 0         0 $lower = new Astro::WaveBand( Filter => $lower );
282             }
283              
284             # First, find out if we have an easy job. Check if the lower refers to
285             # the upper, from which we can get the colour directly.
286 4         13 my $upper_key = $upper->natural();
287 4         133 my $lower_key = $lower->natural();
288 4         127 foreach my $flux ( @{${$self->{FLUXES}}{$lower_key}} ) {
  4         7  
  4         13  
289 8 100       21 if( defined( $flux->reference_waveband ) ) {
290            
291 4 50       12 if ( defined $args{'datetime'} ) {
292 0 0       0 next unless defined $flux->datetime;
293 0 0       0 if ( ($flux->datetime <=> $args{'datetime'}) != 0 ) {
294 0         0 my $datetime = $flux->datetime;
295 0         0 next;
296             } else {
297 0         0 my $datetime = $flux->datetime;
298             }
299             }
300            
301 4         11 my $ref_key = $flux->reference_waveband()->natural();
302 4 100       212 if( $ref_key eq $upper_key ) {
303            
304 2         4 my $num;
305 2 50       9 if ( defined $flux->error('mag') ) {
306 2         7 $num = new Number::Uncertainty ( Value => $flux->quantity('mag'),
307             Error => $flux->error('mag') )
308             } else {
309 0         0 $num = new Number::Uncertainty ( Value => $flux->quantity('mag') );
310             }
311            
312 2 50       241 if ( defined $flux->datetime() ) {
313 0         0 my $color = new Astro::FluxColor( lower => $lower,
314             upper => $upper,
315             quantity => $num,
316             datetime => $flux->datetime() );
317 0         0 return $color;
318             } else {
319 2         14 my $color = new Astro::FluxColor( lower => $lower,
320             upper => $upper,
321             quantity => $num );
322 2         13 return $color;
323             }
324             }
325             }
326             }
327              
328             # So we're here. Maybe we can get magnitudes for the upper and lower wavebands.
329 2         5 my $upper_mag;
330             my $lower_mag;
331 2 50       9 if ( defined( $args{'datetime'} ) ) {
332 0         0 $upper_mag = $self->flux( waveband => $upper, derived => 1,
333             datetime => $args{'datetime'} );
334 0         0 $lower_mag = $self->flux( waveband => $lower, derived => 1,
335             datetime => $args{'datetime'} );
336             } else {
337 2         8 $upper_mag = $self->flux( waveband => $upper, derived => 1 );
338 2         8 $lower_mag = $self->flux( waveband => $lower, derived => 1 );
339             }
340 2 50 33     17 if( defined( $upper_mag ) && defined( $lower_mag ) ) {
341            
342 2         4 my $num;
343 2         13 my $value = $lower_mag->quantity('mag') - $upper_mag->quantity('mag');
344 2 50 33     7 if ( defined $upper_mag->error('mag') && $lower_mag->error('mag') ) {
345 0         0 my $error = sqrt( $upper_mag->error('mag')*$upper_mag->error('mag')
346             + $lower_mag->error('mag')*$lower_mag->error('mag') );
347 0         0 $num = new Number::Uncertainty ( Value => $value,
348             Error => $error )
349             } else {
350 2         8 $num = new Number::Uncertainty ( Value => $value );
351             }
352 2 50 33     79 if ( defined $lower_mag->datetime() && defined $upper_mag->datetime() ) {
353 0         0 my $color = new Astro::FluxColor( lower => $lower,
354             upper => $upper,
355             quantity => $num,
356             datetime => $lower_mag->datetime() );
357 0         0 return $color;
358             } else {
359 2         10 my $color = new Astro::FluxColor( lower => $lower,
360             upper => $upper,
361             quantity => $num );
362 2         17 return $color;
363             }
364             }
365              
366             # At this point I don't really know how to get a colour. If we're here
367             # that means we have some kind of colour-colour relation that we might
368             # be able to get the desired colour from...
369              
370             # Return undef in the meandatetime.
371 0         0 return undef;
372              
373             }
374              
375              
376             =item B
377              
378             Push C and C object into the C
379             object,
380              
381             $fluxes->pushfluxes( $flux1, $flux2, $color1 );
382              
383             Any number of C or C objects can
384             be passed as arguments.
385              
386             =cut
387              
388             sub pushfluxes {
389 4     4 1 12 my $self = shift;
390              
391 4         8 foreach my $arg ( @_ ) {
392 10 100       58 if( UNIVERSAL::isa( $arg, "Astro::Flux" ) ) {
    50          
393 6         20 my $key = $arg->waveband()->natural();
394 6         210 push @{${$self->{FLUXES}}{$key}}, $arg;
  6         9  
  6         30  
395 6         9 push @{$self->{FLUX}}, $arg->waveband();
  6         20  
396             } elsif( UNIVERSAL::isa( $arg, "Astro::FluxColor" ) ) {
397              
398             # Create an Misc::Quality object saying that these are derived
399             # magnitudes.
400 4         20 my $quality = new Misc::Quality( 'derived' => 1 );
401              
402             # Create two flux objects, one for the lower and one for the upper.
403 4         64 my $num = new Number::Uncertainty( Value => $arg->quantity,
404             Error => $arg->error );
405            
406 4         498 my ( $lower_flux, $upper_flux );
407 4 50       29 if ( defined $arg->datetime() ) {
408 0         0 $lower_flux = new Astro::Flux( $num , 'mag', $arg->lower,
409             quality => $quality,
410             reference_waveband => $arg->upper,
411             datetime => $arg->datetime );
412 0         0 $upper_flux = new Astro::Flux( -1.0 * $num, 'mag', $arg->upper,
413             quality => $quality,
414             reference_waveband => $arg->lower,
415             datetime => $arg->datetime );
416             } else {
417 4         12 $lower_flux = new Astro::Flux( $num , 'mag', $arg->lower,
418             quality => $quality,
419             reference_waveband => $arg->upper );
420 4         15 $upper_flux = new Astro::Flux( -1.0 * $num, 'mag', $arg->upper,
421             quality => $quality,
422             reference_waveband => $arg->lower );
423             }
424 4         13 my $lower_key = $lower_flux->waveband->natural;
425 4         144 my $upper_key = $upper_flux->waveband->natural;
426 4         117 push @{${$self->{FLUXES}}{$lower_key}}, $lower_flux;
  4         5  
  4         16  
427 4         8 push @{${$self->{FLUXES}}{$upper_key}}, $upper_flux;
  4         4  
  4         11  
428              
429 4         12 my $color = $arg->upper() . "-" . $arg->lower();
430 4         353 push @{$self->{COLOR}}, $color;
  4         14  
431              
432             }
433             }
434              
435 4         15 return $self;
436              
437             }
438              
439             =item B
440              
441             Returns an array of all the C objects contained in the
442             C object,
443              
444             @fluxes_not_dervied = $fluxes->allfluxes();
445             @fluxes_including_dervied = $fluxes->allfluxes( 'derived' );
446            
447             by default this will not return the derived fluxes, however the method
448             takes an optional arguement of 'derived', in which case it will do.
449              
450             =cut
451              
452             sub allfluxes {
453 2     2 1 1041 my $self = shift;
454            
455 2         4 my $flag;
456 2 100       78 if ( @_ ) {
457 1         34 my $arg = shift;
458 1 50       5 if( $arg eq 'derived' ) {
459 1         3 $flag = 1;
460             }
461             }
462            
463 2         3 my %fluxes = %{$self->{FLUXES}};
  2         15  
464            
465 2         2 my @allfluxes;
466 2         6 foreach my $key ( keys %fluxes ) {
467             #print "\n KEY = $key \n";
468 8         12 my $value = $fluxes{$key};
469            
470 8         12 foreach my $i ( 0 ... $#{$value} ) {
  8         19  
471 12         15 my $flux = ${$value}[$i];
  12         21  
472            
473             # push derived fluxes only if we were asked to...
474 12         57 my $quality = $flux->quality();
475 12 100       40 my $derived = $quality->query('derived') if defined $quality;
476             #print " $i, $derived\n";
477 12 100       69 if ( defined $derived ) {
478 8 100       17 push @allfluxes, $flux if defined $flag;
479             } else {
480 4         7 push @allfluxes, $flux;
481             }
482 12         13 $quality = undef;
483 12         26 $derived = undef;
484             }
485             }
486 2         11 return @allfluxes;
487             }
488              
489             =item B
490              
491             Returns an hash of all the C objects contained in the
492             C object,
493              
494             @fluxes = $fluxes->fluxesbywaveband( waveband => 'J' );
495              
496             =cut
497              
498             sub fluxesbywaveband {
499 1     1 1 879 my $self = shift;
500 1         5 my %args = @_;
501              
502 1         1 my $result;
503              
504 1 50       5 if( ! defined( $args{'waveband'} ) ) {
505 0         0 croak "waveband argument must be passed to &Astro::Fluxes::flux";
506             }
507              
508 1         3 my $waveband = $args{'waveband'};
509 1 50       7 my $derived = defined( $args{'derived'} ) ? $args{'derived'} : 0;
510              
511 1 50       9 if( ! UNIVERSAL::isa( $waveband, "Astro::WaveBand" ) ) {
512             # Upgrade to a proper Astro::WaveBand object.
513 1         5 $waveband = new Astro::WaveBand( Filter => $waveband );
514             }
515              
516             # The key is the first character in the waveband.
517 1         107 my $key = $waveband->natural();
518 1         35 return @{${$self->{FLUXES}}{$key}};
  1         2  
  1         7  
519             }
520              
521              
522             =item B
523              
524             Returns an array of the original (not derived) colors contained in the object
525              
526             @colors = $fluxes->original_colors( );
527              
528             =cut
529              
530             sub original_colors {
531 0     0 1 0 my $self = shift;
532 0         0 return @{$self->{COLOR}};
  0         0  
533             }
534              
535             =item B
536              
537             Returns an array of the original (not derived) filters contained in the object
538              
539             @wavebands = $fluxes->original_wavebands( );
540             @filters = $fluxes->original_wavebands( 'filters' );
541            
542             optional arguement 'filters' returns an actual filter list rather than a list
543             of C objects.
544              
545             =cut
546              
547             sub original_wavebands {
548 1     1 1 953 my $self = shift;
549            
550 1 50       7 return @{$self->{FLUX}} unless @_;
  0         0  
551            
552 1         3 my $arg = shift;
553 1 50       12 return undef unless lc($arg) eq 'filters';
554            
555 1         2 my @filters;
556 1         2 foreach my $band ( @{$self->{FLUX}} ) {
  1         4  
557 2         30 push @filters, $band->filter();
558             }
559 1         24 return @filters;
560            
561             }
562              
563             =item B
564              
565             Merges another C object with this object
566              
567             $fluxes1->merge( $fluxes2 );
568              
569             =cut
570              
571             sub merge {
572 0     0 1   my $self = shift;
573 0           my $other = shift;
574              
575 0 0         croak "Astro::Fluxes::merge() - Not an Astro::Fluxes object\n"
576             unless UNIVERSAL::isa( $other, "Astro::Fluxes" );
577              
578 0           my @fluxes = $other->allfluxes( 'derived' );
579 0           $self->pushfluxes( @fluxes );
580             }
581              
582              
583              
584             =item B
585              
586             Applies a datestamp to all C object with this object
587              
588             $fluxes->datestamp( new DateTime );
589              
590             =cut
591              
592             sub datestamp {
593 0     0 1   my $self = shift;
594 0           my $timestamp = shift;
595            
596 0 0         croak "Astro::Fluxes::datestamp() - Not an DateTime object\n"
597             unless UNIVERSAL::isa( $timestamp, "DateTime" );
598            
599            
600 0           foreach my $key ( keys %{$self->{FLUXES}} ) {
  0            
601 0           foreach my $j ( 0 ... $#{${$self->{FLUXES}}{$key}} ) {
  0            
  0            
602 0           my $date = dclone( $timestamp );
603 0           ${${$self->{FLUXES}}{$key}}[$j]->datetime( $date );
  0            
  0            
604             }
605             }
606            
607 0           return %{$self};
  0            
608              
609             }
610              
611             =back
612              
613             =head1 REVISION
614              
615             $Id: Fluxes.pm,v 1.19 2005/11/15 23:50:32 bradc Exp $
616              
617             =head1 AUTHORS
618              
619             Brad Cavanagh Eb.cavanagh@jach.hawaii.eduE,
620             Alasdair Allan Eaa@astro.ex.ac.ukE
621              
622             =head1 COPYRIGHT
623              
624             Copyright (C) 2004 - 2005 Particle Physics and Astronomy Research
625             Council. All Rights Reserved.
626              
627             This program is free software; you can redistribute it and/or
628             modify it under the same terms as Perl itself.
629              
630             =cut
631              
632             1;