Finally for plotting with pgperl its useful to be able to read in molly files directly and subroutines to do this are listed in this file.
A fairly common problem is that you want to run molly, and then take an action dependent upon the results. There is apparently a way of doing this in one go but I believe that it requires rewriting molly code which I would prefer not to do. Instead one can apply the following procedure: (1) run molly once, (2) get out of it process results with perl, (3) run molly again with actions based upon results of earlier run. You can see this in some of the scripts below.
Here are some example scripts:
#!/usr/local/bin/perl # Perl routine to reorder a molly file to make the spectra ascend # in JD. This works for a maximum of $NMAX spectra and requires # molly to be able to fit in 2*$NMAX spectra. Change the line below # if you need to reorder more. # # Method: read in spectra, dump out list of RJDs, leave molly. Sort RJDS, # read in spectra a second time, reorder spectra dump to new file. # # Note that a file called junk is written. $NMAX = 100; # Arguments:# start up molly, read in spectra dump list of RJDs to a file # close down. open(MOLLY, "|molly") || die "Failed to start molly\n"; print MOLLY "load $ARGV[0] 1 $NMAX 1\n"; print MOLLY "flis 1 $NMAX junk 1 RJD\n"; print MOLLY "q\ny\n"; close(MOLLY); # read RJDs in, make an array which associates the spectrum # number equivalent to each RJD open(RJD, "junk") || die "Failed to open junk\n"; while( ){ ($number,$rjd) = split(' '); $which{$rjd} = $number; } close(RJD); # sort the RJDs into an ascending order array @order = sort byrjd keys(%which); # now start molly again, load spectra reorder them # and dump out open(MOLLY, "|molly") || die "Failed to start molly\n"; print MOLLY "load $ARGV[0] 1 $NMAX 1\n"; $i = 0; foreach(@order){ $from = $which{$_}; $to = $NMAX + (++$i); print MOLLY "copy $from $from $to 1\n"; } $start = $NMAX + 1; print MOLLY "write $ARGV[1] $start $to N\n"; print MOLLY "q\ny\n"; close(MOLLY); # comparison subroutine needed by sort command sub byrjd {$a <=> $b;}
#!/usr/local/bin/perl # Perl routine to strip ' off end of object names # Arguments:$NMAX = 100; # start up molly, read in spectra dump list of object names. open(MOLLY, "|molly") || die "Failed to start molly\n"; print MOLLY "load $ARGV[0] 1 $NMAX 1\n"; print MOLLY "flis 1 $NMAX junk 1 Object\n"; print MOLLY "q\ny\n"; close(MOLLY); # Read in object names, removing final ' if present open(OBJECT_NAMES, "junk") || die "Failed to open junk\n"; $i = 0; while( ){ if(/ *\d+ (.*)/){ ($object[$i++] = $1) =~ s/\'//; } } close(OBJECT_NAMES); # Generate a file to read into the header editor open(OBJECT_NAMES, ">junk") || die "Failed to open junk\n"; print OBJECT_NAMES "Object\nC\n"; for(@object){ $name = substr($_,0,32); print OBJECT_NAMES "\"$name\"\n"; } close(OBJECT_NAMES); # finally read in again, modify names and dump open(MOLLY, "|molly") || die "Failed to start molly\n"; print MOLLY "load $ARGV[0] 1 $NMAX 1\n"; print MOLLY "edit 1 $NMAX\n"; print MOLLY "file\njunk\nquit\n"; print MOLLY "write $ARGV[1] 1 $NMAX n\n"; print MOLLY "q\ny\n"; close(MOLLY);
#!/usr/local/bin/perl
#
# Perl routine to apply slit corrections with molly
#
# Arguments:
#
# narrow -- input file with narrow slit spectra
# wide -- input file with wide slit spectra. Should only have one/object
# or else only the last one will be used
# output -- output file
#
# There must be a file called "slit.msk" to mask the spectra
# prior to polynomial fitting.
#
# $NMAX = maximum number of spectra in either input spectra
# $NPOLY = number of poly coefficients
#
# molly must be able to handle 4*NARROW+WIDE spectra for this to work
# but it is not checked.
#
# Operation:
#
# 1) molly loads input files, dumps object names to two files. The record
# numbers of the wide slit spectra are also dumped.
# 2) Perl loads object names, stripping trailing '. It also loads the
# record numbers of the wide slit spectra.
# 3) For each wide slit spectrum, narrow slit spectra with precisely
# matching names are selected and copied into contiguous slots.
# A check on the relative positions is also made. A warning is printed
# if they are too discrepant and no slit correction is performed.
# 4) The wide slit spectrum is then copied a matching number of times
# 5) The wide slit spectrum is rebinned onto the same scale as the narrow
# slit spectra
# 6) The results from 5 are divided into the narrow slit spectra
# 7) Poly fits are made to the output from 6
# 8) The poly fits are divided into the narrow slit spectra
# 9) The now corrected spectra are copied back to their original places.
# The record number of the wide slit star used is inserted in the headers
# as an integer item called "Wide slit star"
# 10) The spectra ae dumped to disk
#
# ensure not overwriting input files
(($ARGV[0] ne $ARGV[2]) && ($ARGV[1] ne $ARGV[2])) ||
die "Will not overwrite inputs\n";
$NMAX = 100;
$NPOLY = 1; # number of poly coefficients
$DMAX = 1.; # maximum separation in arcminutes
print STDERR "Slit will perform slit corrections\n";
print STDERR " Narrow slit file (input): $ARGV[0]\n";
print STDERR " Wide slit file (input): $ARGV[1]\n";
print STDERR "Slit corrected file (output): $ARGV[2]\n";
print STDERR " Maximum number of spectra set at: $NMAX\n";
print STDERR "Number of poly coefficients set at: $NPOLY\n";
print STDERR "Maximum separation (arcmin) set at: $DMAX\n\n";
# First run molly to list object names (and record numbers of
# wide slit spectra.
open(STDOUT, ">logfile") || die "Failed to open log file\n";
open(MOLLY, "|molly") || die "Failed to start molly\n";
print MOLLY "confirm\n";
print MOLLY "load $ARGV[0] 1 $NMAX 1\n";
print MOLLY "flis 1 $NMAX junk1 3 Object RA Dec\n";
print MOLLY "clear 1 $NMAX sure\n";
print MOLLY "load $ARGV[1] 1 $NMAX 1\n";
print MOLLY "flis 1 $NMAX junk2 4 Object Record RA Dec\n";
print MOLLY "q\ny\n";
close(MOLLY);
# Read in narrow slit object names stripping trailing ' and
# storing RAs and Decs
open(INPUT, "junk1") || die "No junk1 around\n";
$i = 0;
while(){
chop;
if(/^ *\d* *(.*) *\' *(\S*) *(\S*) *$/){
$narrow_ra[$i] = $2;
$narrow_dec[$i] = $3;
($narrow[$i++] = $1) =~ s/ *$//;
}
}
close(INPUT);
$num_narrow = $i;
# Read in wide slit object names stripping trailing ' and storing
# record numbers, RA and Dec
open(INPUT, "junk2") || die "No junk2 around\n";
$i = 0;
while(){
chop;
if(/^ *\d* *(.*) *\' *(\S*) *(\S*) *(\S*) *$/){
$wide_rec[$i] = $2;
$wide_ra[$i] = $3;
$wide_dec[$i] = $4;
($wide[$i++] = $1) =~ s/ *$//;
}
}
close(INPUT);
$num_wide = $i;
# Now apply slit corrections.
open(MOLLY, "|molly") || die "Failed to start molly\n";
print MOLLY "confirm\n";
# read input files
$nstart = 1;
$nend = $num_narrow;
print MOLLY "load $ARGV[0] $nstart $nend 1\n";
$wstart = $num_narrow + 1;
$wend = $wstart + $num_wide - 1;
print MOLLY "load $ARGV[1] $wstart $wend 1\n";
$bstart = $num_narrow+$num_wide+1;
$first = 1;
$twopi = 8.*atan2(1.,1.);
for($nw = 0; $nw < $num_wide; $nw++){
# First check for matching narrow slit spectra
$spec = $wstart + $nw;
$wname = $wide[$nw];
$wra = $wide_ra[$nw];
$wdec = $wide_dec[$nw];
$cosd = cos($twopi*$wdec/360.);
$record = $wide_rec[$nw];
@list = ();
$nlist = 0;
for($i = 0; $i < $num_narrow; $i++){
if($narrow[$i] eq $wname){
$diff = sqrt((15.*60.*$cosd*($narrow_ra[$i]-$wra))**2 +
(60.*($narrow_dec[$i]-$wdec))**2);
if($diff < $DMAX){
$moveto = $bstart + $nlist;
$list[$nlist++] = ($nmatch = $nstart+$i);
print MOLLY "copy $nmatch $nmatch $moveto 1\n";
}
else{
print STDERR "Although the names of wide slit spectrum $nw"
. " and narrow slit spectrum $i match, they differ\n"
." in poisiotn by $diff arcminutes\n"
. "and no correction will be applied\n";
}
}
}
# There are narrow slit spectra to be fixed
if($nlist > 0){
$n1 = $bstart; # range of narrow slit
$n2 = $n1 + $nlist - 1; # spectra
$n3 = $n2 + 1; # range of multiply copied wide slit
$n4 = $n3 + $nlist - 1; # spectrum
$n5 = $n4 + 1; # range of rebinned wide slit
$n6 = $n5 + $nlist - 1; # spectra
print MOLLY "copy $spec $spec $n3 $nlist\n";
print MOLLY "tbin $n3 $n4 $n5 $n1 $n2 Q\n";
print MOLLY "div $n1 $n2 $n5 $n6 $n3\n";
# $n3 .. $n4 now have ratio spectra
if($first){
print MOLLY "pfit $n3 $n4 $n5 $NPOLY -3 3 10 0 y\n";
print MOLLY "load\nslit.msk\nq\n";
$first = 0;
}
else{
print MOLLY "pfit $n3 $n4 $n5 $NPOLY -3 3 10 0 n\n";
}
# $n5 .. $n6 now have poly fits
print MOLLY "div $n1 $n2 $n5 $n6 $n1\n";
# $n1 .. $n2 have slit corrected spectra. Copy back
# edit headers to include record number of wide slit spectrum.
for($j = 0; $j < $nlist; $j++){
$corr = $n1 + $j;
print MOLLY "edit $corr $corr\n";
print MOLLY "term\nWide slit star\nI\n$record\nquit\n";
print MOLLY "copy $corr $corr $list[$j] 1\n";
}
}
} # next wide slit spectrum
print MOLLY "write $ARGV[2] 1 $num_narrow n\n";
print MOLLY "q\ny\n";
close(MOLLY);
close(STDOUT);
There is one drawback to this. It is SLOW. For more than about 20 spectra it starts to become impractical. Karl Glazebrook (the author of pgperl) has now developed a way of handling binary arrays inside perl. I have not used it yet but you may want to if you want to follow this route.
The subroutines are listed below, but first here is an example showing them being called (assumes Perl5):
$file = 'spectra.mol';
open(DATA, $file) || die "Failed to open $file\n";
$i = 0;
# Read in data and store the fluxes in a series of arrays. The counts
# and errors are not required later and are overwritten each time
# the loop breaks when the end of the file is reached
while(&read_molly(DATA, \$npix, \$narc, \%header, \@arc, \@counts, \@errors,
\@{$flux[$i]})){
# calculate the wavelengths from the arc coefficients
@{$wave[$i]} = &calculate_wavelength(\@arc, $narc, $npix);
# remove the Earth velocity to get a heliocentric scale
&remove_earth_velocity(\@{$wave[$i]}, \%header);
# Correct the fluxes for zero count pixels
&correct_fluxes(\@counts,\@errors,\@{$flux[$i]},\@{$ferr[$i]});
$i++;
}
close(DATA);
$nspec = $i;
print "Read $nspec spectra\n";
The flux arrays can be used later. For example assuming the plot has been opened etc, here is some pgperl which plots the spectra with an offset from one to the next:
$off = 10.;
$add = 0.;
for($n = 0; $n < $nspec; $n++){
foreach(@{$flux[$n]}){
$_ += $add;
}
$add += $off;
# Plot spectrum
&pgbin(scalar(@{$flux[$n]}),\@{$wave[$n]},\@{$flux[$n]},1);
}
Here are the subroutines used above:
#read_molly
#
# read_molly($filehandle, \$npix, \$narc, \%header, \@arc, \@counts, \@errors,
# \@flux)
#
# Subroutine for reading a molly spectrum, assuming currently positioned
# at start of headers. Returns associative header array, counts, errors
# and fluxes. Header parameters can be accessed as in
#
# $object_name = $header{'Object'};
#
#read_molly
sub read_molly{
local($filehandle, *npix, *narc, *header, *arc,
*counts, *errors, *flux) = @_;
local($fcode,$units,$nchar,$ndoub,$nintr,$nreal);
local($buffer, @data);
local($nhead,$template,$nbytes,$ndata,$ncoeff);
local(@header_values,@header_names);
@arc = ();
@data = ();
@counts = ();
@errors = ();
@flux = ();
@header_values = ();
@header_names = ();
%header = ();
#
# Read and interpret first line of header
#
if(read($filehandle, $buffer, 52)) {
($fcode,$units,$npix,$narc,$nchar,$ndoub,$nintr,$nreal) =
unpack("x4ia16i7", $buffer);
#
# Read and interpret header item names
#
$nhead = $nchar + $ndoub + $nintr + $nreal;
read($filehandle, $buffer, 16*$nhead+8);
$template = "x4";
for (1..$nhead) {
$template .= "A16";
}
@header_names = unpack($template, $buffer);
foreach(@header_names){
s/ *$//;
}
#
# Read and interpret header item values
#
$nbytes = 32*$nchar + 8*$ndoub + 4*$nintr + 4*$nreal + 8;
read($filehandle, $buffer, $nbytes);
$template = "x4";
for (1..$nchar) {
$template .= "A32";
}
$template .= "d$ndoub i$nintr f$nreal";
@header_values = unpack($template, $buffer);
#
# Convert header to an associative array
#
for (0..$#header_names) {
$header{$header_names[$_]} = $header_values[$_];
}
#
# Read arc coefficients
#
$nbytes = $narc < 0 ? -8*$narc + 8 : 8*$narc + 8;
$ncoeff = $narc < 0 ? -$narc : $narc;
read($filehandle, $buffer, $nbytes);
$template = "x4d$ncoeff";
@arc = unpack($template, $buffer);
#
# Read data
#
$nbytes = 12*$npix + 8;
$ndata = 4*$npix;
read($filehandle, $buffer, $nbytes);
$template = "x4f$ndata";
@data = unpack($template, $buffer);
@counts = @data[0..$npix-1];
@errors = @data[$npix..2*$npix-1];
@flux = @data[2*$npix..3*$npix-1];
}
}
#skip_molly
#
# skip_molly($filehandle, $nskip)
#
# Subroutine for skipping a molly spectrum, assuming currently positioned
# at start of headers.
#
#skip_molly
sub skip_molly{
local($filehandle, $nskip) = @_;
local($fcode,$units,$npix,$narc,$nchar,$ndoub,$nintr,$nreal);
local($nbytes, $buffer);
for (1..$nskip) {
#
# Read and interpret first line of header
#
if(read($filehandle, $buffer, 52)) {
($fcode,$units,$npix,$narc,$nchar,$ndoub,$nintr,
$nreal) = unpack("x4ia16i7", $buffer);
#
# Compute number of bytes to skip
#
$nbytes = 16*($nchar + $ndoub + $nintr + $nreal)+8;
$nbytes += 32*$nchar + 8*$ndoub + 4*$nintr + 4*$nreal + 8;
$nbytes += $narc < 0 ? -8*$narc + 8 : 8*$narc + 8;
$nbytes += 12*$npix + 8;
read($filehandle, $buffer, $nbytes);
}
}
}
#calculate_wavelength
#
# @wave = calculate_wavelength(\@arc, $narc, $npix)
#
# Calculates wavelength scale equivalent to molly arc coefficients. No
# correction for the Earth's velocity is applied.
#
#calculate_wavelength
sub calculate_wavelength{
local(*arc,$narc,$npix) = @_;
local($i,$j,$fac,$z);
local(@wave) = ();
for $i (0..$npix-1) {
$wave[$i] = 0.;
$z = ($i+1)/$npix;
if($narc > 0) {
$fac = 1.;
for $j (0..$narc-1) {
$wave[$i] += $arc[$j]*$fac;
$fac *= $z;
}
}
elsif($narc < 0) {
$fac = 1.;
for $j (0..-$narc-1) {
$wave[$i] += $arc[$j]*$fac;
$fac *= $z;
}
$wave[$i] = exp($wave[$i]);
}
else {
$wave[$i] = $i + 1;
}
}
@wave;
}
#remove_earth_velocity
#
# remove_earth_velocity(\@wave, \%header)
#
# Looks up Earth velocity and corrects wavelengths for it.
#
#remove_earth_velocity
sub remove_earth_velocity{
local(*wave, *header) = @_;
local($i,$j,$fac,$z);
$corr_factor = 1. - $header{'Vearth'}/2.997925e5;
for (@wave) {
$_ *= $corr_factor;
}
}
#correct_fluxes
#
# correct_fluxes(\@counts, \@errors, \@flux, \@ferr)
#
# Checks for 0 count pixels and corrects fluxes. Also generates
# array of flux errors.
#
#correct_fluxes
sub correct_fluxes{
local(*counts, *errors, *flux, *ferr) = @_;
local($i);
@ferr = ();
for $i (0..$#flux) {
if($counts[$i] == 0.) {
$ferr[$i] = $errors[$i]/$flux[$i];
$flux[$i] = 0.;
}
else {
$ferr[$i] = $flux[$i]*$errors[$i]/$counts[$i];
}
$ferr[$i] = $ferr[$i]*$errors[$i] > 0 ? $ferr[$i] : -$ferr[$i];
}
}
#wave_to_kms
#
# @velocity = wave_to_kms(\@wave, $wzero)
#
# Converts from wavelength to velocity in km/s given a central
# wavelength corresponding to zero velocity which must have the
# same units as the wavelength array.
#
#wave_to_kms
sub wave_to_kms{
local(*wave, $wzero) = @_;
local($i,$vlight);
local(@velocity) = ();
$vlight = 2.997925e5;
for $i (0..$#wave) {
$velocity[$i] = $vlight*($wave[$i]-$wzero)/$wzero;
}
@velocity;
}
1;
#!/usr/local/bin/perl # # Perl routine to read in ascii files into molly and dump out # molly data file. It assumes that the list of ascii files is held # in a file called 'files.lis' # # Arguments:# open(STDOUT, ">logfile") || die "Failed to open log file\n"; open(MOLLY, "| molly") || die "Failed to start molly\n"; # load ascii spectra open(FILE, "files.lis") || die "failed to open files.lis\n"; $nspec = 0; while( ){ chop; $nspec++; print MOLLY "lasc $_ $nspec 1 2 -1 A MJY 0.01\n"; } close(FILE); # write out file, close down. print MOLLY "write $ARGV[0] 1 $nspec N\n"; print MOLLY "q\ny\n"; close(MOLLY);