astLib-0.8.0/ 0000775 0001750 0001750 00000000000 12377541253 013266 5 ustar matty matty 0000000 0000000 astLib-0.8.0/README 0000644 0001750 0001750 00000014215 12375447230 014145 0 ustar matty matty 0000000 0000000 astLib: python astronomy modules
version: 0.8.0
(c) 2007-2012 Matt Hilton
(c) 2013-2014 Matt Hilton & Steven Boada
http://astlib.sourceforge.net
email: matt.hilton@mykolab.com
-------------------------------------------------------------------------------------------------------------
INTRODUCTION
astLib provides some tools for research astronomers who use Python. It is divided into several modules:
- astCalc (general calculations, e.g. luminosity distance etc.)
- astCoords (coordinate conversions etc.)
- astImages (clip sections from .fits etc.)
- astPlots (provides a flexible image plot class, e.g. plot image with catalogue objects overlaid)
- astSED (calculate colours, magnitudes from stellar population models or spectral templates, fit
photometric observations using stellar population models etc.)
- astStats (statistics, e.g. biweight location/scale estimators etc.)
- astWCS (routines for using FITS World Coordinate System information)
The astWCS module is a higher level interface to PyWCSTools, a simple SWIG (http://www.swig.org) wrapping
of some of the routines from WCSTools by Jessica Mink (http://tdc-www.harvard.edu/software/wcstools/). It is
used by some routines in astCoords, astImages and astPlots.
The goal of astLib is to provide features useful to astronomers that are not included in the scipy
(http://scipy.org), numpy (http://numpy.scipy.org) or matplotlib (http://matplotlib.sourceforge.net) modules
on which astLib depends. As such, the astStats module is unlikely to be extended beyond its present state
due to the extensive statistical routines provided by scipy (I also recommend the Python bindings for GNU R
- http://rpy.sourceforge.net).
Some scripts using astLib can be found in the examples/ folder in this archive.
-------------------------------------------------------------------------------------------------------------
INSTALLATION
astLib 0.8.0 requires:
* Python
(tested on versions 2.7.6 and 3.4.0)
* PyFITS - http://www.stsci.edu/resources/software_hardware/pyfits
(tested on version 3.3)
* numpy - http://numpy.scipy.org
(tested on version 1.8.1)
* scipy - http://scipy.org
(tested on version 0.13.3)
* matplotlib - http://matplotlib.sourceforge.net
(tested on versions 1.3.1)
optional:
* Python Imaging Library - http://www.pythonware.com/products/pil
(tested on version 1.1.7)
* astropy - http://www.astropy.org/
(tested on 0.4)
This is currently only used if pyfits is not installed (astropy.io.fits at present provides
a drop-in replacement for pyfits).
Other versions of the software listed above are likely to work - I'm unable to test every possible
combination. For reference, astLib is currently developed on Kubuntu 14.04 (x86_64). Note that it is
possible to use some astLib functions (such as the astWCS module) without installing all of the python
modules listed above.
To install astLib system wide (and build and install PyWCSTools), simply:
% sudo python setup.py install
in the directory this README is in.
If you do not have root access to your machine, astLib can be installed in your home directory by
doing the following:
% python setup.py install --prefix=$HOME/python-modules
and adding something like the following to your .bashrc (or equivalent):
export PYTHONPATH=$PYTHONPATH:$HOME/python-modules/lib/python2.5/site-packages
-------------------------------------------------------------------------------------------------------------
USAGE
To access the routines in the astLib modules, simply:
% from astLib import astCalc
% from astLib import astCoords
% from astLib import astWCS
etc.
The astWCS module currently provides access to what are (I think) the most commonly needed WCS information
and functions (such as converting between pixel and WCS coordinates etc.). However, should you wish to
access the wrapped WCSTools routines themselves directly:
% from PyWCSTools import wcs
% from PyWCSTools import wcscon
etc.
Note that PyWCSTools only includes some functions from wcs.c and wcscon.c at present. For examples of usage,
look at the Python code for the astLib.astWCS module. Documentation for the WCSTools routines can be found
here: http://tdc-www.harvard.edu/software/wcstools/subroutines/libwcs.wcs.html.
-------------------------------------------------------------------------------------------------------------
KNOWN ISSUES
This may no longer apply, but just in case...
Recent versions of matplotlib (on which astLib depends) now use locale information. On systems where the
decimal point separator is not '.' (e.g. Germany), the astWCS coordinate conversions routines will give
strange results if this is not accounted for. As of version 0.3.0, the astWCS module will detect if this is
the case and print a warning message to the console.
The workaround for this issue is to add the following after importing any python modules that expicitly set
the locale (such as matplotlib):
% import locale
% locale.setlocale(locale.LC_NUMERIC, 'C')"
Thanks to Markus Demleitner for pointing this out.
-------------------------------------------------------------------------------------------------------------
DOCUMENTATION
Documentation is available on the web at:
http://astlib.sourceforge.net/?q=docs0.7
The API reference documentation (generated using epydoc) is also supplied in HTML format in this archive
under the docs/astLib/ directory.
-------------------------------------------------------------------------------------------------------------
BUGS
Please email bug reports to matt.hilton@mykolab.com, or alternatively use the bug tracker:
http://sourceforge.net/p/astlib/bugs/
Please include details of your operating system, python version, and versions of the python packages
required by astLib that you have installed on your machine. For any WCS-related bugs, it would be helpful
if you could also include the image header as a text file so that I can reproduce them easily.
-------------------------------------------------------------------------------------------------------------
astLib-0.8.0/setup.py 0000644 0001750 0001750 00000010140 12375455346 014777 0 ustar matty matty 0000000 0000000 # -*- coding: utf-8 -*-
#File: setup.py
#Created: Sat Dec 15 19:40:30 2012
#Last Change: Sat Dec 15 19:42:45 2012
import os
import glob
from distutils.core import setup
from distutils.command.build_ext import build_ext
from distutils.extension import Extension
import distutils.ccompiler
import distutils.command.config
import distutils.sysconfig
from pkg_resources import require
topDir = os.getcwd()
sourceDir = "PyWCSTools"+os.path.sep+"wcssubs-3.8.7"+os.path.sep
#oFiles=glob.glob(sourceDir+"*.o")
#print oFiles
oFiles = ['PyWCSTools/wcssubs-3.8.7/cel.o', 'PyWCSTools/wcssubs-3.8.7/wcs.o',
'PyWCSTools/wcssubs-3.8.7/proj.o', 'PyWCSTools/wcssubs-3.8.7/distort.o',
'PyWCSTools/wcssubs-3.8.7/wcsinit.o', 'PyWCSTools/wcssubs-3.8.7/wcslib.o',
'PyWCSTools/wcssubs-3.8.7/poly.o', 'PyWCSTools/wcssubs-3.8.7/platepos.o',
'PyWCSTools/wcssubs-3.8.7/zpxpos.o', 'PyWCSTools/wcssubs-3.8.7/iget.o',
'PyWCSTools/wcssubs-3.8.7/imio.o', 'PyWCSTools/wcssubs-3.8.7/dsspos.o',
'PyWCSTools/wcssubs-3.8.7/tnxpos.o', 'PyWCSTools/wcssubs-3.8.7/wcscon.o',
'PyWCSTools/wcssubs-3.8.7/fitsfile.o',
'PyWCSTools/wcssubs-3.8.7/dateutil.o',
'PyWCSTools/wcssubs-3.8.7/imhfile.o', 'PyWCSTools/wcssubs-3.8.7/lin.o',
'PyWCSTools/wcssubs-3.8.7/fileutil.o',
'PyWCSTools/wcssubs-3.8.7/wcstrig.o',
'PyWCSTools/wcssubs-3.8.7/slasubs.o', 'PyWCSTools/wcssubs-3.8.7/sph.o',
'PyWCSTools/wcssubs-3.8.7/worldpos.o', 'PyWCSTools/wcssubs-3.8.7/hget.o',
'PyWCSTools/wcssubs-3.8.7/hput.o']
exampleScripts = glob.glob("scripts"+os.path.sep+"*.py")
class build_PyWCSTools_ext(build_ext):
def build_extensions(self):
os.chdir(sourceDir)
# This line is tough to make match the style guide
cc =distutils.ccompiler.new_compiler(
distutils.ccompiler.get_default_compiler())
distutils.command.config.customize_compiler(cc)
# Suppress warnings from compiling WCSTools wcssubs-3.8.7
if "-Wstrict-prototypes" in cc.compiler_so:
cc.compiler_so.pop(cc.compiler_so.index("-Wstrict-prototypes"))
if "-Wall" in cc.compiler_so:
cc.compiler_so.pop(cc.compiler_so.index("-Wall"))
WCSToolsCFiles = glob.glob("*.c")
WCSToolsCFiles.pop(WCSToolsCFiles.index("wcs_wrap.c"))
WCSToolsCFiles.pop(WCSToolsCFiles.index("wcscon_wrap.c"))
cc.compile(WCSToolsCFiles)
os.chdir(topDir)
build_ext.build_extensions(self)
setup(name='astLib',
version='0.8.0',
url='http://astlib.sourceforge.net',
download_url='http://sourceforge.net/project/platformdownload.php?group_id=202537',
author='Matt Hilton',
author_email='matt.hilton@mykolab.com',
classifiers=['Development Status :: 5 - Production/Stable',
'Environment :: Console',
'Intended Audience :: Science/Research',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'Natural Language :: English',
'Operating System :: POSIX',
'Programming Language :: Python',
'Topic :: Scientific/Engineering :: Astronomy',
'Topic :: Software Development :: Libraries'],
description='A set of python modules for producing simple plots, statistics, common calculations, coordinate conversions, and manipulating FITS images with World Coordinate System (WCS) information.',
long_description="""astLib is a set of Python modules that provides some tools for research astronomers. It can be
used for simple plots, statistics, common calculations, coordinate conversions, and manipulating FITS images
with World Coordinate System (WCS) information through PyWCSTools - a simple wrapping of WCSTools by Jessica Mink.
PyWCSTools is distributed (and developed) as part of astLib.""",
packages=['astLib', 'PyWCSTools'],
package_data={'astLib': ['data/*']},
cmdclass={"build_ext": build_PyWCSTools_ext},
scripts=exampleScripts,
ext_modules=[
Extension('PyWCSTools._wcscon', [sourceDir+"wcscon_wrap.c"],
extra_objects=oFiles),
Extension('PyWCSTools._wcs', [sourceDir+"wcs_wrap.c"],
extra_objects=oFiles)
]
)
astLib-0.8.0/docs/ 0000775 0001750 0001750 00000000000 12377541253 014216 5 ustar matty matty 0000000 0000000 astLib-0.8.0/docs/astLib/ 0000775 0001750 0001750 00000000000 12377541253 015434 5 ustar matty matty 0000000 0000000 astLib-0.8.0/docs/astLib/astLib.astWCS-pysrc.html 0000644 0001750 0001750 00000330417 12375434532 022046 0 ustar matty matty 0000000 0000000
1"""module for handling World Coordinate Systems (WCS) 2 3(c) 2007-2012 Matt Hilton 4 5(c) 2013-2014 Matt Hilton & Steven Boada 6 7U{http://astlib.sourceforge.net} 8 9This is a higher level interface to some of the routines in PyWCSTools 10(distributed with astLib). 11PyWCSTools is a simple SWIG wrapping of WCSTools by Jessica Mink 12(U{http://tdc-www.harvard.edu/software/wcstools/}). It is intended is to make 13this interface complete enough such that direct use of PyWCSTools is 14unnecessary. 15 16@var NUMPY_MODE: If True (default), pixel coordinates accepted/returned by 17 routines such as L{astWCS.WCS.pix2wcs}, L{astWCS.WCS.wcs2pix} have (0, 0) 18 as the origin. Set to False to make these routines accept/return pixel 19 coords with (1, 1) as the origin (i.e. to match the FITS convention, 20 default behaviour prior to astLib version 0.3.0). 21@type NUMPY_MODE: bool 22 23""" 24 25#----------------------------------------------------------------------------- 26 27# So far as I can tell in astropy 0.4 the API is the same as pyfits for what we need... 28try: 29importpyfits 30except: 31try: 32fromastropy.ioimportfitsaspyfits 33except: 34raiseException,"couldn't import either pyfits or astropy.io.fits" 35fromPyWCSToolsimportwcs 36importnumpy 37importlocale 38 39# if True, -1 from pixel coords to be zero-indexed like numpy. If False, use 40# FITS convention. 41NUMPY_MODE=True 42 43# Check for the locale bug when decimal separator isn't '.' (atof used in 44# libwcs) 45lconv=locale.localeconv() 46iflconv['decimal_point']!='.': 47print("WARNING: decimal point separator is not '.' - astWCS coordinate conversions will not work.") 48print("Workaround: after importing any modules that set the locale (e.g. matplotlib) do the following:") 49print(" import locale") 50print(" locale.setlocale(locale.LC_NUMERIC, 'C')") 51 52#-----------------------------------------------------------------------------
54"""This class provides methods for accessing information from the World 55 Coordinate System (WCS) contained in the header of a FITS image. 56 Conversions between pixel and WCS coordinates can also be performed. 57 58 To create a WCS object from a FITS file called "test.fits", simply: 59 60 WCS=astWCS.WCS("test.fits") 61 62 Likewise, to create a WCS object from the pyfits.header of "test.fits": 63 64 img=pyfits.open("test.fits") 65 header=img[0].header 66 WCS=astWCS.WCS(header, mode = "pyfits") 67 68 """ 69
71"""Creates a WCS object using either the information contained in the 72 header of the specified .fits image, or from a pyfits.header object. 73 Set mode = "pyfits" if the headerSource is a pyfits.header. 74 75 For some images from some archives, particular header keywords such as 76 COMMENT or HISTORY may contain unprintable strings. If you encounter 77 this, try setting zapKeywords = ['COMMENT', 'HISTORY'] (for example). 78 79 @type headerSource: string or pyfits.header 80 @param headerSource: filename of input .fits image, or a pyfits.header 81 object 82 @type extensionName: int or string 83 @param extensionName: name or number of .fits extension in which image 84 data is stored 85 @type mode: string 86 @param mode: set to "image" if headerSource is a .fits file name, or 87 set to "pyfits" if headerSource is a pyfits.header object 88 @type zapKeywords: list 89 @param: zapKeywords: keywords to remove from the header before making 90 astWCS object. 91 92 @note: The meta data provided by headerSource is stored in WCS.header 93 as a pyfits.header object. 94 95 """ 96 97self.mode=mode 98self.headerSource=headerSource 99self.extensionName=extensionName100101ifself.mode=="image":102img=pyfits.open(self.headerSource)103# silentfix below won't deal with unprintable strings104# so here we optionally remove problematic keywords105forzinzapKeywords:106ifzinimg[self.extensionName].header.keys():107forcountinrange(img[self.extensionName].header.count(z)):108img[self.extensionName].header.remove(z)109img.verify('silentfix')# solves problems with non-standard headers110self.header=img[self.extensionName].header111img.close()112elifself.mode=="pyfits":113forzinzapKeywords:114ifzinself.headerSource.keys():115forcountinrange(self.headerSource.count(z)):116self.headerSource.remove(z)117self.header=headerSource118119self.updateFromHeader()
123"""Copies the WCS object to a new object.124125 @rtype: astWCS.WCS object126 @return: WCS object127128 """129130# This only sets up a new WCS object, doesn't do a deep copy131ret=WCS(self.headerSource,self.extensionName,self.mode)132133# This fixes copy bug134ret.header=self.header.copy()135ret.updateFromHeader()136137returnret
141"""Updates the WCS object using information from WCS.header. This142 routine should be called whenever changes are made to WCS keywords in143 WCS.header.144145 """146147# Updated for pyfits 3.1+148newHead=pyfits.Header()149foriinself.header.items():150iflen(str(i[1]))<70:151iflen(str(i[0]))<=8:152newHead.append((i[0],i[1]))153else:154newHead.append(('HIERARCH '+i[0],i[1]))155156# Workaround for ZPN bug when PV2_3 == 0 (as in, e.g., ESO WFI images)157if"PV2_3"inlist(newHead.keys())andnewHead['PV2_3']==0andnewHead['CTYPE1']=='RA---ZPN':158newHead["PV2_3"]=1e-15159160cardstring=""161forcardinnewHead.cards:162cardstring=cardstring+str(card)163164self.WCSStructure=wcs.wcsinit(cardstring)
168"""Returns the RA and dec coordinates (in decimal degrees) at the169 centre of the WCS.170171 @rtype: list172 @return: coordinates in decimal degrees in format [RADeg, decDeg]173174 """175full=wcs.wcsfull(self.WCSStructure)176177RADeg=full[0]178decDeg=full[1]179180return[RADeg,decDeg]
184"""Returns the width, height of the image according to the WCS in185 decimal degrees on the sky (i.e., with the projection taken into186 account).187188 @rtype: list189 @return: width and height of image in decimal degrees on the sky in190 format [width, height]191192 """193full=wcs.wcsfull(self.WCSStructure)194195width=full[2]196height=full[3]197198return[width,height]
202"""Returns the half-width, half-height of the image according to the203 WCS in RA and dec degrees.204205 @rtype: list206 @return: half-width and half-height of image in R.A., dec. decimal207 degrees in format [half-width, half-height]208209 """210half=wcs.wcssize(self.WCSStructure)211212width=half[2]213height=half[3]214215return[width,height]
219"""Returns the minimum, maximum WCS coords defined by the size of the220 parent image (as defined by the NAXIS keywords in the image header).221222 @rtype: list223 @return: [minimum R.A., maximum R.A., minimum Dec., maximum Dec.]224225 """226227# Get size of parent image this WCS is taken from228maxX=self.header['NAXIS1']229maxY=self.header['NAXIS2']230minX=1.0231minY=1.0232233ifNUMPY_MODE==True:234maxX=maxX-1235maxY=maxY-1236minX=minX-1237minY=minY-1238239bottomLeft=self.pix2wcs(minX,minY)240topRight=self.pix2wcs(maxX,maxY)241242xCoords=[bottomLeft[0],topRight[0]]243yCoords=[bottomLeft[1],topRight[1]]244xCoords.sort()245yCoords.sort()246247return[xCoords[0],xCoords[1],yCoords[0],yCoords[1]]
251"""Returns the pixel coordinates corresponding to the input WCS252 coordinates (given in decimal degrees). RADeg, decDeg can be single253 floats, or lists or numpy arrays.254255 @rtype: list256 @return: pixel coordinates in format [x, y]257258 """259260iftype(RADeg)==numpy.ndarrayortype(RADeg)==list:261iftype(decDeg)==numpy.ndarrayortype(decDeg)==list:262pixCoords=[]263forra,decinzip(RADeg,decDeg):264pix=wcs.wcs2pix(self.WCSStructure,float(ra),float(dec))265# Below handles CEA wraparounds266ifpix[0]<1:267xTest=((self.header['CRPIX1'])-(ra-360.0)/268self.getXPixelSizeDeg())269ifxTest>=1andxTest<self.header['NAXIS1']:270pix[0]=xTest271ifNUMPY_MODE==True:272pix[0]=pix[0]-1273pix[1]=pix[1]-1274pixCoords.append([pix[0],pix[1]])275else:276pixCoords=(wcs.wcs2pix(self.WCSStructure,float(RADeg),277float(decDeg)))278# Below handles CEA wraparounds279ifpixCoords[0]<1:280xTest=((self.header['CRPIX1'])-(RADeg-360.0)/281self.getXPixelSizeDeg())282ifxTest>=1andxTest<self.header['NAXIS1']:283pixCoords[0]=xTest284ifNUMPY_MODE==True:285pixCoords[0]=pixCoords[0]-1286pixCoords[1]=pixCoords[1]-1287pixCoords=[pixCoords[0],pixCoords[1]]288289returnpixCoords
293"""Returns the WCS coordinates corresponding to the input pixel294 coordinates.295296 @rtype: list297 @return: WCS coordinates in format [RADeg, decDeg]298299 """300iftype(x)==numpy.ndarrayortype(x)==list:301iftype(y)==numpy.ndarrayortype(y)==list:302WCSCoords=[]303forxc,ycinzip(x,y):304ifNUMPY_MODE==True:305xc+=1306yc+=1307WCSCoords.append(wcs.pix2wcs(self.WCSStructure,float(xc),308float(yc)))309else:310ifNUMPY_MODE==True:311x+=1312y+=1313WCSCoords=wcs.pix2wcs(self.WCSStructure,float(x),float(y))314315returnWCSCoords
319"""Returns True if the given RA, dec coordinate is within the image320 boundaries.321322 @rtype: bool323 @return: True if coordinate within image, False if not.324325 """326327pixCoords=wcs.wcs2pix(self.WCSStructure,RADeg,decDeg)328ifpixCoords[0]>=0andpixCoords[0]<self.header['NAXIS1']and \ 329pixCoords[1]>=0andpixCoords[1]<self.header['NAXIS2']:330returnTrue331else:332returnFalse
336"""Returns the rotation angle in degrees around the axis, North through337 East.338339 @rtype: float340 @return: rotation angle in degrees341342 """343returnself.WCSStructure.rot
347"""Returns 1 if image is reflected around axis, otherwise returns 0.348349 @rtype: int350 @return: 1 if image is flipped, 0 otherwise351352 """353returnself.WCSStructure.imflip
357"""Returns the pixel scale of the WCS. This is the average of the x, y358 pixel scales.359360 @rtype: float361 @return: pixel size in decimal degrees362363 """364365avSize=(abs(self.WCSStructure.xinc)+abs(self.WCSStructure.yinc))/2.0366367returnavSize
371"""Returns the pixel scale along the x-axis of the WCS in degrees.372373 @rtype: float374 @return: pixel size in decimal degrees375376 """377378avSize=abs(self.WCSStructure.xinc)379380returnavSize
384"""Returns the pixel scale along the y-axis of the WCS in degrees.385386 @rtype: float387 @return: pixel size in decimal degrees388389 """390391avSize=abs(self.WCSStructure.yinc)392393returnavSize
419"""Finds the minimum, maximum WCS coords that overlap between wcs1 and420 wcs2. Returns these coordinates, plus the corresponding pixel coordinates421 for each wcs. Useful for clipping overlapping region between two images.422423 @rtype: dictionary424 @return: dictionary with keys 'overlapWCS' (min, max RA, dec of overlap425 between wcs1, wcs2) 'wcs1Pix', 'wcs2Pix' (pixel coords in each input426 WCS that correspond to 'overlapWCS' coords)427428 """429430mm1=wcs1.getImageMinMaxWCSCoords()431mm2=wcs2.getImageMinMaxWCSCoords()432433overlapWCSCoords=[0.0,0.0,0.0,0.0]434435# Note order swapping below is essential436# Min RA437ifmm1[0]-mm2[0]<=0.0:438overlapWCSCoords[0]=mm2[0]439else:440overlapWCSCoords[0]=mm1[0]441442# Max RA443ifmm1[1]-mm2[1]<=0.0:444overlapWCSCoords[1]=mm1[1]445else:446overlapWCSCoords[1]=mm2[1]447448# Min dec.449ifmm1[2]-mm2[2]<=0.0:450overlapWCSCoords[2]=mm2[2]451else:452overlapWCSCoords[2]=mm1[2]453454# Max dec.455ifmm1[3]-mm2[3]<=0.0:456overlapWCSCoords[3]=mm1[3]457else:458overlapWCSCoords[3]=mm2[3]459460# Get corresponding pixel coords461p1Low=wcs1.wcs2pix(overlapWCSCoords[0],overlapWCSCoords[2])462p1High=wcs1.wcs2pix(overlapWCSCoords[1],overlapWCSCoords[3])463p1=[p1Low[0],p1High[0],p1Low[1],p1High[1]]464465p2Low=wcs2.wcs2pix(overlapWCSCoords[0],overlapWCSCoords[2])466p2High=wcs2.wcs2pix(overlapWCSCoords[1],overlapWCSCoords[3])467p2=[p2Low[0],p2High[0],p2Low[1],p2High[1]]468469return{'overlapWCS':overlapWCSCoords,'wcs1Pix':p1,'wcs2Pix':p2}
1"""module for producing astronomical plots 2 3(c) 2007-2013 Matt Hilton 4 5U{http://astlib.sourceforge.net} 6 7This module provides the matplotlib powered ImagePlot class, which is designed to be flexible. 8ImagePlots can have RA, Dec. coordinate axes, contour overlays, and have objects marked in them, 9using WCS coordinates. RGB plots are supported too. 10 11@var DEC_TICK_STEPS: Defines the possible coordinate label steps on the delination axis in 12sexagesimal mode. Dictionary format: {'deg', 'unit'} 13@type DEC_TICK_STEPS: dictionary list 14 15@var RA_TICK_STEPS: Defines the possible coordinate label steps on the right ascension axis in 16sexagesimal mode. Dictionary format: {'deg', 'unit'} 17@type RA_TICK_STEPS: dictionary list 18 19@var DECIMAL_TICK_STEPS: Defines the possible coordinate label steps on both coordinate axes in 20decimal degrees mode. 21@type DECIMAL_TICK_STEPS: list 22 23@var DEG: Variable to stand in for the degrees symbol. 24@type DEG: string 25 26@var PRIME: Variable to stand in for the prime symbol. 27@type PRIME: string 28 29@var DOUBLE_PRIME: Variable to stand in for the double prime symbol. 30@type DOUBLE_PRIME: string 31 32""" 33 34importmath 35from.importastImages 36from.importastWCS 37from.importastCoords 38importnumpy 39importpyfits 40fromscipyimportinterpolate 41importpylab 42importmatplotlib.patchesaspatches 43importsys 44 45# Handle unicode python 2 and 3 46ifsys.version<'3': 47importcodecs
99"""This class describes a matplotlib image plot containing an astronomical image with an 100 associated WCS. 101 102 Objects within the image boundaries can be marked by passing their WCS coordinates to 103 L{ImagePlot.addPlotObjects}. 104 105 Other images can be overlaid using L{ImagePlot.addContourOverlay}. 106 107 For images rotated with North at the top, East at the left (as can be done using 108 L{astImages.clipRotatedImageSectionWCS} or L{astImages.resampleToTanProjection}, WCS coordinate 109 axes can be plotted, with tick marks set appropriately for the image size. Otherwise, a compass 110 can be plotted showing the directions of North and East in the image. 111 112 RGB images are also supported. 113 114 The plot can of course be tweaked further after creation using matplotlib/pylab commands. 115 116 """
121"""Makes an ImagePlot from the given image array and astWCS. For coordinate axes to work, the 122 image and WCS should have been rotated such that East is at the left, North is at the top 123 (see e.g. L{astImages.clipRotatedImageSectionWCS}, or L{astImages.resampleToTanProjection}). 124 125 If imageData is given as a list in the format [r, g, b], a color RGB plot will be made. However, 126 in this case the cutLevels must be specified manually for each component as a list - 127 i.e. cutLevels = [[r min, r max], [g min, g max], [b min, b max]]. In this case of course, the 128 colorMap will be ignored. All r, g, b image arrays must have the same dimensions. 129 130 Set axesLabels = None to make a plot without coordinate axes plotted. 131 132 The axes can be marked in either sexagesimal or decimal celestial coordinates. If RATickSteps 133 or decTickSteps are set to "auto", the appropriate axis scales will be determined automatically 134 from the size of the image array and associated WCS. The tick step sizes can be overidden. 135 If the coordinate axes are in sexagesimal format a dictionary in the format {'deg', 'unit'} is 136 needed (see L{RA_TICK_STEPS} and L{DEC_TICK_STEPS} for examples). If the coordinate axes are in 137 decimal format, the tick step size is specified simply in RA, dec decimal degrees. 138 139 @type imageData: numpy array or list 140 @param imageData: image data array or list of numpy arrays [r, g, b] 141 @type imageWCS: astWCS.WCS 142 @param imageWCS: astWCS.WCS object 143 @type axes: list 144 @param axes: specifies where in the current figure to draw the finder chart (see pylab.axes) 145 @type cutLevels: list 146 @param cutLevels: sets the image scaling - available options: 147 - pixel values: cutLevels=[low value, high value]. 148 - histogram equalisation: cutLevels=["histEq", number of bins ( e.g. 1024)] 149 - relative: cutLevels=["relative", cut per cent level (e.g. 99.5)] 150 - smart: cutLevels=["smart", cut per cent level (e.g. 99.5)] 151 ["smart", 99.5] seems to provide good scaling over a range of different images. 152 Note that for RGB images, cut levels must be specified manually i.e. as a list: 153 [[r min, rmax], [g min, g max], [b min, b max]] 154 @type colorMapName: string 155 @param colorMapName: name of a standard matplotlib colormap, e.g. "hot", "cool", "gray" 156 etc. (do "help(pylab.colormaps)" in the Python interpreter to see available options) 157 @type title: string 158 @param title: optional title for the plot 159 @type axesLabels: string 160 @param axesLabels: either "sexagesimal" (for H:M:S, D:M:S), "decimal" (for decimal degrees) 161 or None (for no coordinate axes labels) 162 @type axesFontFamily: string 163 @param axesFontFamily: matplotlib fontfamily, e.g. 'serif', 'sans-serif' etc. 164 @type axesFontSize: float 165 @param axesFontSize: font size of axes labels and titles (in points) 166 @type colorBar: bool 167 @param colorBar: if True, plot a vertical color bar at the side of the image indicating the intensity 168 scale. 169 @type interpolation: string 170 @param interpolation: interpolation to apply to the image plot (see the documentation for 171 the matplotlib.pylab.imshow command) 172 173 """ 174 175self.RADeg,self.decDeg=imageWCS.getCentreWCSCoords() 176self.wcs=imageWCS 177 178# Handle case where imageData is [r, g, b] 179iftype(imageData)==list: 180iflen(imageData)==3: 181iflen(cutLevels)==3: 182r=astImages.normalise(imageData[0],cutLevels[0]) 183g=astImages.normalise(imageData[1],cutLevels[1]) 184b=astImages.normalise(imageData[2],cutLevels[2]) 185rgb=numpy.array([r.transpose(),g.transpose(),b.transpose()]) 186rgb=rgb.transpose() 187self.data=rgb 188self.rgbImage=True 189else: 190raiseException("tried to create a RGB array, but cutLevels is not a list of 3 lists") 191 192else: 193raiseException("tried to create a RGB array but imageData is not a list of 3 arrays") 194else: 195self.data=imageData 196self.rgbImage=False 197 198self.axes=pylab.axes(axes) 199self.cutLevels=cutLevels 200self.colorMapName=colorMapName 201self.title=title 202self.axesLabels=axesLabels 203self.colorBar=colorBar 204self.axesFontSize=axesFontSize 205self.axesFontFamily=axesFontFamily 206 207self.flipXAxis=False 208self.flipYAxis=False 209 210self.interpolation=interpolation 211 212ifself.axesLabels!=None: 213 214# Allow user to override the automatic coord tick spacing 215ifself.axesLabels=="sexagesimal": 216ifRATickSteps!="auto": 217iftype(RATickSteps)!=dictor"deg"notinlist(RATickSteps.keys()) \ 218or"unit"notinlist(RATickSteps.keys()): 219raiseException("RATickSteps needs to be in format {'deg', 'unit'} for sexagesimal axes labels") 220ifdecTickSteps!="auto": 221iftype(decTickSteps)!=dictor"deg"notinlist(decTickSteps.keys()) \ 222or"unit"notinlist(decTickSteps.keys()): 223raiseException("decTickSteps needs to be in format {'deg', 'unit'} for sexagesimal axes labels") 224elifself.axesLabels=="decimal": 225ifRATickSteps!="auto": 226iftype(RATickSteps)!=float: 227raiseException("RATickSteps needs to be a float (if not 'auto') for decimal axes labels") 228ifdecTickSteps!="auto": 229iftype(decTickSteps)!=float: 230raiseException("decTickSteps needs to be a float (if not 'auto') for decimal axes labels") 231self.RATickSteps=RATickSteps 232self.decTickSteps=decTickSteps 233 234self.calcWCSAxisLabels(axesLabels=self.axesLabels) 235 236# this list stores objects to overplot, add to it using addPlotObjects() 237self.plotObjects=[] 238 239# this list stores image data to overlay as contours, add to it using addContourOverlay() 240self.contourOverlays=[] 241 242self.draw()
380"""Adds image data to the ImagePlot as a contour overlay. The contours can be removed using 381 L{removeContourOverlay}. If a contour overlay already exists with this tag, it will be replaced. 382 383 @type contourImageData: numpy array 384 @param contourImageData: image data array from which contours are to be generated 385 @type contourWCS: astWCS.WCS 386 @param contourWCS: astWCS.WCS object for the image to be contoured 387 @type tag: string 388 @param tag: identifying tag for this set of contours 389 @type levels: list 390 @param levels: sets the contour levels - available options: 391 - values: contourLevels=[list of values specifying each level] 392 - linear spacing: contourLevels=['linear', min level value, max level value, number 393 of levels] - can use "min", "max" to automatically set min, max levels from image data 394 - log spacing: contourLevels=['log', min level value, max level value, number of 395 levels] - can use "min", "max" to automatically set min, max levels from image data 396 @type width: int 397 @param width: width of the overlaid contours 398 @type color: string 399 @param color: color of the overlaid contours, specified by the name of a standard 400 matplotlib color, e.g., "black", "white", "cyan" 401 etc. (do "help(pylab.colors)" in the Python interpreter to see available options) 402 @type smooth: float 403 @param smooth: standard deviation (in arcsec) of Gaussian filter for 404 pre-smoothing of contour image data (set to 0 for no smoothing) 405 @type highAccuracy: bool 406 @param highAccuracy: if True, sample every corresponding pixel in each image; otherwise, sample 407 every nth pixel, where n = the ratio of the image scales. 408 409 """ 410 411ifself.rgbImage==True: 412backgroundData=self.data[:,:,0] 413else: 414backgroundData=self.data 415contourData=astImages.generateContourOverlay(backgroundData,self.wcs,contourImageData, \ 416contourWCS,levels,smooth,highAccuracy=highAccuracy) 417 418alreadyGot=False 419forcinself.contourOverlays: 420ifc['tag']==tag: 421c['contourData']=contourData 422c['tag']=tag 423c['color']=color 424c['width']=width 425alreadyGot=True 426 427ifalreadyGot==False: 428self.contourOverlays.append({'contourData':contourData,'tag':tag,'color':color, \ 429'width':width}) 430self.draw()
434"""Removes the contourOverlay from the ImagePlot corresponding to the tag. 435 436 @type tag: string 437 @param tag: tag for contour overlay in ImagePlot.contourOverlays to be removed 438 439 """ 440 441index=0 442forpinself.contourOverlays: 443ifp['tag']==tag: 444self.plotObjects.remove(self.plotObjects[index]) 445index=index+1 446self.draw()
451"""Add objects with RA, dec coords objRAs, objDecs to the ImagePlot. Only objects that fall within 452 the image boundaries will be plotted. 453 454 symbol specifies the type of symbol with which to mark the object in the image. The following 455 values are allowed: 456 - "circle" 457 - "box" 458 - "cross" 459 - "diamond" 460 461 size specifies the diameter in arcsec of the symbol (if plotSymbol == "circle"), or the width 462 of the box in arcsec (if plotSymbol == "box") 463 464 width specifies the thickness of the symbol lines in pixels 465 466 color can be any valid matplotlib color (e.g. "red", "green", etc.) 467 468 The objects can be removed from the plot by using removePlotObjects(), and then calling 469 draw(). If the ImagePlot already has a set of plotObjects with the same tag, they will be 470 replaced. 471 472 @type objRAs: numpy array or list 473 @param objRAs: object RA coords in decimal degrees 474 @type objDecs: numpy array or list 475 @param objDecs: corresponding object Dec. coords in decimal degrees 476 @type tag: string 477 @param tag: identifying tag for this set of objects 478 @type symbol: string 479 @param symbol: either "circle", "box", "cross", or "diamond" 480 @type size: float 481 @param size: size of symbols to plot (radius in arcsec, or width of box) 482 @type width: float 483 @param width: width of symbols in pixels 484 @type color: string 485 @param color: any valid matplotlib color string, e.g. "red", "green" etc. 486 @type objLabels: list 487 @param objLabels: text labels to plot next to objects in figure 488 @type objLabelSize: float 489 @param objLabelSize: size of font used for object labels (in points) 490 491 """ 492 493pixCoords=self.wcs.wcs2pix(objRAs,objDecs) 494 495xMax=self.data.shape[1] 496yMax=self.data.shape[0] 497 498ifobjLabels==None: 499objLabels=[None]*len(objRAs) 500 501xInPlot=[] 502yInPlot=[] 503RAInPlot=[] 504decInPlot=[] 505labelInPlot=[] 506forp,r,d,linzip(pixCoords,objRAs,objDecs,objLabels): 507ifp[0]>=0andp[0]<xMaxandp[1]>=0andp[1]<yMax: 508xInPlot.append(p[0]) 509yInPlot.append(p[1]) 510RAInPlot.append(r) 511decInPlot.append(d) 512labelInPlot.append(l) 513 514xInPlot=numpy.array(xInPlot) 515yInPlot=numpy.array(yInPlot) 516RAInPlot=numpy.array(RAInPlot) 517decInPlot=numpy.array(decInPlot) 518 519# Size of symbols in pixels in plot - converted from arcsec 520sizePix=(size/3600.0)/self.wcs.getPixelSizeDeg() 521 522alreadyGot=False 523forpinself.plotObjects: 524ifp['tag']==tag: 525p['x']=xInPlot 526p['y']=yInPlot 527p['RA']=RAInPlot 528p['dec']=decInPlot 529p['tag']=tag 530p['objLabels']=objLabels 531p['symbol']=symbol 532p['sizePix']=sizePix 533p['sizeArcSec']=size 534p['width']=width 535p['color']=color 536p['objLabelSize']=objLabelSize 537alreadyGot=True 538 539ifalreadyGot==False: 540self.plotObjects.append({'x':xInPlot,'y':yInPlot,'RA':RAInPlot,'dec':decInPlot, 541'tag':tag,'objLabels':labelInPlot,'symbol':symbol, 542'sizePix':sizePix,'width':width,'color':color, 543'objLabelSize':objLabelSize,'sizeArcSec':size}) 544self.draw()
548"""Removes the plotObjects from the ImagePlot corresponding to the tag. The plot must be redrawn 549 for the change to take effect. 550 551 @type tag: string 552 @param tag: tag for set of objects in ImagePlot.plotObjects to be removed 553 554 """ 555 556index=0 557forpinself.plotObjects: 558ifp['tag']==tag: 559self.plotObjects.remove(self.plotObjects[index]) 560index=index+1 561self.draw()
566"""Adds a compass to the ImagePlot at the given location ('N', 'NE', 'E', 'SE', 'S', 567 'SW', 'W', or 'NW'). Note these aren't directions on the WCS coordinate grid, they are 568 relative positions on the plot - so N is top centre, NE is top right, SW is bottom right etc.. 569 Alternatively, pixel coordinates (x, y) in the image can be given. 570 571 @type location: string or tuple 572 @param location: location in the plot where the compass is drawn: 573 - string: N, NE, E, SE, S, SW, W or NW 574 - tuple: (x, y) 575 @type sizeArcSec: float 576 @param sizeArcSec: length of the compass arrows on the plot in arc seconds 577 @type color: string 578 @param color: any valid matplotlib color string 579 @type fontSize: float 580 @param fontSize: size of font used to label N and E, in points 581 @type width: float 582 @param width: width of arrows used to mark compass 583 584 """ 585 586iftype(location)==str: 587cRADeg,cDecDeg=self.wcs.getCentreWCSCoords() 588RAMin,RAMax,decMin,decMax=self.wcs.getImageMinMaxWCSCoords() 589westPoint,eastPoint,southPoint,northPoint=astCoords.calcRADecSearchBox(cRADeg,cDecDeg,sizeArcSec/3600.0/2.0) 590sizeRADeg=eastPoint-westPoint 591sizeDecDeg=northPoint-southPoint 592xSizePix=(sizeArcSec/3600.0)/self.wcs.getXPixelSizeDeg() 593ySizePix=(sizeArcSec/3600.0)/self.wcs.getYPixelSizeDeg() 594X=self.data.shape[1] 595Y=self.data.shape[0] 596xBufferPix=0.5*xSizePix 597yBufferPix=0.5*ySizePix 598cx,cy=self.wcs.wcs2pix(cRADeg,cDecDeg) 599foundLocation=False 600x=cy 601y=cx 602ifself.wcs.isFlipped()==False: 603iflocation.find("N")!=-1: 604y=Y-2*yBufferPix 605foundLocation=True 606iflocation.find("S")!=-1: 607y=yBufferPix 608foundLocation=True 609iflocation.find("E")!=-1: 610x=xBufferPix*2 611foundLocation=True 612iflocation.find("W")!=-1: 613x=X-xBufferPix 614foundLocation=True 615else: 616iflocation.find("S")!=-1: 617y=Y-2*yBufferPix 618foundLocation=True 619iflocation.find("N")!=-1: 620y=yBufferPix 621foundLocation=True 622iflocation.find("W")!=-1: 623x=xBufferPix*2 624foundLocation=True 625iflocation.find("E")!=-1: 626x=X-xBufferPix 627foundLocation=True 628iffoundLocation==False: 629raiseException("didn't understand location string for scale bar (should be e.g. N, S, E, W).") 630RADeg,decDeg=self.wcs.pix2wcs(x,y) 631eliftype(location)==tupleortype(location)==list: 632x,y=location 633RADeg,decDeg=self.wcs.pix2wcs(x,y) 634else: 635raiseException("didn't understand location for scale bar - should be string or tuple.") 636 637alreadyGot=False 638forpinself.plotObjects: 639ifp['tag']=="compass": 640p['x']=[x] 641p['y']=[y] 642p['RA']=[RADeg] 643p['dec']=[decDeg] 644p['tag']="compass" 645p['objLabels']=[None] 646p['symbol']="compass" 647p['sizeArcSec']=sizeArcSec 648p['width']=width 649p['color']=color 650p['objLabelSize']=fontSize 651alreadyGot=True 652 653ifalreadyGot==False: 654self.plotObjects.append({'x':[x],'y':[y],'RA':[RADeg],'dec':[decDeg], 655'tag':"compass",'objLabels':[None],'symbol':"compass", 656'width':width,'color':color, 657'objLabelSize':fontSize,'sizeArcSec':sizeArcSec}) 658self.draw()
663"""Adds a scale bar to the ImagePlot at the given location ('N', 'NE', 'E', 'SE', 'S', 664 'SW', 'W', or 'NW'). Note these aren't directions on the WCS coordinate grid, they are 665 relative positions on the plot - so N is top centre, NE is top right, SW is bottom right etc.. 666 Alternatively, pixel coordinates (x, y) in the image can be given. 667 668 @type location: string or tuple 669 @param location: location in the plot where the compass is drawn: 670 - string: N, NE, E, SE, S, SW, W or NW 671 - tuple: (x, y) 672 @type sizeArcSec: float 673 @param sizeArcSec: scale length to indicate on the plot in arc seconds 674 @type color: string 675 @param color: any valid matplotlib color string 676 @type fontSize: float 677 @param fontSize: size of font used to label N and E, in points 678 @type width: float 679 @param width: width of arrow used to mark scale 680 681 """ 682 683# Work out where the scale bar is going in WCS coords from the relative location given 684iftype(location)==str: 685cRADeg,cDecDeg=self.wcs.getCentreWCSCoords() 686RAMin,RAMax,decMin,decMax=self.wcs.getImageMinMaxWCSCoords() 687westPoint,eastPoint,southPoint,northPoint=astCoords.calcRADecSearchBox(cRADeg,cDecDeg,sizeArcSec/3600.0/2.0) 688sizeRADeg=eastPoint-westPoint 689sizeDecDeg=northPoint-southPoint 690xSizePix=(sizeArcSec/3600.0)/self.wcs.getXPixelSizeDeg() 691ySizePix=(sizeArcSec/3600.0)/self.wcs.getYPixelSizeDeg() 692X=self.data.shape[1] 693Y=self.data.shape[0] 694xBufferPix=0.6*ySizePix 695yBufferPix=0.05*Y 696cx,cy=self.wcs.wcs2pix(cRADeg,cDecDeg) 697foundLocation=False 698x=cy 699y=cx 700ifself.wcs.isFlipped()==False: 701iflocation.find("N")!=-1: 702y=Y-1.5*yBufferPix 703foundLocation=True 704iflocation.find("S")!=-1: 705y=yBufferPix 706foundLocation=True 707iflocation.find("E")!=-1: 708x=xBufferPix 709foundLocation=True 710iflocation.find("W")!=-1: 711x=X-xBufferPix 712foundLocation=True 713else: 714iflocation.find("S")!=-1: 715y=Y-1.5*yBufferPix 716foundLocation=True 717iflocation.find("N")!=-1: 718y=yBufferPix 719foundLocation=True 720iflocation.find("W")!=-1: 721x=xBufferPix 722foundLocation=True 723iflocation.find("E")!=-1: 724x=X-xBufferPix 725foundLocation=True 726iffoundLocation==False: 727raiseException("didn't understand location string for scale bar (should be e.g. N, S, E, W).") 728RADeg,decDeg=self.wcs.pix2wcs(x,y) 729eliftype(location)==tupleortype(location)==list: 730x,y=location 731RADeg,decDeg=self.wcs.pix2wcs(x,y) 732else: 733raiseException("didn't understand location for scale bar - should be string or tuple.") 734 735alreadyGot=False 736forpinself.plotObjects: 737ifp['tag']=="scaleBar": 738p['x']=[x] 739p['y']=[y] 740p['RA']=[RADeg] 741p['dec']=[decDeg] 742p['tag']="scaleBar" 743p['objLabels']=[None] 744p['symbol']="scaleBar" 745p['sizeArcSec']=sizeArcSec 746p['width']=width 747p['color']=color 748p['objLabelSize']=fontSize 749alreadyGot=True 750 751ifalreadyGot==False: 752self.plotObjects.append({'x':[x],'y':[y],'RA':[RADeg],'dec':[decDeg], 753'tag':"scaleBar",'objLabels':[None],'symbol':"scaleBar", 754'width':width,'color':color, 755'objLabelSize':fontSize,'sizeArcSec':sizeArcSec}) 756self.draw()
974"""Saves the ImagePlot in any format that matplotlib can understand, as determined from the 975 fileName extension. 976 977 @type fileName: string 978 @param fileName: path where plot will be written 979 980 """ 981 982pylab.draw() 983pylab.savefig(fileName)
987"""Chooses the appropriate WCS coordinate tick steps for the plot based on its size. 988 Whether the ticks are decimal or sexagesimal is set by self.axesLabels. 989 990 Note: minor ticks not used at the moment. 991 992 @rtype: dictionary 993 @return: tick step sizes for major, minor plot ticks, in format {'major', 'minor'} 994 995 """ 996 997# Aim for 5 major tick marks on a plot 998xArray=numpy.arange(0,self.data.shape[1],1) 999yArray=numpy.arange(0,self.data.shape[0],1)1000xWCS=self.wcs.pix2wcs(xArray,numpy.zeros(xArray.shape[0],dtype=float))1001yWCS=self.wcs.pix2wcs(numpy.zeros(yArray.shape[0],dtype=float),yArray)1002xWCS=numpy.array(xWCS)1003yWCS=numpy.array(yWCS)1004ras=xWCS[:,0]1005decs=yWCS[:,1]1006RAEdges=numpy.array([ras[0],ras[-1]])1007RAMin=RAEdges.min()1008RAMax=RAEdges.max()1009decMin=decs.min()1010decMax=decs.max()10111012# Work out if wrapped around1013midRAPix,midDecPix=self.wcs.wcs2pix((RAEdges[1]+RAEdges[0])/2.0,(decMax+decMin)/2.0)1014ifmidRAPix<0ormidRAPix>self.wcs.header['NAXIS1']:1015wrappedRA=True1016else:1017wrappedRA=False1018ifwrappedRA==False:1019RAWidthDeg=RAMax-RAMin1020else:1021RAWidthDeg=(360.0-RAMax)+RAMin1022decHeightDeg=decMax-decMin10231024ticsDict={}1025ticsDict['major']={}1026ticsDict['minor']={}1027ifself.axesLabels=="sexagesimal":10281029matchIndex=01030foriinrange(len(RA_TICK_STEPS)):1031ifRAWidthDeg/2.5>RA_TICK_STEPS[i]['deg']:1032matchIndex=i10331034ticsDict['major']['RA']=RA_TICK_STEPS[matchIndex]1035ticsDict['minor']['RA']=RA_TICK_STEPS[matchIndex-1]10361037matchIndex=01038foriinrange(len(DEC_TICK_STEPS)):1039ifdecHeightDeg/2.5>DEC_TICK_STEPS[i]['deg']:1040matchIndex=i10411042ticsDict['major']['dec']=DEC_TICK_STEPS[matchIndex]1043ticsDict['minor']['dec']=DEC_TICK_STEPS[matchIndex-1]10441045returnticsDict10461047elifself.axesLabels=="decimal":10481049matchIndex=01050foriinrange(len(DECIMAL_TICK_STEPS)):1051ifRAWidthDeg/2.5>DECIMAL_TICK_STEPS[i]:1052matchIndex=i10531054ticsDict['major']['RA']=DECIMAL_TICK_STEPS[matchIndex]1055ticsDict['minor']['RA']=DECIMAL_TICK_STEPS[matchIndex-1]10561057matchIndex=01058foriinrange(len(DECIMAL_TICK_STEPS)):1059ifdecHeightDeg/2.5>DECIMAL_TICK_STEPS[i]:1060matchIndex=i10611062ticsDict['major']['dec']=DECIMAL_TICK_STEPS[matchIndex]1063ticsDict['minor']['dec']=DECIMAL_TICK_STEPS[matchIndex-1]10641065returnticsDict10661067else:1068raiseException("axesLabels must be either 'sexagesimal' or 'decimal'")
1"""module for performing statistical calculations. 2 3(c) 2007-2012 Matt Hilton 4 5(c) 2013-2014 Matt Hilton & Steven Boada 6 7U{http://astlib.sourceforge.net} 8 9This module (as you may notice) provides very few statistical routines. It does, however, provide 10biweight (robust) estimators of location and scale, as described in Beers et al. 1990 (AJ, 100, 1132), in addition to a robust least squares fitting routine that uses the biweight transform. 12 13Some routines may fail if they are passed lists with few items and encounter a `divide by zero' 14error. Where this occurs, the function will return None. An error message will be printed to the 15console when this happens if astStats.REPORT_ERRORS=True (the default). Testing if an 16astStats function returns None can be used to handle errors in scripts. 17 18For extensive statistics modules, the Python bindings for GNU R (U{http://rpy.sourceforge.net}), or 19SciPy (U{http://www.scipy.org}) are suggested. 20 21""" 22 23importmath 24importnumpy 25importsys 26 27REPORT_ERRORS=True 28 29#---------------------------------------------------------------------------------------------------
31"""Calculates the mean average of a list of numbers. 32 33 @type dataList: list or numpy array 34 @param dataList: input data, must be a one dimensional list 35 @rtype: float 36 @return: mean average 37 38 """ 39returnnumpy.mean(dataList)
43"""Calculates the weighted mean average of a two dimensional list (value, weight) of 44 numbers. 45 46 @type dataList: list 47 @param dataList: input data, must be a two dimensional list in format [value, weight] 48 @rtype: float 49 @return: weighted mean average 50 51 """ 52sum=0 53weightSum=0 54foritemindataList: 55sum=sum+float(item[0]*item[1]) 56weightSum=weightSum+item[1] 57iflen(dataList)>0: 58mean=sum/weightSum 59else: 60mean=0 61returnmean
65"""Calculates the (sample) standard deviation of a list of numbers. 66 67 @type dataList: list or numpy array 68 @param dataList: input data, must be a one dimensional list 69 @rtype: float 70 @return: standard deviation 71 72 """ 73returnnumpy.std(dataList)
77"""Calculates the root mean square of a list of numbers. 78 79 @type dataList: list 80 @param dataList: input data, must be a one dimensional list 81 @rtype: float 82 @return: root mean square 83 84 """ 85dataListSq=[] 86foritemindataList: 87dataListSq.append(item*item) 88listMeanSq=mean(dataListSq) 89rms=math.sqrt(listMeanSq) 90 91returnrms
95"""Calculates the weighted (sample) standard deviation of a list of numbers. 96 97 @type dataList: list 98 @param dataList: input data, must be a two dimensional list in format [value, weight] 99 @rtype: float100 @return: weighted standard deviation101102 @note: Returns None if an error occurs.103104 """105listMean=weightedMean(dataList)106sum=0107wSum=0108wNonZero=0109foritemindataList:110ifitem[1]>0.0:111sum=sum+float((item[0]-listMean)/item[1])*float((item[0]-listMean)/item[1])112wSum=wSum+float(1.0/item[1])*float(1.0/item[1])113114iflen(dataList)>1:115nFactor=float(len(dataList))/float(len(dataList)-1)116stdev=math.sqrt(nFactor*(sum/wSum))117else:118ifREPORT_ERRORS==True:119print("""ERROR: astStats.weightedStdev() : dataList contains < 2 items.""")120stdev=None121returnstdev
125"""Calculates the median of a list of numbers.126127 @type dataList: list or numpy array128 @param dataList: input data, must be a one dimensional list129 @rtype: float130 @return: median average131132 """133returnnumpy.median(dataList)
137"""Returns an estimate of the mode of a set of values by mode=(3*median)-(2*mean).138139 @type dataList: list140 @param dataList: input data, must be a one dimensional list141 @rtype: float142 @return: estimate of mode average143144 """145mode=(3*median(dataList))-(2*mean(dataList))146147returnmode
151"""Calculates the Median Absolute Deviation of a list of numbers.152153 @type dataList: list154 @param dataList: input data, must be a one dimensional list155 @rtype: float156 @return: median absolute deviation157158 """159listMedian=median(dataList)160161# Calculate |x-M| values162diffModuli=[]163foritemindataList:164diffModuli.append(math.fabs(item-listMedian))165166MAD=median(diffModuli)167168returnMAD
172"""Calculates the biweight location estimator (like a robust average) of a list of173 numbers.174175 @type dataList: list176 @param dataList: input data, must be a one dimensional list177 @type tuningConstant: float178 @param tuningConstant: 6.0 is recommended.179 @rtype: float180 @return: biweight location181182 @note: Returns None if an error occurs. 183184 """185C=tuningConstant186listMedian=median(dataList)187listMAD=MAD(dataList)188iflistMAD!=0:189uValues=[]190foritemindataList:191uValues.append((item-listMedian)/(C*listMAD))192193top=0# numerator equation (5) Beers et al if you like194bottom=0# denominator195foriinrange(len(uValues)):196ifmath.fabs(uValues[i])<=1.0:197top=top+((dataList[i]-listMedian) \ 198*(1.0-(uValues[i]*uValues[i])) \ 199*(1.0-(uValues[i]*uValues[i])))200201bottom=bottom+((1.0-(uValues[i]*uValues[i])) \ 202*(1.0-(uValues[i]*uValues[i])))203204CBI=listMedian+(top/bottom)205206else:207ifREPORT_ERRORS==True:208print("""ERROR: astStats: biweightLocation() : MAD() returned 0.""")209returnNone210211returnCBI
215"""Calculates the biweight scale estimator (like a robust standard deviation) of a list216 of numbers. 217218 @type dataList: list219 @param dataList: input data, must be a one dimensional list220 @type tuningConstant: float221 @param tuningConstant: 9.0 is recommended.222 @rtype: float223 @return: biweight scale224225 @note: Returns None if an error occurs.226227 """228C=tuningConstant229230# Calculate |x-M| values and u values231listMedian=median(dataList)232listMAD=MAD(dataList)233diffModuli=[]234foritemindataList:235diffModuli.append(math.fabs(item-listMedian))236uValues=[]237foritemindataList:238try:239uValues.append((item-listMedian)/(C*listMAD))240exceptZeroDivisionError:241ifREPORT_ERRORS==True:242print("""ERROR: astStats.biweightScale() : divide by zero error.""")243returnNone244245top=0# numerator equation (9) Beers et al246bottom=0247valCount=0# Count values where u<1 only248249foriinrange(len(uValues)):250# Skip u values >1251ifmath.fabs(uValues[i])<=1.0:252u2Term=1.0-(uValues[i]*uValues[i])253u4Term=math.pow(u2Term,4)254top=top+((diffModuli[i]*diffModuli[i])*u4Term)255bottom=bottom+(u2Term*(1.0-(5.0*(uValues[i]*uValues[i]))))256valCount=valCount+1257258top=math.sqrt(top)259bottom=math.fabs(bottom)260261SBI=math.pow(float(valCount),0.5)*(top/bottom)262returnSBI
266"""Iteratively calculates biweight location and scale, using sigma clipping, for a list267 of values. The calculation is performed on the first column of a multi-dimensional268 list; other columns are ignored.269270 @type dataList: list271 @param dataList: input data272 @type tuningConstant: float273 @param tuningConstant: 6.0 is recommended for location estimates, 9.0 is recommended for274 scale estimates 275 @type sigmaCut: float276 @param sigmaCut: sigma clipping to apply277 @rtype: dictionary 278 @return: estimate of biweight location, scale, and list of non-clipped data, in the format279 {'biweightLocation', 'biweightScale', 'dataList'}280281 @note: Returns None if an error occurs.282283 """284285iterations=0286clippedValues=[]287forrowindataList:288iftype(row)==list:289clippedValues.append(row[0])290else:291clippedValues.append(row)292293whileiterations<11andlen(clippedValues)>5:294295cbi=biweightLocation(clippedValues,tuningConstant)296sbi=biweightScale(clippedValues,tuningConstant)297298# check for either biweight routine falling over299# happens when feed in lots of similar numbers300# e.g. when bootstrapping with a small sample 301ifcbi==Noneorsbi==None:302303ifREPORT_ERRORS==True:304print("""ERROR: astStats : biweightClipped() :305 divide by zero error.""")306307returnNone308309else:310311clippedValues=[]312clippedData=[]313forrowindataList:314iftype(row)==list:315ifrow[0]>cbi-(sigmaCut*sbi) \ 316androw[0]<cbi+(sigmaCut*sbi):317clippedValues.append(row[0])318clippedData.append(row)319else:320ifrow>cbi-(sigmaCut*sbi) \ 321androw<cbi+(sigmaCut*sbi):322clippedValues.append(row)323clippedData.append(row)324325iterations=iterations+1326327return{'biweightLocation':cbi,'biweightScale':sbi,'dataList':clippedData}
331"""Calculates the biweight transform for a set of values. Useful for using as weights in332 robust line fitting.333334 @type dataList: list335 @param dataList: input data, must be a one dimensional list336 @type tuningConstant: float337 @param tuningConstant: 6.0 is recommended for location estimates, 9.0 is recommended for338 scale estimates 339 @rtype: list340 @return: list of biweights 341342 """343C=tuningConstant344345# Calculate |x-M| values and u values346listMedian=abs(median(dataList))347cutoff=C*listMedian348biweights=[]349foritemindataList:350ifabs(item)<cutoff:351biweights.append([item,352(1.0-((item/cutoff)*(item/cutoff))) \ 353*(1.0-((item/cutoff)*(item/cutoff)))])354else:355biweights.append([item,0.0])356357returnbiweights
361"""Performs an ordinary least squares fit on a two dimensional list of numbers.362 Minimum number of data points is 5.363364 @type dataList: list365 @param dataList: input data, must be a two dimensional list in format [x, y]366 @rtype: dictionary367 @return: slope and intercept on y-axis, with associated errors, in the format368 {'slope', 'intercept', 'slopeError', 'interceptError'}369370 @note: Returns None if an error occurs. 371372 """373sumX=0374sumY=0375sumXY=0376sumXX=0377n=float(len(dataList))378ifn>2:379foritemindataList:380sumX=sumX+item[0]381sumY=sumY+item[1]382sumXY=sumXY+(item[0]*item[1])383sumXX=sumXX+(item[0]*item[0])384m=((n*sumXY)-(sumX*sumY))/((n*sumXX)-(sumX*sumX))385c=((sumXX*sumY)-(sumX*sumXY))/((n*sumXX)-(sumX*sumX))386387sumRes=0388foritemindataList:389390sumRes=sumRes+((item[1]-(m*item[0])-c) \ 391*(item[1]-(m*item[0])-c))392393sigma=math.sqrt((1.0/(n-2))*sumRes)394395try:396mSigma=(sigma*math.sqrt(n))/math.sqrt((n*sumXX)-(sumX*sumX))397except:398mSigma=numpy.nan399try:400cSigma=(sigma*math.sqrt(sumXX))/math.sqrt((n*sumXX)-(sumX*sumX))401except:402cSigma=numpy.nan403else:404ifREPORT_ERRORS==True:405print("""ERROR: astStats.OLSFit() : dataList contains < 3 items.""")406407returnNone408409return{'slope':m,410'intercept':c,411'slopeError':mSigma,412'interceptError':cSigma}
416"""Calculates the clipped mean and stdev of a list of numbers.417418 @type dataList: list419 @param dataList: input data, one dimensional list of numbers420 @type sigmaCut: float421 @param sigmaCut: clipping in Gaussian sigma to apply422 @type maxIterations: int423 @param maxIterations: maximum number of iterations424 @rtype: dictionary425 @return: format {'clippedMean', 'clippedStdev', 'numPoints'}426427 """428429listCopy=[]430fordindataList:431listCopy.append(d)432listCopy=numpy.array(listCopy)433434iterations=0435whileiterations<maxIterationsandlen(listCopy)>4:436437m=listCopy.mean()438s=listCopy.std()439440listCopy=listCopy[numpy.less(abs(listCopy),abs(m+sigmaCut*s))]441442iterations=iterations+1443444return{'clippedMean':m,'clippedStdev':s,'numPoints':listCopy.shape[0]}
448"""Performs a weighted least squares fit on a list of numbers with sigma clipping. Minimum number of data449 points is 5.450451 @type dataList: list452 @param dataList: input data, must be a three dimensional list in format [x, y, y weight]453 @rtype: dictionary454 @return: slope and intercept on y-axis, with associated errors, in the format455 {'slope', 'intercept', 'slopeError', 'interceptError'}456457 @note: Returns None if an error occurs. 458459 """460461iterations=0462clippedValues=[]463forrowindataList:464clippedValues.append(row)465466whileiterations<11andlen(clippedValues)>4:467468fitResults=weightedLSFit(clippedValues,"errors")469470iffitResults['slope']==None:471472ifREPORT_ERRORS==True:473print("""ERROR: astStats : clippedWeightedLSFit() :474 divide by zero error.""")475476returnNone477478else:479480clippedValues=[]481forrowindataList:482483# Trim points more than sigmaCut*sigma away from the fitted line484fit=fitResults['slope']*row[0]+fitResults['intercept']485res=row[1]-fit486ifabs(res)/row[2]<sigmaCut:487clippedValues.append(row)488489iterations=iterations+1490491# store the number of values that made it through the clipping process492fitResults['numDataPoints']=len(clippedValues)493494returnfitResults
498"""Performs a weighted least squares fit on a three dimensional list of numbers [x, y, y error].499500 @type dataList: list501 @param dataList: input data, must be a three dimensional list in format [x, y, y error]502 @type weightType: string503 @param weightType: if "errors", weights are calculated assuming the input data is in the504 format [x, y, error on y]; if "weights", the weights are assumed to be already calculated and505 stored in a fourth column [x, y, error on y, weight] (as used by e.g. L{astStats.biweightLSFit})506 @rtype: dictionary507 @return: slope and intercept on y-axis, with associated errors, in the format508 {'slope', 'intercept', 'slopeError', 'interceptError'}509510 @note: Returns None if an error occurs. 511512 """513ifweightType=="weights":514sumW=0515sumWX=0516sumWY=0517sumWXY=0518sumWXX=0519n=float(len(dataList))520ifn>4:521foritemindataList:522W=item[3]523sumWX=sumWX+(W*item[0])524sumWY=sumWY+(W*item[1])525sumWXY=sumWXY+(W*item[0]*item[1])526sumWXX=sumWXX+(W*item[0]*item[0])527sumW=sumW+W528#print sumW, sumWXX, sumWX529530try:531m=((sumW*sumWXY)-(sumWX*sumWY)) \ 532/((sumW*sumWXX)-(sumWX*sumWX))533exceptZeroDivisionError:534ifREPORT_ERRORS==True:535print("ERROR: astStats.weightedLSFit() : divide by zero error.")536returnNone537538try:539c=((sumWXX*sumWY)-(sumWX*sumWXY)) \ 540/((sumW*sumWXX)-(sumWX*sumWX))541exceptZeroDivisionError:542ifREPORT_ERRORS==True:543print("ERROR: astStats.weightedLSFit() : divide by zero error.")544returnNone545546sumRes=0547foritemindataList:548549sumRes=sumRes+((item[1]-(m*item[0])-c) \ 550*(item[1]-(m*item[0])-c))551552sigma=math.sqrt((1.0/(n-2))*sumRes)553554# Can get div0 errors here so check555# When biweight fitting converges this shouldn't happen556if(n*sumWXX)-(sumWX*sumWX)>0.0:557558mSigma=(sigma*math.sqrt(n)) \ 559/math.sqrt((n*sumWXX)-(sumWX*sumWX))560561cSigma=(sigma*math.sqrt(sumWXX)) \ 562/math.sqrt((n*sumWXX)-(sumWX*sumWX))563564else:565566ifREPORT_ERRORS==True:567print("""ERROR: astStats.weightedLSFit()568 : divide by zero error.""")569returnNone570571else:572ifREPORT_ERRORS==True:573print("""ERROR: astStats.weightedLSFit() :574 dataList contains < 5 items.""")575returnNone576577elifweightType=="errors":578sumX=0579sumY=0580sumXY=0581sumXX=0582sumSigma=0583n=float(len(dataList))584foritemindataList:585sumX=sumX+(item[0]/(item[2]*item[2]))586sumY=sumY+(item[1]/(item[2]*item[2]))587sumXY=sumXY+((item[0]*item[1])/(item[2]*item[2]))588sumXX=sumXX+((item[0]*item[0])/(item[2]*item[2]))589sumSigma=sumSigma+(1.0/(item[2]*item[2]))590delta=(sumSigma*sumXX)-(sumX*sumX)591m=((sumSigma*sumXY)-(sumX*sumY))/delta592c=((sumXX*sumY)-(sumX*sumXY))/delta593mSigma=math.sqrt(sumSigma/delta)594cSigma=math.sqrt(sumXX/delta)595596return{'slope':m,597'intercept':c,598'slopeError':mSigma,599'interceptError':cSigma}
603"""Performs a weighted least squares fit, where the weights used are the biweight604 transforms of the residuals to the previous best fit .i.e. the procedure is iterative,605 and converges very quickly (iterations is set to 10 by default). Minimum number of data606 points is 10.607608 This seems to give slightly different results to the equivalent R routine, so use at your609 own risk!610611 @type dataList: list612 @param dataList: input data, must be a three dimensional list in format [x, y, y weight]613 @type tuningConstant: float614 @param tuningConstant: 6.0 is recommended for location estimates, 9.0 is recommended for615 scale estimates616 @type sigmaCut: float617 @param sigmaCut: sigma clipping to apply (set to None if not required) 618 @rtype: dictionary619 @return: slope and intercept on y-axis, with associated errors, in the format620 {'slope', 'intercept', 'slopeError', 'interceptError'}621622 @note: Returns None if an error occurs.623624 """625626dataCopy=[]627forrowindataList:628dataCopy.append(row)629630# First perform unweighted fit, then calculate residuals631results=OLSFit(dataCopy)632origLen=len(dataCopy)633forkinrange(10):634m=results['slope']635c=results['intercept']636res=[]637foritemindataCopy:638res.append((m*item[0]+c)-item[1])639640iflen(res)>5:641# For clipping, trim away things >3 sigma 642# away from median643ifsigmaCut!=None:644absRes=[]645foriteminres:646absRes.append(abs(item))647sigma=stdev(absRes)648count=0649foriteminabsRes:650ifitem>(sigmaCut*sigma) \ 651andlen(dataCopy)>2:652deldataCopy[count]653delres[count]654655# Index of datalist gets out of656# sync with absRes as we delete657# items658count=count-1659660count=count+1661662# Biweight transform residuals663weights=biweightTransform(res,tuningConstant)664665# Perform weighted fit, using biweight transforms 666# of residuals as weight667wData=[]668foriinrange(len(dataCopy)):669wData.append([dataCopy[i][0],dataCopy[i][1],dataCopy[i][2],weights[i][1]])670671results=weightedLSFit(wData,"weights")672673returnresults
677"""Bins the input data cumulatively.678679 @param data: input data, must be a one dimensional list680 @type binMin: float681 @param binMin: minimum value from which to bin data682 @type binMax: float683 @param binMax: maximum value from which to bin data 684 @type binTotal: int685 @param binTotal: number of bins 686 @rtype: list687 @return: binned data, in format [bin centre, frequency]688689 """690#Bin data691binStep=float(binMax-binMin)/binTotal692bins=[]693totalItems=len(data)694foriinrange(binTotal):695bins.append(0)696foritemindata:697ifitem>(binMin+(i*binStep)):698bins[i]=bins[i]+1.0/totalItems699700# Gnuplot requires points at bin midpoints701coords=[]702foriinrange(binTotal):703coords.append([binMin+(float(i+0.5)*binStep),bins[i]])704705returncoords
709"""Bins the input data..710711 @param data: input data, must be a one dimensional list712 @type binMin: float713 @param binMin: minimum value from which to bin data714 @type binMax: float715 @param binMax: maximum value from which to bin data 716 @type binTotal: int717 @param binTotal: number of bins 718 @rtype: list719 @return: binned data, in format [bin centre, frequency]720721 """722#Bin data723binStep=float(binMax-binMin)/binTotal724bins=[]725foriinrange(binTotal):726bins.append(0)727foritemindata:728ifitem>(binMin+(i*binStep)) \ 729anditem<=(binMin+((i+1)*binStep)):730bins[i]=bins[i]+1731732# Gnuplot requires points at bin midpoints733coords=[]734foriinrange(binTotal):735coords.append([binMin+(float(i+0.5)*binStep),bins[i]])736737returncoords
741"""Bins the input data, recorded frequency is sum of weights in bin.742743 @param data: input data, must be a one dimensional list744 @type binMin: float745 @param binMin: minimum value from which to bin data746 @type binMax: float747 @param binMax: maximum value from which to bin data 748 @type binTotal: int749 @param binTotal: number of bins 750 @rtype: list751 @return: binned data, in format [bin centre, frequency]752753 """754#Bin data755binStep=float(binMax-binMin)/binTotal756bins=[]757foriinrange(binTotal):758bins.append(0.0)759foritem,weightinzip(data,weights):760ifitem>(binMin+(i*binStep)) \ 761anditem<=(binMin+((i+1)*binStep)):762bins[i]=bins[i]+weight763764# Gnuplot requires points at bin midpoints765coords=[]766foriinrange(binTotal):767coords.append([binMin+(float(i+0.5)*binStep),bins[i]])768769returncoords
83"""Converts decimal degrees to string in Hours:Minutes:Seconds format with 84 user specified delimiter. 85 86 @type RADeg: float 87 @param RADeg: coordinate in decimal degrees 88 @type delimiter: string 89 @param delimiter: delimiter character in returned string 90 @rtype: string 91 @return: coordinate string in H:M:S format 92 93 """ 94hours=(RADeg/360.0)*24 95#if hours < 10 and hours >= 1: 96if1<=hours<10: 97sHours="0"+str(hours)[0] 98elifhours>=10: 99sHours=str(hours)[:2]100elifhours<1:101sHours="00"102103ifstr(hours).find(".")==-1:104mins=float(hours)*60.0105else:106mins=float(str(hours)[str(hours).index("."):])*60.0107#if mins<10 and mins>=1:108if1<=mins<10:109sMins="0"+str(mins)[:1]110elifmins>=10:111sMins=str(mins)[:2]112elifmins<1:113sMins="00"114115secs=(hours-(float(sHours)+float(sMins)/60.0))*3600.0116#if secs < 10 and secs>0.001:117if0.001<secs<10:118sSecs="0"+str(secs)[:str(secs).find(".")+4]119elifsecs<0.0001:120sSecs="00.001"121else:122sSecs=str(secs)[:str(secs).find(".")+4]123iflen(sSecs)<5:124sSecs=sSecs+"00"# So all to 3dp125126iffloat(sSecs)==60.000:127sSecs="00.00"128sMins=str(int(sMins)+1)129ifint(sMins)==60:130sMins="00"131sDeg=str(int(sDeg)+1)132133returnsHours+delimiter+sMins+delimiter+sSecs
137"""Converts decimal degrees to string in Degrees:Minutes:Seconds format138 with user specified delimiter.139140 @type decDeg: float141 @param decDeg: coordinate in decimal degrees142 @type delimiter: string143 @param delimiter: delimiter character in returned string144 @rtype: string145 @return: coordinate string in D:M:S format146147 """148# Positive149ifdecDeg>0:150#if decDeg < 10 and decDeg>=1:151if1<=decDeg<10:152sDeg="0"+str(decDeg)[0]153elifdecDeg>=10:154sDeg=str(decDeg)[:2]155elifdecDeg<1:156sDeg="00"157158ifstr(decDeg).find(".")==-1:159mins=float(decDeg)*60.0160else:161mins=float(str(decDeg)[str(decDeg).index("."):])*60162#if mins<10 and mins>=1:163if1<=mins<10:164sMins="0"+str(mins)[:1]165elifmins>=10:166sMins=str(mins)[:2]167elifmins<1:168sMins="00"169170secs=(decDeg-(float(sDeg)+float(sMins)/60.0))*3600.0171#if secs<10 and secs>0:172if0<secs<10:173sSecs="0"+str(secs)[:str(secs).find(".")+3]174elifsecs<0.001:175sSecs="00.00"176else:177sSecs=str(secs)[:str(secs).find(".")+3]178iflen(sSecs)<5:179sSecs=sSecs+"0"# So all to 2dp180181iffloat(sSecs)==60.00:182sSecs="00.00"183sMins=str(int(sMins)+1)184ifint(sMins)==60:185sMins="00"186sDeg=str(int(sDeg)+1)187188return"+"+sDeg+delimiter+sMins+delimiter+sSecs189190else:191#if decDeg>-10 and decDeg<=-1:192if-10<decDeg<=-1:193sDeg="-0"+str(decDeg)[1]194elifdecDeg<=-10:195sDeg=str(decDeg)[:3]196elifdecDeg>-1:197sDeg="-00"198199ifstr(decDeg).find(".")==-1:200mins=float(decDeg)*-60.0201else:202mins=float(str(decDeg)[str(decDeg).index("."):])*60203#if mins<10 and mins>=1:204if1<=mins<10:205sMins="0"+str(mins)[:1]206elifmins>=10:207sMins=str(mins)[:2]208elifmins<1:209sMins="00"210211secs=(decDeg-(float(sDeg)-float(sMins)/60.0))*3600.0212#if secs>-10 and secs<0:213# so don't get minus sign214if-10<secs<0:215sSecs="0"+str(secs)[1:str(secs).find(".")+3]216elifsecs>-0.001:217sSecs="00.00"218else:219sSecs=str(secs)[1:str(secs).find(".")+3]220iflen(sSecs)<5:221sSecs=sSecs+"0"# So all to 2dp222223iffloat(sSecs)==60.00:224sSecs="00.00"225sMins=str(int(sMins)+1)226ifint(sMins)==60:227sMins="00"228sDeg=str(int(sDeg)-1)229230returnsDeg+delimiter+sMins+delimiter+sSecs
234"""Calculates the angular separation of two positions on the sky (specified235 in decimal degrees) in decimal degrees, assuming a tangent plane projection236 (so separation has to be <90 deg). Note that RADeg2, decDeg2 can be numpy237 arrays.238239 @type RADeg1: float240 @param RADeg1: R.A. in decimal degrees for position 1241 @type decDeg1: float242 @param decDeg1: dec. in decimal degrees for position 1243 @type RADeg2: float or numpy array244 @param RADeg2: R.A. in decimal degrees for position 2245 @type decDeg2: float or numpy array246 @param decDeg2: dec. in decimal degrees for position 2247 @rtype: float or numpy array, depending upon type of RADeg2, decDeg2248 @return: angular separation in decimal degrees249250 """251cRA=numpy.radians(RADeg1)252cDec=numpy.radians(decDeg1)253254gRA=numpy.radians(RADeg2)255gDec=numpy.radians(decDeg2)256257dRA=cRA-gRA258dDec=gDec-cDec259cosC=((numpy.sin(gDec)*numpy.sin(cDec))+260(numpy.cos(gDec)*numpy.cos(cDec)*numpy.cos(gRA-cRA)))261x=(numpy.cos(cDec)*numpy.sin(gRA-cRA))/cosC262y=(((numpy.cos(gDec)*numpy.sin(cDec))-(numpy.sin(gDec)*263numpy.cos(cDec)*numpy.cos(gRA-cRA)))/cosC)264r=numpy.degrees(numpy.sqrt(x*x+y*y))265266returnr
270"""Computes new right ascension and declination shifted from the original271 by some delta RA and delta DEC. Input position is decimal degrees. Shifts272 (deltaRA, deltaDec) are arcseconds, and output is decimal degrees. Based on273 an IDL routine of the same name.274275 @param ra1: float276 @type ra1: R.A. in decimal degrees277 @param dec1: float278 @type dec1: dec. in decimal degrees279 @param deltaRA: float280 @type deltaRA: shift in R.A. in arcseconds281 @param deltaDec: float282 @type deltaDec: shift in dec. in arcseconds283 @rtype: float [newRA, newDec]284 @return: shifted R.A. and dec.285286 """287288d2r=math.pi/180.289as2r=math.pi/648000.290291# Convert everything to radians292rara1=ra1*d2r293dcrad1=dec1*d2r294shiftRArad=deltaRA*as2r295shiftDCrad=deltaDec*as2r296297# Shift!298deldec2=0.0299sindis=math.sin(shiftRArad/2.0)300sindelRA=sindis/math.cos(dcrad1)301delra=2.0*math.asin(sindelRA)/d2r302303# Make changes304ra2=ra1+delra305dec2=dec1+deltaDec/3600.0306307returnra2,dec2
311"""Converts specified coordinates (given in decimal degrees) between J2000,312 B1950, and Galactic.313314 @type inputSystem: string315 @param inputSystem: system of the input coordinates (either "J2000",316 "B1950" or "GALACTIC")317 @type outputSystem: string318 @param outputSystem: system of the returned coordinates (either "J2000",319 "B1950" or "GALACTIC")320 @type coordX: float321 @param coordX: longitude coordinate in decimal degrees, e.g. R. A.322 @type coordY: float323 @param coordY: latitude coordinate in decimal degrees, e.g. dec.324 @type epoch: float325 @param epoch: epoch of the input coordinates326 @rtype: list327 @return: coordinates in decimal degrees in requested output system328329 """330331ifinputSystem=="J2000"orinputSystem=="B1950"orinputSystem=="GALACTIC":332ifoutputSystem=="J2000"oroutputSystem=="B1950"or \ 333outputSystem=="GALACTIC":334335outCoords=wcscon.wcscon(wcscon.wcscsys(inputSystem),336wcscon.wcscsys(outputSystem),0,0,coordX,coordY,epoch)337338returnoutCoords339340raiseException("inputSystem and outputSystem must be 'J2000', 'B1950'"341"or 'GALACTIC'")
345"""Calculates minimum and maximum RA, dec coords needed to define a box346 enclosing a circle of radius radiusSkyDeg around the given RADeg, decDeg347 coordinates. Useful for freeform queries of e.g. SDSS, UKIDSS etc.. Uses348 L{calcAngSepDeg}, so has the same limitations.349350 @type RADeg: float351 @param RADeg: RA coordinate of centre of search region352 @type decDeg: float353 @param decDeg: dec coordinate of centre of search region354 @type radiusSkyDeg: float355 @param radiusSkyDeg: radius in degrees on the sky used to define search356 region357 @rtype: list358 @return: [RAMin, RAMax, decMin, decMax] - coordinates in decimal degrees359 defining search box360361 """362363tolerance=1e-5# in degrees on sky364targetHalfSizeSkyDeg=radiusSkyDeg365funcCalls=["calcAngSepDeg(RADeg, decDeg, guess, decDeg)",366"calcAngSepDeg(RADeg, decDeg, guess, decDeg)",367"calcAngSepDeg(RADeg, decDeg, RADeg, guess)",368"calcAngSepDeg(RADeg, decDeg, RADeg, guess)"]369coords=[RADeg,RADeg,decDeg,decDeg]370signs=[1.0,-1.0,1.0,-1.0]371results=[]372forf,c,signinzip(funcCalls,coords,signs):373# Initial guess range374maxGuess=sign*targetHalfSizeSkyDeg*10.0375minGuess=sign*targetHalfSizeSkyDeg/10.0376guessStep=(maxGuess-minGuess)/10.0377guesses=numpy.arange(minGuess+c,maxGuess+c,guessStep)378foriinrange(50):379minSizeDiff=1e6380bestGuess=None381forguessinguesses:382sizeDiff=abs(eval(f)-targetHalfSizeSkyDeg)383ifsizeDiff<minSizeDiff:384minSizeDiff=sizeDiff385bestGuess=guess386ifminSizeDiff<tolerance:387break388else:389guessRange=abs((maxGuess-minGuess))390maxGuess=bestGuess+guessRange/4.0391minGuess=bestGuess-guessRange/4.0392guessStep=(maxGuess-minGuess)/10.0393guesses=numpy.arange(minGuess,maxGuess,guessStep)394results.append(bestGuess)395396RAMax=results[0]397RAMin=results[1]398decMax=results[2]399decMin=results[3]400401return[RAMin,RAMax,decMin,decMax]
astLib-0.8.0/docs/astLib/index.html 0000644 0001750 0001750 00000001114 12375434532 017423 0 ustar matty matty 0000000 0000000
API Documentation
astLib-0.8.0/docs/astLib/epydoc.js 0000644 0001750 0001750 00000024525 12375434532 017262 0 ustar matty matty 0000000 0000000 function toggle_private() {
// Search for any private/public links on this page. Store
// their old text in "cmd," so we will know what action to
// take; and change their text to the opposite action.
var cmd = "?";
var elts = document.getElementsByTagName("a");
for(var i=0; i";
s += " ";
for (var i=0; i... ";
elt.innerHTML = s;
}
}
function toggle(id) {
elt = document.getElementById(id+"-toggle");
if (elt.innerHTML == "-")
collapse(id);
else
expand(id);
return false;
}
function highlight(id) {
var elt = document.getElementById(id+"-def");
if (elt) elt.className = "py-highlight-hdr";
var elt = document.getElementById(id+"-expanded");
if (elt) elt.className = "py-highlight";
var elt = document.getElementById(id+"-collapsed");
if (elt) elt.className = "py-highlight";
}
function num_lines(s) {
var n = 1;
var pos = s.indexOf("\n");
while ( pos > 0) {
n += 1;
pos = s.indexOf("\n", pos+1);
}
return n;
}
// Collapse all blocks that mave more than `min_lines` lines.
function collapse_all(min_lines) {
var elts = document.getElementsByTagName("div");
for (var i=0; i 0)
if (elt.id.substring(split, elt.id.length) == "-expanded")
if (num_lines(elt.innerHTML) > min_lines)
collapse(elt.id.substring(0, split));
}
}
function expandto(href) {
var start = href.indexOf("#")+1;
if (start != 0 && start != href.length) {
if (href.substring(start, href.length) != "-") {
collapse_all(4);
pos = href.indexOf(".", start);
while (pos != -1) {
var id = href.substring(start, pos);
expand(id);
pos = href.indexOf(".", pos+1);
}
var id = href.substring(start, href.length);
expand(id);
highlight(id);
}
}
}
function kill_doclink(id) {
var parent = document.getElementById(id);
parent.removeChild(parent.childNodes.item(0));
}
function auto_kill_doclink(ev) {
if (!ev) var ev = window.event;
if (!this.contains(ev.toElement)) {
var parent = document.getElementById(this.parentID);
parent.removeChild(parent.childNodes.item(0));
}
}
function doclink(id, name, targets_id) {
var elt = document.getElementById(id);
// If we already opened the box, then destroy it.
// (This case should never occur, but leave it in just in case.)
if (elt.childNodes.length > 1) {
elt.removeChild(elt.childNodes.item(0));
}
else {
// The outer box: relative + inline positioning.
var box1 = document.createElement("div");
box1.style.position = "relative";
box1.style.display = "inline";
box1.style.top = 0;
box1.style.left = 0;
// A shadow for fun
var shadow = document.createElement("div");
shadow.style.position = "absolute";
shadow.style.left = "-1.3em";
shadow.style.top = "-1.3em";
shadow.style.background = "#404040";
// The inner box: absolute positioning.
var box2 = document.createElement("div");
box2.style.position = "relative";
box2.style.border = "1px solid #a0a0a0";
box2.style.left = "-.2em";
box2.style.top = "-.2em";
box2.style.background = "white";
box2.style.padding = ".3em .4em .3em .4em";
box2.style.fontStyle = "normal";
box2.onmouseout=auto_kill_doclink;
box2.parentID = id;
// Get the targets
var targets_elt = document.getElementById(targets_id);
var targets = targets_elt.getAttribute("targets");
var links = "";
target_list = targets.split(",");
for (var i=0; i" +
target[0] + "";
}
// Put it all together.
elt.insertBefore(box1, elt.childNodes.item(0));
//box1.appendChild(box2);
box1.appendChild(shadow);
shadow.appendChild(box2);
box2.innerHTML =
"Which "+name+" do you want to see documentation for?" +
"
";
}
return false;
}
function get_anchor() {
var href = location.href;
var start = href.indexOf("#")+1;
if ((start != 0) && (start != href.length))
return href.substring(start, href.length);
}
function redirect_url(dottedName) {
// Scan through each element of the "pages" list, and check
// if "name" matches with any of them.
for (var i=0; i-m" or "-c";
// extract the portion & compare it to dottedName.
var pagename = pages[i].substring(0, pages[i].length-2);
if (pagename == dottedName.substring(0,pagename.length)) {
// We've found a page that matches `dottedName`;
// construct its URL, using leftover `dottedName`
// content to form an anchor.
var pagetype = pages[i].charAt(pages[i].length-1);
var url = pagename + ((pagetype=="m")?"-module.html":
"-class.html");
if (dottedName.length > pagename.length)
url += "#" + dottedName.substring(pagename.length+1,
dottedName.length);
return url;
}
}
}
astLib-0.8.0/docs/astLib/astLib.astSED.TopHatPassband-class.html 0000664 0001750 0001750 00000020617 12375434532 024644 0 ustar matty matty 0000000 0000000
astLib.astSED.TopHatPassband
Some routines in this module will fail if, e.g., asked to clip a
section from a .fits image at a position not found within the image (as
determined using the WCS). Where this occurs, the function will return
None. An error message will be printed to the console when this happens
if astImages.REPORT_ERRORS=True (the default). Testing if an astImages
function returns None can be used to handle errors in scripts.
clipImageSectionWCS(imageData,
imageWCS,
RADeg,
decDeg,
clipSizeDeg,
returnWCS=True)
Clips a square or rectangular section from an image array at the
given celestial coordinates.
clipRotatedImageSectionWCS(imageData,
imageWCS,
RADeg,
decDeg,
clipSizeDeg,
returnWCS=True)
Clips a square or rectangular section from an image array at the
given celestial coordinates.
clipUsingRADecCoords(imageData,
imageWCS,
RAMin,
RAMax,
decMin,
decMax,
returnWCS=True)
Clips a section from an image array at the pixel coordinates
corresponding to the given celestial coordinates.
resampleToWCS(im1Data,
im1WCS,
im2Data,
im2WCS,
highAccuracy=False,
onlyOverlapping=True)
Resamples data corresponding to second image (with data im2Data, WCS
im2WCS) onto the WCS of the first image (im1Data, im1WCS).
generateContourOverlay(backgroundImageData,
backgroundImageWCS,
contourImageData,
contourImageWCS,
contourLevels,
contourSmoothFactor=0,
highAccuracy=False)
Rescales an image array to be used as a contour overlay to have the
same dimensions as the background image, and generates a set of
contour levels.
saveBitmap(outputFileName,
imageData,
cutLevels,
size,
colorMapName)
Makes a bitmap image from an image array; the image format is
specified by the filename extension.
saveContourOverlayBitmap(outputFileName,
backgroundImageData,
backgroundImageWCS,
cutLevels,
size,
colorMapName,
contourImageData,
contourImageWCS,
contourSmoothFactor,
contourLevels,
contourColor,
contourWidth)
Makes a bitmap image from an image array, with a set of contours
generated from a second image array overlaid.
Clips a square or rectangular section from an image array at the given
celestial coordinates. An updated WCS for the clipped section is
optionally returned, as well as the x, y pixel coordinates in the
original image corresponding to the clipped section.
Note that the clip size is specified in degrees on the sky. For
projections that have varying real pixel scale across the map (e.g. CEA),
use clipUsingRADecCoords instead.
Parameters:
imageData (numpy array) - image data array
imageWCS (astWCS.WCS) - astWCS.WCS object
RADeg (float) - coordinate in decimal degrees
decDeg (float) - coordinate in decimal degrees
clipSizeDeg (float or list in format [widthDeg, heightDeg]) - if float, size of square clipped section in decimal degrees; if
list, size of clipped section in degrees in x, y axes of image
respectively
returnWCS (bool) - if True, return an updated WCS for the clipped section
Returns: dictionary
clipped image section (numpy array), updated astWCS WCS object
for clipped image section, and coordinates of clipped section in
imageData in format {'data', 'wcs', 'clippedSection'}.
Clips a square or rectangular section from an image array at the given
pixel coordinates.
Parameters:
imageData (numpy array) - image data array
XCoord (float) - coordinate in pixels
YCoord (float) - coordinate in pixels
clipSizePix (float or list in format [widthPix, heightPix]) - if float, size of square clipped section in pixels; if list, size
of clipped section in pixels in x, y axes of output image
respectively
Clips a square or rectangular section from an image array at the given
celestial coordinates. The resulting clip is rotated and/or flipped such
that North is at the top, and East appears at the left. An updated WCS
for the clipped section is also returned. Note that the alignment of the
rotated WCS is currently not perfect - however, it is probably good
enough in most cases for use with ImagePlot
for plotting purposes.
Note that the clip size is specified in degrees on the sky. For
projections that have varying real pixel scale across the map (e.g. CEA),
use clipUsingRADecCoords instead.
Parameters:
imageData (numpy array) - image data array
imageWCS (astWCS.WCS) - astWCS.WCS object
RADeg (float) - coordinate in decimal degrees
decDeg (float) - coordinate in decimal degrees
clipSizeDeg (float) - if float, size of square clipped section in decimal degrees; if
list, size of clipped section in degrees in RA, dec. axes of
output rotated image respectively
returnWCS (bool) - if True, return an updated WCS for the clipped section
Returns: dictionary
clipped image section (numpy array), updated astWCS WCS object
for clipped image section, in format {'data', 'wcs'}.
Note:
Returns 'None' if the requested position is not found within the
image. If the image WCS does not have keywords of the form CD1_1
etc., the output WCS will not be rotated.
Clips a section from an image array at the pixel coordinates
corresponding to the given celestial coordinates.
Parameters:
imageData (numpy array) - image data array
imageWCS (astWCS.WCS) - astWCS.WCS object
RAMin (float) - minimum RA coordinate in decimal degrees
RAMax (float) - maximum RA coordinate in decimal degrees
decMin (float) - minimum dec coordinate in decimal degrees
decMax (float) - maximum dec coordinate in decimal degrees
returnWCS (bool) - if True, return an updated WCS for the clipped section
Returns: dictionary
clipped image section (numpy array), updated astWCS WCS object
for clipped image section, and corresponding pixel coordinates in
imageData in format {'data', 'wcs', 'clippedSection'}.
Note:
Returns 'None' if the requested position is not found within the
image.
Creates a matplotlib.pylab plot of an image array with the specified
cuts in intensity applied. This routine is used by saveBitmap and saveContourOverlayBitmap, which both produce output as
.png, .jpg, etc. images.
Parameters:
imageData (numpy array) - image data array
cutLevels (list) - sets the image scaling - available options:
pixel values: cutLevels=[low value, high value].
histogram equalisation: cutLevels=["histEq", number
of bins ( e.g. 1024)]
relative: cutLevels=["relative", cut per cent level
(e.g. 99.5)]
smart: cutLevels=["smart", cut per cent level (e.g.
99.5)]
["smart", 99.5] seems to provide good scaling over a
range of different images.
Returns: dictionary
image section (numpy.array), matplotlib image normalisation
(matplotlib.colors.Normalize), in the format {'image', 'norm'}.
Note:
If cutLevels[0] == "histEq", then only {'image'} is
returned.
Resamples data corresponding to second image (with data im2Data, WCS
im2WCS) onto the WCS of the first image (im1Data, im1WCS). The output,
resampled image is of the pixel same dimensions of the first image. This
routine is for assisting in plotting - performing photometry on the
output is not recommended.
Set highAccuracy == True to sample every corresponding pixel in each
image; otherwise only every nth pixel (where n is the ratio of the image
scales) will be sampled, with values in between being set using a linear
interpolation (much faster).
Set onlyOverlapping == True to speed up resampling by only resampling
the overlapping area defined by both image WCSs.
Parameters:
im1Data (numpy array) - image data array for first image
im1WCS (astWCS.WCS) - astWCS.WCS object corresponding to im1Data
im2Data (numpy array) - image data array for second image (to be resampled to match first
image)
im2WCS (astWCS.WCS) - astWCS.WCS object corresponding to im2Data
highAccuracy (bool) - if True, sample every corresponding pixel in each image;
otherwise, sample every nth pixel, where n = the ratio of the
image scales.
onlyOverlapping (bool) - if True, only consider the overlapping area defined by both image
WCSs (speeds things up)
Returns: dictionary
numpy image data array and associated WCS in format {'data',
'wcs'}
Rescales an image array to be used as a contour overlay to have the
same dimensions as the background image, and generates a set of contour
levels. The image array from which the contours are to be generated will
be resampled to the same dimensions as the background image data, and can
be optionally smoothed using a Gaussian filter. The sigma of the Gaussian
filter (contourSmoothFactor) is specified in arcsec.
Parameters:
backgroundImageData (numpy array) - background image data array
backgroundImageWCS (astWCS.WCS) - astWCS.WCS object of the background image data array
contourImageData (numpy array) - image data array from which contours are to be generated
contourImageWCS (astWCS.WCS) - astWCS.WCS object corresponding to contourImageData
contourLevels (list) - sets the contour levels - available options:
values: contourLevels=[list of values specifying each level]
linear spacing: contourLevels=['linear', min level value, max
level value, number of levels] - can use "min",
"max" to automatically set min, max levels from
image data
log spacing: contourLevels=['log', min level value, max level
value, number of levels] - can use "min",
"max" to automatically set min, max levels from
image data
contourSmoothFactor (float) - standard deviation (in arcsec) of Gaussian filter for
pre-smoothing of contour image data (set to 0 for no smoothing)
highAccuracy (bool) - if True, sample every corresponding pixel in each image;
otherwise, sample every nth pixel, where n = the ratio of the
image scales.
Makes a bitmap image from an image array; the image format is
specified by the filename extension. (e.g. ".jpg" =JPEG,
".png"=PNG).
Parameters:
outputFileName (string) - filename of output bitmap image
imageData (numpy array) - image data array
cutLevels (list) - sets the image scaling - available options:
pixel values: cutLevels=[low value, high value].
histogram equalisation: cutLevels=["histEq", number
of bins ( e.g. 1024)]
relative: cutLevels=["relative", cut per cent level
(e.g. 99.5)]
smart: cutLevels=["smart", cut per cent level (e.g.
99.5)]
["smart", 99.5] seems to provide good scaling over a
range of different images.
size (int) - size of output image in pixels
colorMapName (string) - name of a standard matplotlib colormap, e.g. "hot",
"cool", "gray" etc. (do
"help(pylab.colormaps)" in the Python interpreter to
see available options)
Makes a bitmap image from an image array, with a set of contours
generated from a second image array overlaid. The image format is
specified by the file extension (e.g. ".jpg"=JPEG,
".png"=PNG). The image array from which the contours are to be
generated can optionally be pre-smoothed using a Gaussian filter.
Parameters:
outputFileName (string) - filename of output bitmap image
backgroundImageData (numpy array) - background image data array
backgroundImageWCS (astWCS.WCS) - astWCS.WCS object of the background image data array
cutLevels (list) - sets the image scaling - available options:
pixel values: cutLevels=[low value, high value].
histogram equalisation: cutLevels=["histEq", number
of bins ( e.g. 1024)]
relative: cutLevels=["relative", cut per cent level
(e.g. 99.5)]
smart: cutLevels=["smart", cut per cent level (e.g.
99.5)]
["smart", 99.5] seems to provide good scaling over a
range of different images.
size (int) - size of output image in pixels
colorMapName (string) - name of a standard matplotlib colormap, e.g. "hot",
"cool", "gray" etc. (do
"help(pylab.colormaps)" in the Python interpreter to
see available options)
contourImageData (numpy array) - image data array from which contours are to be generated
contourImageWCS (astWCS.WCS) - astWCS.WCS object corresponding to contourImageData
contourSmoothFactor (float) - standard deviation (in pixels) of Gaussian filter for
pre-smoothing of contour image data (set to 0 for no smoothing)
contourLevels (list) - sets the contour levels - available options:
values: contourLevels=[list of values specifying each level]
linear spacing: contourLevels=['linear', min level value, max
level value, number of levels] - can use "min",
"max" to automatically set min, max levels from
image data
log spacing: contourLevels=['log', min level value, max level
value, number of levels] - can use "min",
"max" to automatically set min, max levels from
image data
contourColor (string) - color of the overlaid contours, specified by the name of a
standard matplotlib color, e.g., "black",
"white", "cyan" etc. (do
"help(pylab.colors)" in the Python interpreter to see
available options)
contourWidth (int) - width of the overlaid contours
Clips the inputArray in intensity and normalises the array such that
minimum and maximum values are 0, 1. Clip in intensity is specified by
clipMinMax, a list in the format [clipMin, clipMax]
Used for normalising image arrays so that they can be turned into RGB
arrays that matplotlib can plot (see astPlots.ImagePlot).
Parameters:
inputArray (numpy array) - image data array
clipMinMax (list) - [minimum value of clipped array, maximum value of clipped array]
Returns: numpy array
normalised array with minimum value 0, maximum value 1
This class provides methods for accessing information from the World
Coordinate System (WCS) contained in the header of a FITS image.
Conversions between pixel and WCS coordinates can also be performed.
To create a WCS object from a FITS file called "test.fits",
simply:
WCS=astWCS.WCS("test.fits")
Likewise, to create a WCS object from the pyfits.header of
"test.fits":
__init__(self,
headerSource,
extensionName=0,
mode="image",
zapKeywords=[])
Creates a WCS object using either the information contained in the
header of the specified .fits image, or from a pyfits.header object.
getFullSizeSkyDeg(self)
Returns the width, height of the image according to the WCS in
decimal degrees on the sky (i.e., with the projection taken into
account).
getImageMinMaxWCSCoords(self)
Returns the minimum, maximum WCS coords defined by the size of the
parent image (as defined by the NAXIS keywords in the image header).
Creates a WCS object using either the information contained in the
header of the specified .fits image, or from a pyfits.header object. Set
mode = "pyfits" if the headerSource is a pyfits.header.
For some images from some archives, particular header keywords such as
COMMENT or HISTORY may contain unprintable strings. If you encounter
this, try setting zapKeywords = ['COMMENT', 'HISTORY'] (for example).
Parameters:
headerSource (string or pyfits.header) - filename of input .fits image, or a pyfits.header object
extensionName (int or string) - name or number of .fits extension in which image data is stored
mode (string) - set to "image" if headerSource is a .fits file name, or
set to "pyfits" if headerSource is a pyfits.header
object
zapKeywords (list)
Note:
The meta data provided by headerSource is stored in WCS.header as a
pyfits.header object.
Returns the pixel coordinates corresponding to the input WCS
coordinates (given in decimal degrees). RADeg, decDeg can be single
floats, or lists or numpy arrays.
The focus in this module is at present on calculations of distances in
a given cosmology. The parameters for the cosmological model are set
using the variables OMEGA_M0, OMEGA_L0, OMEGA_R0, H0 in the module
namespace (see below for details).
Ez(z)
Calculates the value of E(z), which describes evolution of the Hubble
parameter with redshift, at redshift z for the current set of
cosmological parameters.
Ez2(z)
Calculates the value of E(z)^2, which describes evolution of the
Hubble parameter with redshift, at redshift z for the current set of
cosmological parameters.
RVirialXRayCluster(kT,
z,
betaT)
Calculates the virial radius (in Mpc) of a galaxy cluster at redshift
z with X-ray temperature kT, assuming self-similar evolution and a
flat cosmology.
Calculates the value of E(z), which describes evolution of the Hubble
parameter with redshift, at redshift z for the current set of
cosmological parameters. See, e.g., Bryan & Norman 1998 (ApJ, 495,
80).
Calculates the value of E(z)^2, which describes evolution of the
Hubble parameter with redshift, at redshift z for the current set of
cosmological parameters. See, e.g., Bryan & Norman 1998 (ApJ, 495,
80).
Calculates the virial radius (in Mpc) of a galaxy cluster at redshift
z with X-ray temperature kT, assuming self-similar evolution and a flat
cosmology. See Arnaud et al. 2002 (A&A, 389, 1) and Bryan &
Norman 1998 (ApJ, 495, 80). A flat ΛCDM-type flat cosmology is
assumed.
Parameters:
kT (float) - cluster X-ray temperature in keV
z (float) - redshift
betaT (float) - the normalisation of the virial relation, for which Evrard et al.
1996 (ApJ,469, 494) find a value of 1.05
Returns: float
virial radius of cluster in Mpc
Note:
If OMEGA_M0+OMEGA_L0 is not equal to 1, this routine exits and
prints an error message to the console.
This class describes a stellar population model, either a Simple
Stellar Population (SSP) or a Composite Stellar Population (CSP), such as
the models of Bruzual & Charlot 2003 or Maraston 2005.
The constructor for this class can be used for generic SSPs or CSPs
stored in white space delimited text files, containing columns for age,
wavelength, and flux. Columns are counted from 0 ... n. Lines starting
with # are ignored.
The classes M05Model (for Maraston 2005 models), BC03Model (for
Bruzual & Charlot 2003 models), and P09Model (for
Percival et al. 2009 models) are derived from this class. The only
difference between them is the code used to load in the model data.
getColourEvolution(self,
passband1,
passband2,
zFormation,
zStepSize=0.05,
magType='Vega')
Calculates the evolution of the colour observed through passband1 -
passband2 for the StellarPopulation with redshift, from z = 0 to z =
zFormation.
getMagEvolution(self,
passband,
magNormalisation,
zNormalisation,
zFormation,
zStepSize=0.05,
onePlusZSteps=False,
magType='Vega')
Calculates the evolution with redshift (from z = 0 to z = zFormation)
of apparent magnitude in the observed frame through the passband for
the StellarPopulation, normalised to magNormalisation (apparent) at z
= zNormalisation.
calcEvolutionCorrection(self,
zFrom,
zTo,
zFormation,
passband,
magType='Vega')
Calculates the evolution correction in magnitudes in the rest frame
through the passband from redshift zFrom to redshift zTo, where the
stellarPopulation is assumed to be formed at redshift zFormation.
Calculates the evolution with redshift (from z = 0 to z = zFormation)
of apparent magnitude in the observed frame through the passband for the
StellarPopulation, normalised to magNormalisation (apparent) at z =
zNormalisation.
Parameters:
passband (Passband object) - filter passband through which to calculate the magnitude
magNormalisation (float) - sets the apparent magnitude of the SED at zNormalisation
zNormalisation (float) - the redshift at which the magnitude normalisation is carried out
zFormation (float) - formation redshift of the StellarPopulation
zStepSize (float) - size of interval in z at which to calculate model magnitudes
onePlusZSteps (bool) - if True, zSteps are (1+z)*zStepSize, otherwise zSteps are linear
Calculates the evolution correction in magnitudes in the rest frame
through the passband from redshift zFrom to redshift zTo, where the
stellarPopulation is assumed to be formed at redshift zFormation.
Parameters:
zFormation (float) - redshift to evolution correct from
zTo (float) - redshift to evolution correct to
zFormation (float) - formation redshift of the StellarPopulation
passband (Passband object) - filter passband through which to calculate magnitude
magType (string) - either "Vega" or "AB"
zFrom (float)
Returns: float
evolution correction in magnitudes in the rest frame
1""" 2astLib - python astronomy modules 3 4(c) 2007-2012 Matt Hilton 5 6(c) 2013-2014 Matt Hilton & Steven Boada 7 8U{http://astlib.sourceforge.net} 910See the README file for information on usage and installation.11See the LICENSE file for information on distribution.1213"""1415__all__=['astCalc','astCoords','astImages','astPlots','astStats','astWCS','astSED']16__version__='0.8.0'17
This document contains the API (Application Programming Interface)
documentation for this project. Documentation for the Python
objects defined by the project is divided into separate pages for each
package, module, and class. The API documentation also includes two
pages containing information about the project as a whole: a trees
page, and an index page.
Object Documentation
Each Package Documentation page contains:
A description of the package.
A list of the modules and sub-packages contained by the
package.
A summary of the classes defined by the package.
A summary of the functions defined by the package.
A summary of the variables defined by the package.
A detailed description of each function defined by the
package.
A detailed description of each variable defined by the
package.
Each Module Documentation page contains:
A description of the module.
A summary of the classes defined by the module.
A summary of the functions defined by the module.
A summary of the variables defined by the module.
A detailed description of each function defined by the
module.
A detailed description of each variable defined by the
module.
Each Class Documentation page contains:
A class inheritance diagram.
A list of known subclasses.
A description of the class.
A summary of the methods defined by the class.
A summary of the instance variables defined by the class.
A summary of the class (static) variables defined by the
class.
A detailed description of each method defined by the
class.
A detailed description of each instance variable defined by the
class.
A detailed description of each class (static) variable defined
by the class.
Project Documentation
The Trees page contains the module and class hierarchies:
The module hierarchy lists every package and module, with
modules grouped into packages. At the top level, and within each
package, modules and sub-packages are listed alphabetically.
The class hierarchy lists every class, grouped by base
class. If a class has more than one base class, then it will be
listed under each base class. At the top level, and under each base
class, classes are listed alphabetically.
The Index page contains indices of terms and
identifiers:
The term index lists every term indexed by any object's
documentation. For each term, the index provides links to each
place where the term is indexed.
The identifier index lists the (short) name of every package,
module, class, method, function, variable, and parameter. For each
identifier, the index provides a short description, and a link to
its documentation.
The Table of Contents
The table of contents occupies the two frames on the left side of
the window. The upper-left frame displays the project
contents, and the lower-left frame displays the module
contents:
Project Contents...
API Documentation Frame
Module Contents ...
The project contents frame contains a list of all packages
and modules that are defined by the project. Clicking on an entry
will display its contents in the module contents frame. Clicking on a
special entry, labeled "Everything," will display the contents of
the entire project.
The module contents frame contains a list of every
submodule, class, type, exception, function, and variable defined by a
module or package. Clicking on an entry will display its
documentation in the API documentation frame. Clicking on the name of
the module, at the top of the frame, will display the documentation
for the module itself.
The "frames" and "no frames" buttons below the top
navigation bar can be used to control whether the table of contents is
displayed or not.
The Navigation Bar
A navigation bar is located at the top and bottom of every page.
It indicates what type of page you are currently viewing, and allows
you to go to related pages. The following table describes the labels
on the navigation bar. Note that not some labels (such as
[Parent]) are not displayed on all pages.
Label
Highlighted when...
Links to...
[Parent]
(never highlighted)
the parent of the current package
[Package]
viewing a package
the package containing the current object
[Module]
viewing a module
the module containing the current object
[Class]
viewing a class
the class containing the current object
[Trees]
viewing the trees page
the trees page
[Index]
viewing the index page
the index page
[Help]
viewing the help page
the help page
The "show private" and "hide private" buttons below
the top navigation bar can be used to control whether documentation
for private objects is displayed. Private objects are usually defined
as objects whose (short) names begin with a single underscore, but do
not end with an underscore. For example, "_x",
"__pprint", and "epydoc.epytext._tokenize"
are private objects; but "re.sub",
"__init__", and "type_" are not. However,
if a module defines the "__all__" variable, then its
contents are used to decide which objects are private.
A timestamp below the bottom navigation bar indicates when each
page was last updated.
astLib.astSED.TopHatPassband:
This class generates a passband with a top hat response between the
given wavelengths.
astLib.astSED.SED:
This class describes a Spectral Energy Distribution (SED).
astLib.astSED.VegaSED:
This class stores the SED of Vega, used for calculation of
magnitudes on the Vega system.
astLib.astSED.StellarPopulation:
This class describes a stellar population model, either a Simple
Stellar Population (SSP) or a Composite Stellar Population (CSP),
such as the models of Bruzual & Charlot 2003 or Maraston 2005.
astLib.astSED.BC03Model:
This class describes a Bruzual & Charlot 2003 stellar
population model, extracted from a GALAXEV .ised file using the
galaxevpl program that is included in GALAXEV.
astLib.astSED.P09Model:
This class describes a Percival et al 2009 (BaSTI;
http://albione.oa-teramo.inaf.it) stellar population model.
astLib.astWCS.WCS:
This class provides methods for accessing information from the
World Coordinate System (WCS) contained in the header of a FITS
image.
This class describes a Maraston 2005 stellar population model. To load
a composite stellar population model (CSP) for a tau = 0.1 Gyr burst of
star formation, solar metallicity, Salpeter IMF:
where M05_DIR is set to point to the directory where the Maraston 2005
models are stored on your system.
The file format of the Maraston 2005 simple stellar poulation (SSP)
models is different to the file format used for the CSPs, and this needs
to be specified using the fileType parameter. To load a SSP with solar
metallicity, red horizontal branch morphology:
This is a higher level interface to some of the routines in PyWCSTools
(distributed with astLib). PyWCSTools is a simple SWIG wrapping of
WCSTools by Jessica Mink (http://tdc-www.harvard.edu/software/wcstools/). It is
intended is to make this interface complete enough such that direct use
of PyWCSTools is unnecessary.
Finds the minimum, maximum WCS coords that overlap between wcs1 and
wcs2. Returns these coordinates, plus the corresponding pixel coordinates
for each wcs. Useful for clipping overlapping region between two
images.
Returns: dictionary
dictionary with keys 'overlapWCS' (min, max RA, dec of overlap
between wcs1, wcs2) 'wcs1Pix', 'wcs2Pix' (pixel coords in each
input WCS that correspond to 'overlapWCS' coords)
If True (default), pixel coordinates accepted/returned by routines such
as astWCS.WCS.pix2wcs, astWCS.WCS.wcs2pix have (0, 0) as the origin. Set to
False to make these routines accept/return pixel coords with (1, 1) as
the origin (i.e. to match the FITS convention, default behaviour prior to
astLib version 0.3.0).
This module provides the matplotlib powered ImagePlot class, which is
designed to be flexible. ImagePlots can have RA, Dec. coordinate axes,
contour overlays, and have objects marked in them, using WCS coordinates.
RGB plots are supported too.
DEC_TICK_STEPS = [{'deg': 1.0/ 60.0/ 60.0, 'unit': "s"}, {'deg...
Defines the possible coordinate label steps on the delination axis in
sexagesimal mode.
dictionary list
RA_TICK_STEPS = [{'deg':(0.5/ 60.0/ 60.0/ 24.0)* 360.0, 'unit'...
Defines the possible coordinate label steps on the right ascension
axis in sexagesimal mode.
list
DECIMAL_TICK_STEPS = [0.001, 0.0025, 0.005, 0.01, 0.025, 0.05,...
Defines the possible coordinate label steps on both coordinate axes
in decimal degrees mode.
string
DEG = u("\N{DEGREE SIGN}")
Variable to stand in for the degrees symbol.
string
PRIME = "$^\prime$"
Variable to stand in for the prime symbol.
string
DOUBLE_PRIME = "$^{\prime\prime}$"
Variable to stand in for the double prime symbol.
astLib-0.8.0/docs/astLib/epydoc.css 0000644 0001750 0001750 00000037227 12375434532 017441 0 ustar matty matty 0000000 0000000
/* Epydoc CSS Stylesheet
*
* This stylesheet can be used to customize the appearance of epydoc's
* HTML output.
*
*/
/* Default Colors & Styles
* - Set the default foreground & background color with 'body'; and
* link colors with 'a:link' and 'a:visited'.
* - Use bold for decision list terms.
* - The heading styles defined here are used for headings *within*
* docstring descriptions. All headings used by epydoc itself use
* either class='epydoc' or class='toc' (CSS styles for both
* defined below).
*/
body { background: #ffffff; color: #000000; }
p { margin-top: 0.5em; margin-bottom: 0.5em; }
a:link { color: #0000ff; }
a:visited { color: #204080; }
dt { font-weight: bold; }
h1 { font-size: +140%; font-style: italic;
font-weight: bold; }
h2 { font-size: +125%; font-style: italic;
font-weight: bold; }
h3 { font-size: +110%; font-style: italic;
font-weight: normal; }
code { font-size: 100%; }
/* N.B.: class, not pseudoclass */
a.link { font-family: monospace; }
/* Page Header & Footer
* - The standard page header consists of a navigation bar (with
* pointers to standard pages such as 'home' and 'trees'); a
* breadcrumbs list, which can be used to navigate to containing
* classes or modules; options links, to show/hide private
* variables and to show/hide frames; and a page title (using
*
). The page title may be followed by a link to the
* corresponding source code (using 'span.codelink').
* - The footer consists of a navigation bar, a timestamp, and a
* pointer to epydoc's homepage.
*/
h1.epydoc { margin: 0; font-size: +140%; font-weight: bold; }
h2.epydoc { font-size: +130%; font-weight: bold; }
h3.epydoc { font-size: +115%; font-weight: bold;
margin-top: 0.2em; }
td h3.epydoc { font-size: +115%; font-weight: bold;
margin-bottom: 0; }
table.navbar { background: #a0c0ff; color: #000000;
border: 2px groove #c0d0d0; }
table.navbar table { color: #000000; }
th.navbar-select { background: #70b0ff;
color: #000000; }
table.navbar a { text-decoration: none; }
table.navbar a:link { color: #0000ff; }
table.navbar a:visited { color: #204080; }
span.breadcrumbs { font-size: 85%; font-weight: bold; }
span.options { font-size: 70%; }
span.codelink { font-size: 85%; }
td.footer { font-size: 85%; }
/* Table Headers
* - Each summary table and details section begins with a 'header'
* row. This row contains a section title (marked by
* 'span.table-header') as well as a show/hide private link
* (marked by 'span.options', defined above).
* - Summary tables that contain user-defined groups mark those
* groups using 'group header' rows.
*/
td.table-header { background: #70b0ff; color: #000000;
border: 1px solid #608090; }
td.table-header table { color: #000000; }
td.table-header table a:link { color: #0000ff; }
td.table-header table a:visited { color: #204080; }
span.table-header { font-size: 120%; font-weight: bold; }
th.group-header { background: #c0e0f8; color: #000000;
text-align: left; font-style: italic;
font-size: 115%;
border: 1px solid #608090; }
/* Summary Tables (functions, variables, etc)
* - Each object is described by a single row of the table with
* two cells. The left cell gives the object's type, and is
* marked with 'code.summary-type'. The right cell gives the
* object's name and a summary description.
* - CSS styles for the table's header and group headers are
* defined above, under 'Table Headers'
*/
table.summary { border-collapse: collapse;
background: #e8f0f8; color: #000000;
border: 1px solid #608090;
margin-bottom: 0.5em; }
td.summary { border: 1px solid #608090; }
code.summary-type { font-size: 85%; }
table.summary a:link { color: #0000ff; }
table.summary a:visited { color: #204080; }
/* Details Tables (functions, variables, etc)
* - Each object is described in its own div.
* - A single-row summary table w/ table-header is used as
* a header for each details section (CSS style for table-header
* is defined above, under 'Table Headers').
*/
table.details { border-collapse: collapse;
background: #e8f0f8; color: #000000;
border: 1px solid #608090;
margin: .2em 0 0 0; }
table.details table { color: #000000; }
table.details a:link { color: #0000ff; }
table.details a:visited { color: #204080; }
/* Fields */
dl.fields { margin-left: 2em; margin-top: 1em;
margin-bottom: 1em; }
dl.fields dd ul { margin-left: 0em; padding-left: 0em; }
dl.fields dd ul li ul { margin-left: 2em; padding-left: 0em; }
div.fields { margin-left: 2em; }
div.fields p { margin-bottom: 0.5em; }
/* Index tables (identifier index, term index, etc)
* - link-index is used for indices containing lists of links
* (namely, the identifier index & term index).
* - index-where is used in link indices for the text indicating
* the container/source for each link.
* - metadata-index is used for indices containing metadata
* extracted from fields (namely, the bug index & todo index).
*/
table.link-index { border-collapse: collapse;
background: #e8f0f8; color: #000000;
border: 1px solid #608090; }
td.link-index { border-width: 0px; }
table.link-index a:link { color: #0000ff; }
table.link-index a:visited { color: #204080; }
span.index-where { font-size: 70%; }
table.metadata-index { border-collapse: collapse;
background: #e8f0f8; color: #000000;
border: 1px solid #608090;
margin: .2em 0 0 0; }
td.metadata-index { border-width: 1px; border-style: solid; }
table.metadata-index a:link { color: #0000ff; }
table.metadata-index a:visited { color: #204080; }
/* Function signatures
* - sig* is used for the signature in the details section.
* - .summary-sig* is used for the signature in the summary
* table, and when listing property accessor functions.
* */
.sig-name { color: #006080; }
.sig-arg { color: #008060; }
.sig-default { color: #602000; }
.summary-sig { font-family: monospace; }
.summary-sig-name { color: #006080; font-weight: bold; }
table.summary a.summary-sig-name:link
{ color: #006080; font-weight: bold; }
table.summary a.summary-sig-name:visited
{ color: #006080; font-weight: bold; }
.summary-sig-arg { color: #006040; }
.summary-sig-default { color: #501800; }
/* Subclass list
*/
ul.subclass-list { display: inline; }
ul.subclass-list li { display: inline; }
/* To render variables, classes etc. like functions */
table.summary .summary-name { color: #006080; font-weight: bold;
font-family: monospace; }
table.summary
a.summary-name:link { color: #006080; font-weight: bold;
font-family: monospace; }
table.summary
a.summary-name:visited { color: #006080; font-weight: bold;
font-family: monospace; }
/* Variable values
* - In the 'variable details' sections, each varaible's value is
* listed in a 'pre.variable' box. The width of this box is
* restricted to 80 chars; if the value's repr is longer than
* this it will be wrapped, using a backslash marked with
* class 'variable-linewrap'. If the value's repr is longer
* than 3 lines, the rest will be ellided; and an ellipsis
* marker ('...' marked with 'variable-ellipsis') will be used.
* - If the value is a string, its quote marks will be marked
* with 'variable-quote'.
* - If the variable is a regexp, it is syntax-highlighted using
* the re* CSS classes.
*/
pre.variable { padding: .5em; margin: 0;
background: #dce4ec; color: #000000;
border: 1px solid #708890; }
.variable-linewrap { color: #604000; font-weight: bold; }
.variable-ellipsis { color: #604000; font-weight: bold; }
.variable-quote { color: #604000; font-weight: bold; }
.variable-group { color: #008000; font-weight: bold; }
.variable-op { color: #604000; font-weight: bold; }
.variable-string { color: #006030; }
.variable-unknown { color: #a00000; font-weight: bold; }
.re { color: #000000; }
.re-char { color: #006030; }
.re-op { color: #600000; }
.re-group { color: #003060; }
.re-ref { color: #404040; }
/* Base tree
* - Used by class pages to display the base class hierarchy.
*/
pre.base-tree { font-size: 80%; margin: 0; }
/* Frames-based table of contents headers
* - Consists of two frames: one for selecting modules; and
* the other listing the contents of the selected module.
* - h1.toc is used for each frame's heading
* - h2.toc is used for subheadings within each frame.
*/
h1.toc { text-align: center; font-size: 105%;
margin: 0; font-weight: bold;
padding: 0; }
h2.toc { font-size: 100%; font-weight: bold;
margin: 0.5em 0 0 -0.3em; }
/* Syntax Highlighting for Source Code
* - doctest examples are displayed in a 'pre.py-doctest' block.
* If the example is in a details table entry, then it will use
* the colors specified by the 'table pre.py-doctest' line.
* - Source code listings are displayed in a 'pre.py-src' block.
* Each line is marked with 'span.py-line' (used to draw a line
* down the left margin, separating the code from the line
* numbers). Line numbers are displayed with 'span.py-lineno'.
* The expand/collapse block toggle button is displayed with
* 'a.py-toggle' (Note: the CSS style for 'a.py-toggle' should not
* modify the font size of the text.)
* - If a source code page is opened with an anchor, then the
* corresponding code block will be highlighted. The code
* block's header is highlighted with 'py-highlight-hdr'; and
* the code block's body is highlighted with 'py-highlight'.
* - The remaining py-* classes are used to perform syntax
* highlighting (py-string for string literals, py-name for names,
* etc.)
*/
pre.py-doctest { padding: .5em; margin: 1em;
background: #e8f0f8; color: #000000;
border: 1px solid #708890; }
table pre.py-doctest { background: #dce4ec;
color: #000000; }
pre.py-src { border: 2px solid #000000;
background: #f0f0f0; color: #000000; }
.py-line { border-left: 2px solid #000000;
margin-left: .2em; padding-left: .4em; }
.py-lineno { font-style: italic; font-size: 90%;
padding-left: .5em; }
a.py-toggle { text-decoration: none; }
div.py-highlight-hdr { border-top: 2px solid #000000;
border-bottom: 2px solid #000000;
background: #d8e8e8; }
div.py-highlight { border-bottom: 2px solid #000000;
background: #d0e0e0; }
.py-prompt { color: #005050; font-weight: bold;}
.py-more { color: #005050; font-weight: bold;}
.py-string { color: #006030; }
.py-comment { color: #003060; }
.py-keyword { color: #600000; }
.py-output { color: #404040; }
.py-name { color: #000050; }
.py-name:link { color: #000050 !important; }
.py-name:visited { color: #000050 !important; }
.py-number { color: #005000; }
.py-defname { color: #000060; font-weight: bold; }
.py-def-name { color: #000060; font-weight: bold; }
.py-base-class { color: #000060; }
.py-param { color: #000060; }
.py-docstring { color: #006030; }
.py-decorator { color: #804020; }
/* Use this if you don't want links to names underlined: */
/*a.py-name { text-decoration: none; }*/
/* Graphs & Diagrams
* - These CSS styles are used for graphs & diagrams generated using
* Graphviz dot. 'img.graph-without-title' is used for bare
* diagrams (to remove the border created by making the image
* clickable).
*/
img.graph-without-title { border: none; }
img.graph-with-title { border: 1px solid #000000; }
span.graph-title { font-weight: bold; }
span.graph-caption { }
/* General-purpose classes
* - 'p.indent-wrapped-lines' defines a paragraph whose first line
* is not indented, but whose subsequent lines are.
* - The 'nomargin-top' class is used to remove the top margin (e.g.
* from lists). The 'nomargin' class is used to remove both the
* top and bottom margin (but not the left or right margin --
* for lists, that would cause the bullets to disappear.)
*/
p.indent-wrapped-lines { padding: 0 0 0 7em; text-indent: -7em;
margin: 0; }
.nomargin-top { margin-top: 0; }
.nomargin { margin-top: 0; margin-bottom: 0; }
/* HTML Log */
div.log-block { padding: 0; margin: .5em 0 .5em 0;
background: #e8f0f8; color: #000000;
border: 1px solid #000000; }
div.log-error { padding: .1em .3em .1em .3em; margin: 4px;
background: #ffb0b0; color: #000000;
border: 1px solid #000000; }
div.log-warning { padding: .1em .3em .1em .3em; margin: 4px;
background: #ffffb0; color: #000000;
border: 1px solid #000000; }
div.log-info { padding: .1em .3em .1em .3em; margin: 4px;
background: #b0ffb0; color: #000000;
border: 1px solid #000000; }
h2.log-hdr { background: #70b0ff; color: #000000;
margin: 0; padding: 0em 0.5em 0em 0.5em;
border-bottom: 1px solid #000000; font-size: 110%; }
p.log { font-weight: bold; margin: .5em 0 .5em 0; }
tr.opt-changed { color: #000000; font-weight: bold; }
tr.opt-default { color: #606060; }
pre.log { margin: 0; padding: 0; padding-left: 1em; }
astLib-0.8.0/docs/astLib/astLib.astStats-module.html 0000644 0001750 0001750 00000137206 12375434532 022636 0 ustar matty matty 0000000 0000000
astLib.astStats
This module (as you may notice) provides very few statistical
routines. It does, however, provide biweight (robust) estimators of
location and scale, as described in Beers et al. 1990 (AJ, 100, 32), in
addition to a robust least squares fitting routine that uses the biweight
transform.
Some routines may fail if they are passed lists with few items and
encounter a `divide by zero' error. Where this occurs, the function will
return None. An error message will be printed to the console when this
happens if astStats.REPORT_ERRORS=True (the default). Testing if an
astStats function returns None can be used to handle errors in
scripts.
biweightLSFit(dataList,
tuningConstant,
sigmaCut=None)
Performs a weighted least squares fit, where the weights used are the
biweight transforms of the residuals to the previous best fit .i.e.
Iteratively calculates biweight location and scale, using sigma
clipping, for a list of values. The calculation is performed on the
first column of a multi-dimensional list; other columns are ignored.
Parameters:
dataList (list) - input data
tuningConstant (float) - 6.0 is recommended for location estimates, 9.0 is recommended for
scale estimates
sigmaCut (float) - sigma clipping to apply
Returns: dictionary
estimate of biweight location, scale, and list of non-clipped
data, in the format {'biweightLocation', 'biweightScale',
'dataList'}
Performs a weighted least squares fit on a three dimensional list of
numbers [x, y, y error].
Parameters:
dataList (list) - input data, must be a three dimensional list in format [x, y, y
error]
weightType (string) - if "errors", weights are calculated assuming the input
data is in the format [x, y, error on y]; if "weights",
the weights are assumed to be already calculated and stored in a
fourth column [x, y, error on y, weight] (as used by e.g. astStats.biweightLSFit)
Returns: dictionary
slope and intercept on y-axis, with associated errors, in the
format {'slope', 'intercept', 'slopeError', 'interceptError'}
Performs a weighted least squares fit, where the weights used are the
biweight transforms of the residuals to the previous best fit .i.e. the
procedure is iterative, and converges very quickly (iterations is set to
10 by default). Minimum number of data points is 10.
This seems to give slightly different results to the equivalent R
routine, so use at your own risk!
Parameters:
dataList (list) - input data, must be a three dimensional list in format [x, y, y
weight]
tuningConstant (float) - 6.0 is recommended for location estimates, 9.0 is recommended for
scale estimates
sigmaCut (float) - sigma clipping to apply (set to None if not required)
Returns: dictionary
slope and intercept on y-axis, with associated errors, in the
format {'slope', 'intercept', 'slopeError', 'interceptError'}
This class describes a filter transmission curve. Passband objects are
created by loading data from from text files containing wavelength in
angstroms in the first column, relative transmission efficiency in the
second column (whitespace delimited). For example, to create a Passband
object for the 2MASS J filter:
passband=astSED.Passband("J_2MASS.res")
where "J_2MASS.res" is a file in the current working
directory that describes the filter.
Wavelength units can be specified as 'angstroms', 'nanometres' or
'microns'; if either of the latter, they will be converted to
angstroms.
plot(self,
xmin='min',
xmax='max',
maxTransmission=None)
Plots the passband, rescaling the maximum of the tranmission curve to
maxTransmission if required.
This class describes a Percival et al 2009 (BaSTI;
http://albione.oa-teramo.inaf.it) stellar population model. We assume
that the synthetic spectra for each model are unpacked under the
directory pointed to by fileName.
The wavelength units of SEDs from P09 models are converted to
Angstroms. Flux is converted into units of erg/s/Angstrom (the units in
the BaSTI low-res spectra are 4.3607e-33 erg/s/m).
This class describes a Spectral Energy Distribution (SED).
To create a SED object, lists (or numpy arrays) of wavelength and
relative flux must be provided. The SED can optionally be redshifted. The
wavelength units of SEDs are assumed to be Angstroms - flux calculations
using Passband and SED objects specified with different wavelength units
will be incorrect.
The StellarPopulation class (and derivatives) can be used to
extract SEDs for specified ages from e.g. the Bruzual & Charlot 2003
or Maraston 2005 models.
matchFlux(self,
matchSED,
minWavelength,
maxWavelength)
Matches the flux in the wavelength range given by minWavelength,
maxWavelength to the flux in the same region in matchSED.
getSEDDict(self,
passbands)
This is a convenience function for pulling out fluxes from a SED for
a given set of passbands in the same format as made by mags2SEDDict - designed to make fitting code
simpler.
Matches the flux in the wavelength range given by minWavelength,
maxWavelength to the flux in the same region in matchSED. Useful for
plotting purposes.
Calculates magnitude in the given passband. If addDistanceModulus ==
True, then the distance modulus (5.0*log10*(dl*1e5), where dl is the
luminosity distance in Mpc at the redshift of the SED) is added.
Parameters:
passband (Passband object) - filter passband through which to calculate the magnitude from the
SED
addDistanceModulus (bool) - if True, adds 5.0*log10*(dl*1e5) to the mag returned, where dl is
the luminosity distance (Mpc) corresponding to the SED z
magType (string) - either "Vega" or "AB"
Returns: float
magnitude through the given passband on the specified magnitude
system
This is a convenience function for pulling out fluxes from a SED for a
given set of passbands in the same format as made by mags2SEDDict - designed to make fitting code
simpler.
Parameters:
passbands (list of Passband objects) - list of passbands through which fluxes will be calculated
Applies the Calzetti et al. 2000 (ApJ, 533, 682) extinction law to the
SED with the given E(B-V) amount of extinction. R_v' = 4.05 is assumed
(see equation (5) of Calzetti et al.).
Parameters:
EBMinusV (float) - extinction E(B-V), in magnitudes
1"""module for simple .fits image tasks (rotation, clipping out sections, making .pngs etc.) 2 3(c) 2007-2014 Matt Hilton 4 5U{http://astlib.sourceforge.net} 6 7Some routines in this module will fail if, e.g., asked to clip a section from a .fits image at a 8position not found within the image (as determined using the WCS). Where this occurs, the function 9will return None. An error message will be printed to the console when this happens if 10astImages.REPORT_ERRORS=True (the default). Testing if an astImages function returns None can be 11used to handle errors in scripts. 12 13""" 14 15REPORT_ERRORS=True 16 17importos 18importsys 19importmath 20fromastLibimportastWCS 21 22# So far as I can tell in astropy 0.4 the API is the same as pyfits for what we need... 23try: 24importpyfits 25except: 26try: 27fromastropy.ioimportfitsaspyfits 28except: 29raiseException,"couldn't import either pyfits or astropy.io.fits" 30 31try: 32fromscipyimportndimage 33fromscipyimportinterpolate 34exceptImportError: 35print("WARNING: astImages: failed to import scipy.ndimage - some functions will not work.") 36importnumpy 37try: 38importmatplotlib 39frommatplotlibimportpylab 40matplotlib.interactive(False) 41exceptImportError: 42print("WARNING: astImages: failed to import matplotlib - some functions will not work.") 43 44#---------------------------------------------------------------------------------------------------
46"""Clips a square or rectangular section from an image array at the given celestial coordinates. 47 An updated WCS for the clipped section is optionally returned, as well as the x, y pixel 48 coordinates in the original image corresponding to the clipped section. 49 50 Note that the clip size is specified in degrees on the sky. For projections that have varying 51 real pixel scale across the map (e.g. CEA), use L{clipUsingRADecCoords} instead. 52 53 @type imageData: numpy array 54 @param imageData: image data array 55 @type imageWCS: astWCS.WCS 56 @param imageWCS: astWCS.WCS object 57 @type RADeg: float 58 @param RADeg: coordinate in decimal degrees 59 @type decDeg: float 60 @param decDeg: coordinate in decimal degrees 61 @type clipSizeDeg: float or list in format [widthDeg, heightDeg] 62 @param clipSizeDeg: if float, size of square clipped section in decimal degrees; if list, 63 size of clipped section in degrees in x, y axes of image respectively 64 @type returnWCS: bool 65 @param returnWCS: if True, return an updated WCS for the clipped section 66 @rtype: dictionary 67 @return: clipped image section (numpy array), updated astWCS WCS object for 68 clipped image section, and coordinates of clipped section in imageData in format 69 {'data', 'wcs', 'clippedSection'}. 70 71 """ 72 73imHeight=imageData.shape[0] 74imWidth=imageData.shape[1] 75xImScale=imageWCS.getXPixelSizeDeg() 76yImScale=imageWCS.getYPixelSizeDeg() 77 78iftype(clipSizeDeg)==float: 79xHalfClipSizeDeg=clipSizeDeg/2.0 80yHalfClipSizeDeg=xHalfClipSizeDeg 81eliftype(clipSizeDeg)==listortype(clipSizeDeg)==tuple: 82xHalfClipSizeDeg=clipSizeDeg[0]/2.0 83yHalfClipSizeDeg=clipSizeDeg[1]/2.0 84else: 85raiseException("did not understand clipSizeDeg: should be float, or [widthDeg, heightDeg]") 86 87xHalfSizePix=xHalfClipSizeDeg/xImScale 88yHalfSizePix=yHalfClipSizeDeg/yImScale 89 90cPixCoords=imageWCS.wcs2pix(RADeg,decDeg) 91 92cTopLeft=[cPixCoords[0]+xHalfSizePix,cPixCoords[1]+yHalfSizePix] 93cBottomRight=[cPixCoords[0]-xHalfSizePix,cPixCoords[1]-yHalfSizePix] 94 95X=[int(round(cTopLeft[0])),int(round(cBottomRight[0]))] 96Y=[int(round(cTopLeft[1])),int(round(cBottomRight[1]))] 97 98X.sort() 99Y.sort() 100 101ifX[0]<0: 102X[0]=0 103ifX[1]>imWidth: 104X[1]=imWidth 105ifY[0]<0: 106Y[0]=0 107ifY[1]>imHeight: 108Y[1]=imHeight 109 110clippedData=imageData[Y[0]:Y[1],X[0]:X[1]] 111 112# Update WCS 113ifreturnWCS==True: 114try: 115oldCRPIX1=imageWCS.header['CRPIX1'] 116oldCRPIX2=imageWCS.header['CRPIX2'] 117clippedWCS=imageWCS.copy() 118clippedWCS.header['NAXIS1']=clippedData.shape[1] 119clippedWCS.header['NAXIS2']=clippedData.shape[0] 120clippedWCS.header['CRPIX1']=oldCRPIX1-X[0] 121clippedWCS.header['CRPIX2']=oldCRPIX2-Y[0] 122clippedWCS.updateFromHeader() 123 124exceptKeyError: 125 126ifREPORT_ERRORS==True: 127 128print("WARNING: astImages.clipImageSectionWCS() : no CRPIX1, CRPIX2 keywords found - not updating clipped image WCS.") 129 130clippedData=imageData[Y[0]:Y[1],X[0]:X[1]] 131clippedWCS=imageWCS.copy() 132else: 133clippedWCS=None 134 135return{'data':clippedData,'wcs':clippedWCS,'clippedSection':[X[0],X[1],Y[0],Y[1]]}
189"""Clips a square or rectangular section from an image array at the given celestial coordinates. 190 The resulting clip is rotated and/or flipped such that North is at the top, and East appears at 191 the left. An updated WCS for the clipped section is also returned. Note that the alignment 192 of the rotated WCS is currently not perfect - however, it is probably good enough in most 193 cases for use with L{ImagePlot} for plotting purposes. 194 195 Note that the clip size is specified in degrees on the sky. For projections that have varying 196 real pixel scale across the map (e.g. CEA), use L{clipUsingRADecCoords} instead. 197 198 @type imageData: numpy array 199 @param imageData: image data array 200 @type imageWCS: astWCS.WCS 201 @param imageWCS: astWCS.WCS object 202 @type RADeg: float 203 @param RADeg: coordinate in decimal degrees 204 @type decDeg: float 205 @param decDeg: coordinate in decimal degrees 206 @type clipSizeDeg: float 207 @param clipSizeDeg: if float, size of square clipped section in decimal degrees; if list, 208 size of clipped section in degrees in RA, dec. axes of output rotated image respectively 209 @type returnWCS: bool 210 @param returnWCS: if True, return an updated WCS for the clipped section 211 @rtype: dictionary 212 @return: clipped image section (numpy array), updated astWCS WCS object for 213 clipped image section, in format {'data', 'wcs'}. 214 215 @note: Returns 'None' if the requested position is not found within the image. If the image 216 WCS does not have keywords of the form CD1_1 etc., the output WCS will not be rotated. 217 218 """ 219 220halfImageSize=imageWCS.getHalfSizeDeg() 221imageCentre=imageWCS.getCentreWCSCoords() 222imScale=imageWCS.getPixelSizeDeg() 223 224iftype(clipSizeDeg)==float: 225xHalfClipSizeDeg=clipSizeDeg/2.0 226yHalfClipSizeDeg=xHalfClipSizeDeg 227eliftype(clipSizeDeg)==listortype(clipSizeDeg)==tuple: 228xHalfClipSizeDeg=clipSizeDeg[0]/2.0 229yHalfClipSizeDeg=clipSizeDeg[1]/2.0 230else: 231raiseException("did not understand clipSizeDeg: should be float, or [widthDeg, heightDeg]") 232 233diagonalHalfSizeDeg=math.sqrt((xHalfClipSizeDeg*xHalfClipSizeDeg) \ 234+(yHalfClipSizeDeg*yHalfClipSizeDeg)) 235 236diagonalHalfSizePix=diagonalHalfSizeDeg/imScale 237 238ifRADeg>imageCentre[0]-halfImageSize[0]andRADeg<imageCentre[0]+halfImageSize[0] \ 239anddecDeg>imageCentre[1]-halfImageSize[1]anddecDeg<imageCentre[1]+halfImageSize[1]: 240 241imageDiagonalClip=clipImageSectionWCS(imageData,imageWCS,RADeg, 242decDeg,diagonalHalfSizeDeg*2.0) 243diagonalClip=imageDiagonalClip['data'] 244diagonalWCS=imageDiagonalClip['wcs'] 245 246rotDeg=diagonalWCS.getRotationDeg() 247imageRotated=ndimage.rotate(diagonalClip,rotDeg) 248ifdiagonalWCS.isFlipped()==1: 249imageRotated=pylab.fliplr(imageRotated) 250 251# Handle WCS rotation 252rotatedWCS=diagonalWCS.copy() 253rotRadians=math.radians(rotDeg) 254 255ifreturnWCS==True: 256try: 257 258CD11=rotatedWCS.header['CD1_1'] 259CD21=rotatedWCS.header['CD2_1'] 260CD12=rotatedWCS.header['CD1_2'] 261CD22=rotatedWCS.header['CD2_2'] 262ifrotatedWCS.isFlipped()==1: 263CD11=CD11*-1 264CD12=CD12*-1 265CDMatrix=numpy.array([[CD11,CD12],[CD21,CD22]],dtype=numpy.float64) 266 267rotRadians=rotRadians 268rot11=math.cos(rotRadians) 269rot12=math.sin(rotRadians) 270rot21=-math.sin(rotRadians) 271rot22=math.cos(rotRadians) 272rotMatrix=numpy.array([[rot11,rot12],[rot21,rot22]],dtype=numpy.float64) 273newCDMatrix=numpy.dot(rotMatrix,CDMatrix) 274 275P1=diagonalWCS.header['CRPIX1'] 276P2=diagonalWCS.header['CRPIX2'] 277V1=diagonalWCS.header['CRVAL1'] 278V2=diagonalWCS.header['CRVAL2'] 279 280PMatrix=numpy.zeros((2,),dtype=numpy.float64) 281PMatrix[0]=P1 282PMatrix[1]=P2 283 284# BELOW IS HOW TO WORK OUT THE NEW REF PIXEL 285CMatrix=numpy.array([imageRotated.shape[1]/2.0,imageRotated.shape[0]/2.0]) 286centreCoords=diagonalWCS.getCentreWCSCoords() 287alphaRad=math.radians(centreCoords[0]) 288deltaRad=math.radians(centreCoords[1]) 289thetaRad=math.asin(math.sin(deltaRad)*math.sin(math.radians(V2))+ \ 290math.cos(deltaRad)*math.cos(math.radians(V2))*math.cos(alphaRad-math.radians(V1))) 291phiRad=math.atan2(-math.cos(deltaRad)*math.sin(alphaRad-math.radians(V1)), \ 292math.sin(deltaRad)*math.cos(math.radians(V2))- \ 293math.cos(deltaRad)*math.sin(math.radians(V2))*math.cos(alphaRad-math.radians(V1)))+ \ 294math.pi 295RTheta=(180.0/math.pi)*(1.0/math.tan(thetaRad)) 296 297xy=numpy.zeros((2,),dtype=numpy.float64) 298xy[0]=RTheta*math.sin(phiRad) 299xy[1]=-RTheta*math.cos(phiRad) 300newPMatrix=CMatrix-numpy.dot(numpy.linalg.inv(newCDMatrix),xy) 301 302# But there's a small offset to CRPIX due to the rotatedImage being rounded to an integer 303# number of pixels (not sure this helps much) 304#d=numpy.dot(rotMatrix, [diagonalClip.shape[1], diagonalClip.shape[0]]) 305#offset=abs(d)-numpy.array(imageRotated.shape) 306 307rotatedWCS.header['NAXIS1']=imageRotated.shape[1] 308rotatedWCS.header['NAXIS2']=imageRotated.shape[0] 309rotatedWCS.header['CRPIX1']=newPMatrix[0] 310rotatedWCS.header['CRPIX2']=newPMatrix[1] 311rotatedWCS.header['CRVAL1']=V1 312rotatedWCS.header['CRVAL2']=V2 313rotatedWCS.header['CD1_1']=newCDMatrix[0][0] 314rotatedWCS.header['CD2_1']=newCDMatrix[1][0] 315rotatedWCS.header['CD1_2']=newCDMatrix[0][1] 316rotatedWCS.header['CD2_2']=newCDMatrix[1][1] 317rotatedWCS.updateFromHeader() 318 319exceptKeyError: 320 321ifREPORT_ERRORS==True: 322print("WARNING: astImages.clipRotatedImageSectionWCS() : no CDi_j keywords found - not rotating WCS.") 323 324imageRotated=diagonalClip 325rotatedWCS=diagonalWCS 326 327imageRotatedClip=clipImageSectionWCS(imageRotated,rotatedWCS,RADeg,decDeg,clipSizeDeg) 328 329ifreturnWCS==True: 330return{'data':imageRotatedClip['data'],'wcs':imageRotatedClip['wcs']} 331else: 332return{'data':imageRotatedClip['data'],'wcs':None} 333 334else: 335 336ifREPORT_ERRORS==True: 337print("""ERROR: astImages.clipRotatedImageSectionWCS() : 338 RADeg, decDeg are not within imageData.""") 339 340returnNone
486"""Creates a matplotlib.pylab plot of an image array with the specified cuts in intensity 487 applied. This routine is used by L{saveBitmap} and L{saveContourOverlayBitmap}, which both 488 produce output as .png, .jpg, etc. images. 489 490 @type imageData: numpy array 491 @param imageData: image data array 492 @type cutLevels: list 493 @param cutLevels: sets the image scaling - available options: 494 - pixel values: cutLevels=[low value, high value]. 495 - histogram equalisation: cutLevels=["histEq", number of bins ( e.g. 1024)] 496 - relative: cutLevels=["relative", cut per cent level (e.g. 99.5)] 497 - smart: cutLevels=["smart", cut per cent level (e.g. 99.5)] 498 ["smart", 99.5] seems to provide good scaling over a range of different images. 499 @rtype: dictionary 500 @return: image section (numpy.array), matplotlib image normalisation (matplotlib.colors.Normalize), in the format {'image', 'norm'}. 501 502 @note: If cutLevels[0] == "histEq", then only {'image'} is returned. 503 504 """ 505 506oImWidth=imageData.shape[1] 507oImHeight=imageData.shape[0] 508 509# Optional histogram equalisation 510ifcutLevels[0]=="histEq": 511 512imageData=histEq(imageData,cutLevels[1]) 513anorm=pylab.Normalize(imageData.min(),imageData.max()) 514 515elifcutLevels[0]=="relative": 516 517# this turns image data into 1D array then sorts 518sorted=numpy.sort(numpy.ravel(imageData)) 519maxValue=sorted.max() 520minValue=sorted.min() 521 522# want to discard the top and bottom specified 523topCutIndex=len(sorted-1) \ 524-int(math.floor(float((100.0-cutLevels[1])/100.0)*len(sorted-1))) 525bottomCutIndex=int(math.ceil(float((100.0-cutLevels[1])/100.0)*len(sorted-1))) 526topCut=sorted[topCutIndex] 527bottomCut=sorted[bottomCutIndex] 528anorm=pylab.Normalize(bottomCut,topCut) 529 530elifcutLevels[0]=="smart": 531 532# this turns image data into 1Darray then sorts 533sorted=numpy.sort(numpy.ravel(imageData)) 534maxValue=sorted.max() 535minValue=sorted.min() 536numBins=10000# 0.01 per cent accuracy 537binWidth=(maxValue-minValue)/float(numBins) 538histogram=ndimage.histogram(sorted,minValue,maxValue,numBins) 539 540# Find the bin with the most pixels in it, set that as our minimum 541# Then search through the bins until we get to a bin with more/or the same number of 542# pixels in it than the previous one. 543# We take that to be the maximum. 544# This means that we avoid the traps of big, bright, saturated stars that cause 545# problems for relative scaling 546backgroundValue=histogram.max() 547foundBackgroundBin=False 548foundTopBin=False 549lastBin=-10000 550foriinrange(len(histogram)): 551 552ifhistogram[i]>=lastBinandfoundBackgroundBin==True: 553 554# Added a fudge here to stop us picking for top bin a bin within 555# 10 percent of the background pixel value 556if(minValue+(binWidth*i))>bottomBinValue*1.1: 557topBinValue=minValue+(binWidth*i) 558foundTopBin=True 559break 560 561ifhistogram[i]==backgroundValueandfoundBackgroundBin==False: 562bottomBinValue=minValue+(binWidth*i) 563foundBackgroundBin=True 564 565lastBin=histogram[i] 566 567iffoundTopBin==False: 568topBinValue=maxValue 569 570#Now we apply relative scaling to this 571smartClipped=numpy.clip(sorted,bottomBinValue,topBinValue) 572topCutIndex=len(smartClipped-1) \ 573-int(math.floor(float((100.0-cutLevels[1])/100.0)*len(smartClipped-1))) 574bottomCutIndex=int(math.ceil(float((100.0-cutLevels[1])/100.0)*len(smartClipped-1))) 575topCut=smartClipped[topCutIndex] 576bottomCut=smartClipped[bottomCutIndex] 577anorm=pylab.Normalize(bottomCut,topCut) 578else: 579 580# Normalise using given cut levels 581anorm=pylab.Normalize(cutLevels[0],cutLevels[1]) 582 583ifcutLevels[0]=="histEq": 584return{'image':imageData.copy()} 585else: 586return{'image':imageData.copy(),'norm':anorm}
639"""Resamples data corresponding to second image (with data im2Data, WCS im2WCS) onto the WCS 640 of the first image (im1Data, im1WCS). The output, resampled image is of the pixel same 641 dimensions of the first image. This routine is for assisting in plotting - performing 642 photometry on the output is not recommended. 643 644 Set highAccuracy == True to sample every corresponding pixel in each image; otherwise only 645 every nth pixel (where n is the ratio of the image scales) will be sampled, with values 646 in between being set using a linear interpolation (much faster). 647 648 Set onlyOverlapping == True to speed up resampling by only resampling the overlapping 649 area defined by both image WCSs. 650 651 @type im1Data: numpy array 652 @param im1Data: image data array for first image 653 @type im1WCS: astWCS.WCS 654 @param im1WCS: astWCS.WCS object corresponding to im1Data 655 @type im2Data: numpy array 656 @param im2Data: image data array for second image (to be resampled to match first image) 657 @type im2WCS: astWCS.WCS 658 @param im2WCS: astWCS.WCS object corresponding to im2Data 659 @type highAccuracy: bool 660 @param highAccuracy: if True, sample every corresponding pixel in each image; otherwise, sample 661 every nth pixel, where n = the ratio of the image scales. 662 @type onlyOverlapping: bool 663 @param onlyOverlapping: if True, only consider the overlapping area defined by both image WCSs 664 (speeds things up) 665 @rtype: dictionary 666 @return: numpy image data array and associated WCS in format {'data', 'wcs'} 667 668 """ 669 670resampledData=numpy.zeros(im1Data.shape) 671 672# Find overlap - speed things up 673# But have a border so as not to require the overlap to be perfect 674# There's also no point in oversampling image 1 if it's much higher res than image 2 675xPixRatio=(im2WCS.getXPixelSizeDeg()/im1WCS.getXPixelSizeDeg())/2.0 676yPixRatio=(im2WCS.getYPixelSizeDeg()/im1WCS.getYPixelSizeDeg())/2.0 677xBorder=xPixRatio*10.0 678yBorder=yPixRatio*10.0 679ifhighAccuracy==False: 680ifxPixRatio>1: 681xPixStep=int(math.ceil(xPixRatio)) 682else: 683xPixStep=1 684ifyPixRatio>1: 685yPixStep=int(math.ceil(yPixRatio)) 686else: 687yPixStep=1 688else: 689xPixStep=1 690yPixStep=1 691 692ifonlyOverlapping==True: 693overlap=astWCS.findWCSOverlap(im1WCS,im2WCS) 694xOverlap=[overlap['wcs1Pix'][0],overlap['wcs1Pix'][1]] 695yOverlap=[overlap['wcs1Pix'][2],overlap['wcs1Pix'][3]] 696xOverlap.sort() 697yOverlap.sort() 698xMin=int(math.floor(xOverlap[0]-xBorder)) 699xMax=int(math.ceil(xOverlap[1]+xBorder)) 700yMin=int(math.floor(yOverlap[0]-yBorder)) 701yMax=int(math.ceil(yOverlap[1]+yBorder)) 702xRemainder=(xMax-xMin)%xPixStep 703yRemainder=(yMax-yMin)%yPixStep 704ifxRemainder!=0: 705xMax=xMax+xRemainder 706ifyRemainder!=0: 707yMax=yMax+yRemainder 708# Check that we're still within the image boundaries, to be on the safe side 709ifxMin<0: 710xMin=0 711ifxMax>im1Data.shape[1]: 712xMax=im1Data.shape[1] 713ifyMin<0: 714yMin=0 715ifyMax>im1Data.shape[0]: 716yMax=im1Data.shape[0] 717else: 718xMin=0 719xMax=im1Data.shape[1] 720yMin=0 721yMax=im1Data.shape[0] 722 723forxinrange(xMin,xMax,xPixStep): 724foryinrange(yMin,yMax,yPixStep): 725RA,dec=im1WCS.pix2wcs(x,y) 726x2,y2=im2WCS.wcs2pix(RA,dec) 727x2=int(round(x2)) 728y2=int(round(y2)) 729ifx2>=0andx2<im2Data.shape[1]andy2>=0andy2<im2Data.shape[0]: 730resampledData[y][x]=im2Data[y2][x2] 731 732# linear interpolation 733ifhighAccuracy==False: 734forrowinrange(resampledData.shape[0]): 735vals=resampledData[row,numpy.arange(xMin,xMax,xPixStep)] 736index2data=interpolate.interp1d(numpy.arange(0,vals.shape[0],1),vals) 737interpedVals=index2data(numpy.arange(0,vals.shape[0]-1,1.0/xPixStep)) 738resampledData[row,xMin:xMin+interpedVals.shape[0]]=interpedVals 739forcolinrange(resampledData.shape[1]): 740vals=resampledData[numpy.arange(yMin,yMax,yPixStep),col] 741index2data=interpolate.interp1d(numpy.arange(0,vals.shape[0],1),vals) 742interpedVals=index2data(numpy.arange(0,vals.shape[0]-1,1.0/yPixStep)) 743resampledData[yMin:yMin+interpedVals.shape[0],col]=interpedVals 744 745# Note: should really just copy im1WCS keywords into im2WCS and return that 746# Only a problem if we're using this for anything other than plotting 747return{'data':resampledData,'wcs':im1WCS.copy()}
752"""Rescales an image array to be used as a contour overlay to have the same dimensions as the 753 background image, and generates a set of contour levels. The image array from which the contours 754 are to be generated will be resampled to the same dimensions as the background image data, and 755 can be optionally smoothed using a Gaussian filter. The sigma of the Gaussian filter 756 (contourSmoothFactor) is specified in arcsec. 757 758 @type backgroundImageData: numpy array 759 @param backgroundImageData: background image data array 760 @type backgroundImageWCS: astWCS.WCS 761 @param backgroundImageWCS: astWCS.WCS object of the background image data array 762 @type contourImageData: numpy array 763 @param contourImageData: image data array from which contours are to be generated 764 @type contourImageWCS: astWCS.WCS 765 @param contourImageWCS: astWCS.WCS object corresponding to contourImageData 766 @type contourLevels: list 767 @param contourLevels: sets the contour levels - available options: 768 - values: contourLevels=[list of values specifying each level] 769 - linear spacing: contourLevels=['linear', min level value, max level value, number 770 of levels] - can use "min", "max" to automatically set min, max levels from image data 771 - log spacing: contourLevels=['log', min level value, max level value, number of 772 levels] - can use "min", "max" to automatically set min, max levels from image data 773 @type contourSmoothFactor: float 774 @param contourSmoothFactor: standard deviation (in arcsec) of Gaussian filter for 775 pre-smoothing of contour image data (set to 0 for no smoothing) 776 @type highAccuracy: bool 777 @param highAccuracy: if True, sample every corresponding pixel in each image; otherwise, sample 778 every nth pixel, where n = the ratio of the image scales. 779 780 """ 781 782# For compromise between speed and accuracy, scale a copy of the background 783# image down to a scale that is one pixel = 1/5 of a pixel in the contour image 784# But only do this if it has CDij keywords as we know how to scale those 785if("CD1_1"inbackgroundImageWCS.header)==True: 786xScaleFactor=backgroundImageWCS.getXPixelSizeDeg()/(contourImageWCS.getXPixelSizeDeg()/5.0) 787yScaleFactor=backgroundImageWCS.getYPixelSizeDeg()/(contourImageWCS.getYPixelSizeDeg()/5.0) 788scaledBackground=scaleImage(backgroundImageData,backgroundImageWCS,(xScaleFactor,yScaleFactor)) 789scaled=resampleToWCS(scaledBackground['data'],scaledBackground['wcs'], 790contourImageData,contourImageWCS,highAccuracy=highAccuracy) 791scaledContourData=scaled['data'] 792scaledContourWCS=scaled['wcs'] 793scaledBackground=True 794else: 795scaled=resampleToWCS(backgroundImageData,backgroundImageWCS, 796contourImageData,contourImageWCS,highAccuracy=highAccuracy) 797scaledContourData=scaled['data'] 798scaledContourWCS=scaled['wcs'] 799scaledBackground=False 800 801ifcontourSmoothFactor>0: 802sigmaPix=(contourSmoothFactor/3600.0)/scaledContourWCS.getPixelSizeDeg() 803scaledContourData=ndimage.gaussian_filter(scaledContourData,sigmaPix) 804 805# Various ways of setting the contour levels 806# If just a list is passed in, use those instead 807ifcontourLevels[0]=="linear": 808ifcontourLevels[1]=="min": 809xMin=contourImageData.flatten().min() 810else: 811xMin=float(contourLevels[1]) 812ifcontourLevels[2]=="max": 813xMax=contourImageData.flatten().max() 814else: 815xMax=float(contourLevels[2]) 816nLevels=contourLevels[3] 817xStep=(xMax-xMin)/(nLevels-1) 818cLevels=[] 819forjinrange(nLevels+1): 820level=xMin+j*xStep 821cLevels.append(level) 822 823elifcontourLevels[0]=="log": 824ifcontourLevels[1]=="min": 825xMin=contourImageData.flatten().min() 826else: 827xMin=float(contourLevels[1]) 828ifcontourLevels[2]=="max": 829xMax=contourImageData.flatten().max() 830else: 831xMax=float(contourLevels[2]) 832ifxMin<=0.0: 833raiseException("minimum contour level set to <= 0 and log scaling chosen.") 834xLogMin=math.log10(xMin) 835xLogMax=math.log10(xMax) 836nLevels=contourLevels[3] 837xLogStep=(xLogMax-xLogMin)/(nLevels-1) 838cLevels=[] 839prevLevel=0 840forjinrange(nLevels+1): 841level=math.pow(10,xLogMin+j*xLogStep) 842cLevels.append(level) 843 844else: 845cLevels=contourLevels 846 847# Now blow the contour image data back up to the size of the original image 848ifscaledBackground==True: 849scaledBack=scaleImage(scaledContourData,scaledContourWCS,(1.0/xScaleFactor,1.0/yScaleFactor))['data'] 850else: 851scaledBack=scaledContourData 852 853return{'scaledImage':scaledBack,'contourLevels':cLevels}
857"""Makes a bitmap image from an image array; the image format is specified by the 858 filename extension. (e.g. ".jpg" =JPEG, ".png"=PNG). 859 860 @type outputFileName: string 861 @param outputFileName: filename of output bitmap image 862 @type imageData: numpy array 863 @param imageData: image data array 864 @type cutLevels: list 865 @param cutLevels: sets the image scaling - available options: 866 - pixel values: cutLevels=[low value, high value]. 867 - histogram equalisation: cutLevels=["histEq", number of bins ( e.g. 1024)] 868 - relative: cutLevels=["relative", cut per cent level (e.g. 99.5)] 869 - smart: cutLevels=["smart", cut per cent level (e.g. 99.5)] 870 ["smart", 99.5] seems to provide good scaling over a range of different images. 871 @type size: int 872 @param size: size of output image in pixels 873 @type colorMapName: string 874 @param colorMapName: name of a standard matplotlib colormap, e.g. "hot", "cool", "gray" 875 etc. (do "help(pylab.colormaps)" in the Python interpreter to see available options) 876 877 """ 878 879cut=intensityCutImage(imageData,cutLevels) 880 881# Make plot 882aspectR=float(cut['image'].shape[0])/float(cut['image'].shape[1]) 883pylab.figure(figsize=(10,10*aspectR)) 884pylab.axes([0,0,1,1]) 885 886try: 887colorMap=pylab.cm.get_cmap(colorMapName) 888exceptAssertionError: 889raiseException(colorMapName+" is not a defined matplotlib colormap.") 890 891ifcutLevels[0]=="histEq": 892pylab.imshow(cut['image'],interpolation="bilinear",origin='lower',cmap=colorMap) 893 894else: 895pylab.imshow(cut['image'],interpolation="bilinear",norm=cut['norm'],origin='lower', 896cmap=colorMap) 897 898pylab.axis("off") 899 900pylab.savefig("out_astImages.png") 901pylab.close("all") 902 903try: 904fromPILimportImage 905except: 906raiseException("astImages.saveBitmap requires the Python Imaging Library to be installed.") 907im=Image.open("out_astImages.png") 908im.thumbnail((int(size),int(size))) 909im.save(outputFileName) 910 911os.remove("out_astImages.png")
917"""Makes a bitmap image from an image array, with a set of contours generated from a 918 second image array overlaid. The image format is specified by the file extension 919 (e.g. ".jpg"=JPEG, ".png"=PNG). The image array from which the contours are to be generated 920 can optionally be pre-smoothed using a Gaussian filter. 921 922 @type outputFileName: string 923 @param outputFileName: filename of output bitmap image 924 @type backgroundImageData: numpy array 925 @param backgroundImageData: background image data array 926 @type backgroundImageWCS: astWCS.WCS 927 @param backgroundImageWCS: astWCS.WCS object of the background image data array 928 @type cutLevels: list 929 @param cutLevels: sets the image scaling - available options: 930 - pixel values: cutLevels=[low value, high value]. 931 - histogram equalisation: cutLevels=["histEq", number of bins ( e.g. 1024)] 932 - relative: cutLevels=["relative", cut per cent level (e.g. 99.5)] 933 - smart: cutLevels=["smart", cut per cent level (e.g. 99.5)] 934 ["smart", 99.5] seems to provide good scaling over a range of different images. 935 @type size: int 936 @param size: size of output image in pixels 937 @type colorMapName: string 938 @param colorMapName: name of a standard matplotlib colormap, e.g. "hot", "cool", "gray" 939 etc. (do "help(pylab.colormaps)" in the Python interpreter to see available options) 940 @type contourImageData: numpy array 941 @param contourImageData: image data array from which contours are to be generated 942 @type contourImageWCS: astWCS.WCS 943 @param contourImageWCS: astWCS.WCS object corresponding to contourImageData 944 @type contourSmoothFactor: float 945 @param contourSmoothFactor: standard deviation (in pixels) of Gaussian filter for 946 pre-smoothing of contour image data (set to 0 for no smoothing) 947 @type contourLevels: list 948 @param contourLevels: sets the contour levels - available options: 949 - values: contourLevels=[list of values specifying each level] 950 - linear spacing: contourLevels=['linear', min level value, max level value, number 951 of levels] - can use "min", "max" to automatically set min, max levels from image data 952 - log spacing: contourLevels=['log', min level value, max level value, number of 953 levels] - can use "min", "max" to automatically set min, max levels from image data 954 @type contourColor: string 955 @param contourColor: color of the overlaid contours, specified by the name of a standard 956 matplotlib color, e.g., "black", "white", "cyan" 957 etc. (do "help(pylab.colors)" in the Python interpreter to see available options) 958 @type contourWidth: int 959 @param contourWidth: width of the overlaid contours 960 961 """ 962 963cut=intensityCutImage(backgroundImageData,cutLevels) 964 965# Make plot of just the background image 966aspectR=float(cut['image'].shape[0])/float(cut['image'].shape[1]) 967pylab.figure(figsize=(10,10*aspectR)) 968pylab.axes([0,0,1,1]) 969 970try: 971colorMap=pylab.cm.get_cmap(colorMapName) 972exceptAssertionError: 973raiseException(colorMapName+" is not a defined matplotlib colormap.") 974 975ifcutLevels[0]=="histEq": 976pylab.imshow(cut['image'],interpolation="bilinear",origin='lower',cmap=colorMap) 977 978else: 979pylab.imshow(cut['image'],interpolation="bilinear",norm=cut['norm'],origin='lower', 980cmap=colorMap) 981 982pylab.axis("off") 983 984# Add the contours 985contourData=generateContourOverlay(backgroundImageData,backgroundImageWCS,contourImageData, \ 986contourImageWCS,contourLevels,contourSmoothFactor) 987 988pylab.contour(contourData['scaledImage'],contourData['contourLevels'],colors=contourColor, 989linewidths=contourWidth) 990 991pylab.savefig("out_astImages.png") 992pylab.close("all") 993 994try: 995fromPILimportImage 996except: 997raiseException("astImages.saveContourOverlayBitmap requires the Python Imaging Library to be installed") 998 999im=Image.open("out_astImages.png")1000im.thumbnail((int(size),int(size)))1001im.save(outputFileName)10021003os.remove("out_astImages.png")
1007"""Writes an image array to a new .fits file.10081009 @type outputFileName: string1010 @param outputFileName: filename of output FITS image1011 @type imageData: numpy array1012 @param imageData: image data array1013 @type imageWCS: astWCS.WCS object1014 @param imageWCS: image WCS object10151016 @note: If imageWCS=None, the FITS image will be written with a rudimentary header containing1017 no meta data.10181019 """10201021ifos.path.exists(outputFileName):1022os.remove(outputFileName)10231024newImg=pyfits.HDUList()10251026ifimageWCS!=None:1027hdu=pyfits.PrimaryHDU(None,imageWCS.header)1028else:1029hdu=pyfits.PrimaryHDU(None,None)10301031hdu.data=imageData1032newImg.append(hdu)1033newImg.writeto(outputFileName)1034newImg.close()
1038"""Performs histogram equalisation of the input numpy array.10391040 @type inputArray: numpy array1041 @param inputArray: image data array1042 @type numBins: int1043 @param numBins: number of bins in which to perform the operation (e.g. 1024)1044 @rtype: numpy array1045 @return: image data array10461047 """10481049imageData=inputArray10501051# histogram equalisation: we want an equal number of pixels in each intensity range1052sortedDataIntensities=numpy.sort(numpy.ravel(imageData))1053median=numpy.median(sortedDataIntensities)10541055# Make cumulative histogram of data values, simple min-max used to set bin sizes and range1056dataCumHist=numpy.zeros(numBins)1057minIntensity=sortedDataIntensities.min()1058maxIntensity=sortedDataIntensities.max()1059histRange=maxIntensity-minIntensity1060binWidth=histRange/float(numBins-1)1061foriinrange(len(sortedDataIntensities)):1062binNumber=int(math.ceil((sortedDataIntensities[i]-minIntensity)/binWidth))1063addArray=numpy.zeros(numBins)1064onesArray=numpy.ones(numBins-binNumber)1065onesRange=list(range(binNumber,numBins))1066numpy.put(addArray,onesRange,onesArray)1067dataCumHist=dataCumHist+addArray10681069# Make ideal cumulative histogram1070idealValue=dataCumHist.max()/float(numBins)1071idealCumHist=numpy.arange(idealValue,dataCumHist.max()+idealValue,idealValue)10721073# Map the data to the ideal1074foryinrange(imageData.shape[0]):1075forxinrange(imageData.shape[1]):1076# Get index corresponding to dataIntensity1077intensityBin=int(math.ceil((imageData[y][x]-minIntensity)/binWidth))10781079# Guard against rounding errors (happens rarely I think)1080ifintensityBin<0:1081intensityBin=01082ifintensityBin>len(dataCumHist)-1:1083intensityBin=len(dataCumHist)-110841085# Get the cumulative frequency corresponding intensity level in the data1086dataCumFreq=dataCumHist[intensityBin]10871088# Get the index of the corresponding ideal cumulative frequency1089idealBin=numpy.searchsorted(idealCumHist,dataCumFreq)1090idealIntensity=(idealBin*binWidth)+minIntensity1091imageData[y][x]=idealIntensity10921093returnimageData
1097"""Clips the inputArray in intensity and normalises the array such that minimum and maximum1098 values are 0, 1. Clip in intensity is specified by clipMinMax, a list in the format 1099 [clipMin, clipMax]11001101 Used for normalising image arrays so that they can be turned into RGB arrays that matplotlib1102 can plot (see L{astPlots.ImagePlot}).11031104 @type inputArray: numpy array1105 @param inputArray: image data array1106 @type clipMinMax: list1107 @param clipMinMax: [minimum value of clipped array, maximum value of clipped array]1108 @rtype: numpy array1109 @return: normalised array with minimum value 0, maximum value 111101111 """1112clipped=inputArray.clip(clipMinMax[0],clipMinMax[1])1113slope=1.0/(clipMinMax[1]-clipMinMax[0])1114intercept=-clipMinMax[0]*slope1115clipped=clipped*slope+intercept11161117returnclipped
1"""module for performing common calculations 2 3(c) 2007-2011 Matt Hilton 4 5(c) 2013-2014 Matt Hilton & Steven Boada 6 7U{http://astlib.sourceforge.net} 8 9The focus in this module is at present on calculations of distances in a given 10cosmology. The parameters for the cosmological model are set using the 11variables OMEGA_M0, OMEGA_L0, OMEGA_R0, H0 in the module namespace (see below for details). 12 13@var OMEGA_M0: The matter density parameter at z=0. 14@type OMEGA_M0: float 15 16@var OMEGA_L0: The dark energy density (in the form of a cosmological 17 constant) at z=0. 18@type OMEGA_L0: float 19 20@var OMEGA_R0: The radiation density at z=0 (note this is only used currently 21 in calculation of L{Ez}). 22@type OMEGA_R0: float 23 24@var H0: The Hubble parameter (in km/s/Mpc) at z=0. 25@type H0: float 26 27@var C_LIGHT: The speed of light in km/s. 28@type C_LIGHT: float 29 30""" 31 32OMEGA_M0=0.3 33OMEGA_L0=0.7 34OMEGA_R0=8.24E-5 35H0=70.0 36 37C_LIGHT=3.0e5 38 39importmath 40try: 41fromscipyimportintegrate 42exceptImportError: 43print"WARNING: astCalc failed to import scipy modules - ", 44print"some functions will not work" 45 46#import sys 47 48#------------------------------------------------------------------------------
116"""Calculates the line of sight comoving distance in Mpc at redshift z.117118 @type z: float119 @param z: redshift120 @rtype: float121 @return: transverse comoving distance (proper motion distance) in Mpc122123 """124125OMEGA_K=1.0-OMEGA_M0-OMEGA_L0126127# Integration limits128xMax=1.0129xMin=1.0/(1.0+z)130131# Function to be integrated132yn=lambdax:(1.0/math.sqrt(OMEGA_M0*x+OMEGA_L0*math.pow(x,4)+133OMEGA_K*math.pow(x,2)))134135integralValue,integralError=integrate.quad(yn,xMin,xMax)136137DC=C_LIGHT/H0*integralValue138139returnDC
143"""Calculates the line of sight comoving volume element per steradian dV/dz144 at redshift z.145146 @type z: float147 @param z: redshift148 @rtype: float149 @return: comoving volume element per steradian150151 """152153dH=C_LIGHT/H0154dVcdz=(dH*(math.pow(da(z),2))*(math.pow(1+z,2))/Ez(z))155156returndVcdz
160"""Calculates the redshift z corresponding to the luminosity distance given161 in Mpc.162163 @type distanceMpc: float164 @param distanceMpc: distance in Mpc165 @rtype: float166 @return: redshift167168 """169170dTarget=distanceMpc171172toleranceMpc=0.1173174zMin=0.0175zMax=10.0176177diff=dl(zMax)-dTarget178whilediff<0:179zMax=zMax+5.0180diff=dl(zMax)-dTarget181182zTrial=zMin+(zMax-zMin)/2.0183184dTrial=dl(zTrial)185diff=dTrial-dTarget186whileabs(diff)>toleranceMpc:187188ifdiff>0:189zMax=zMax-(zMax-zMin)/2.0190else:191zMin=zMin+(zMax-zMin)/2.0192193zTrial=zMin+(zMax-zMin)/2.0194dTrial=dl(zTrial)195diff=dTrial-dTarget196197returnzTrial
201"""Calculates the redshift z corresponding to the comoving distance given202 in Mpc.203204 @type distanceMpc: float205 @param distanceMpc: distance in Mpc206 @rtype: float207 @return: redshift208209 """210211dTarget=distanceMpc212213toleranceMpc=0.1214215zMin=0.0216zMax=10.0217218diff=dc(zMax)-dTarget219whilediff<0:220zMax=zMax+5.0221diff=dc(zMax)-dTarget222223zTrial=zMin+(zMax-zMin)/2.0224225dTrial=dc(zTrial)226diff=dTrial-dTarget227whileabs(diff)>toleranceMpc:228229ifdiff>0:230zMax=zMax-(zMax-zMin)/2.0231else:232zMin=zMin+(zMax-zMin)/2.0233234zTrial=zMin+(zMax-zMin)/2.0235dTrial=dc(zTrial)236diff=dTrial-dTarget237238returnzTrial
242"""Calculates the age of the universe in Gyr at z=0 for the current set of243 cosmological parameters.244245 @rtype: float246 @return: age of the universe in Gyr at z=0247248 """249250OMEGA_K=1.0-OMEGA_M0-OMEGA_L0251252# Integration limits253xMax=1.0254xMin=0255256# Function to be integrated257yn=lambdax:(x/math.sqrt(OMEGA_M0*x+OMEGA_L0*math.pow(x,4)+258OMEGA_K*math.pow(x,2)))259260integralValue,integralError=integrate.quad(yn,xMin,xMax)261262T0=(1.0/H0*integralValue*3.08e19)/3.16e7/1e9263264returnT0
268""" Calculates the lookback time in Gyr to redshift z for the current set269 of cosmological parameters.270271 @type z: float272 @param z: redshift273 @rtype: float274 @return: lookback time in Gyr to redshift z275276 """277OMEGA_K=1.0-OMEGA_M0-OMEGA_L0278279# Integration limits280xMax=1.0281xMin=1./(1.+z)282283# Function to be integrated284yn=lambdax:(x/math.sqrt(OMEGA_M0*x+OMEGA_L0*math.pow(x,4)+285OMEGA_K*math.pow(x,2)))286287integralValue,integralError=integrate.quad(yn,xMin,xMax)288289T0=(1.0/H0*integralValue*3.08e19)/3.16e7/1e9290291returnT0
295"""Calculates the age of the universe at redshift z for the current set of296 cosmological parameters.297298 @type z: float299 @param z: redshift300 @rtype: float301 @return: age of the universe in Gyr at redshift z302303 """304305TZ=t0()-tl(z)306307returnTZ
311"""Calculates the redshift z corresponding to lookback time tlGyr given in312 Gyr.313314 @type tlGyr: float315 @param tlGyr: lookback time in Gyr316 @rtype: float317 @return: redshift318319 @note: Raises ValueError if tlGyr is not positive.320321 """322iftlGyr<0.:323raiseValueError('Lookback time must be positive')324325tTarget=tlGyr326327toleranceGyr=0.001328329zMin=0.0330zMax=10.0331332diff=tl(zMax)-tTarget333whilediff<0:334zMax=zMax+5.0335diff=tl(zMax)-tTarget336337zTrial=zMin+(zMax-zMin)/2.0338339tTrial=tl(zTrial)340diff=tTrial-tTarget341whileabs(diff)>toleranceGyr:342343ifdiff>0:344zMax=zMax-(zMax-zMin)/2.0345else:346zMin=zMin+(zMax-zMin)/2.0347348zTrial=zMin+(zMax-zMin)/2.0349tTrial=tl(zTrial)350diff=tTrial-tTarget351352returnzTrial
356"""Calculates the redshift z corresponding to age of the universe tzGyr357 given in Gyr.358359 @type tzGyr: float360 @param tzGyr: age of the universe in Gyr361 @rtype: float362 @return: redshift363364 @note: Raises ValueError if Universe age not positive365366 """367iftzGyr<=0:368raiseValueError('Universe age must be positive.')369tl=t0()-tzGyr370z=tl2z(tl)371372returnz
376"""Calculates the absolute magnitude of an object at given luminosity377 distance in Mpc.378379 @type appMag: float380 @param appMag: apparent magnitude of object381 @type distMpc: float382 @param distMpc: distance to object in Mpc383 @rtype: float384 @return: absolute magnitude of object385386 """387absMag=appMag-(5.0*math.log10(distMpc*1.0e5))388389returnabsMag
393"""Calculates the value of E(z), which describes evolution of the Hubble394 parameter with redshift, at redshift z for the current set of cosmological395 parameters. See, e.g., Bryan & Norman 1998 (ApJ, 495, 80).396397 @type z: float398 @param z: redshift399 @rtype: float400 @return: value of E(z) at redshift z401402 """403404Ez=math.sqrt(Ez2(z))405406returnEz
410"""Calculates the value of E(z)^2, which describes evolution of the Hubble411 parameter with redshift, at redshift z for the current set of cosmological412 parameters. See, e.g., Bryan & Norman 1998 (ApJ, 495, 80).413414 @type z: float415 @param z: redshift416 @rtype: float417 @return: value of E(z)^2 at redshift z418419 """420# This form of E(z) is more reliable at high redshift. It is basically the421# same for all redshifts below 10. But above that, the radiation term422# begins to dominate. From Peebles 1993.423424Ez2=(OMEGA_R0*math.pow(1.0+z,4)+425OMEGA_M0*math.pow(1.0+z,3)+426(1.0-OMEGA_M0-OMEGA_L0)*427math.pow(1.0+z,2)+OMEGA_L0)428429returnEz2
433"""Calculates the matter density of the universe at redshift z. See, e.g.,434 Bryan & Norman 1998 (ApJ, 495, 80).435436 @type z: float437 @param z: redshift438 @rtype: float439 @return: matter density of universe at redshift z440441 """442ez2=Ez2(z)443444Omega_Mz=(OMEGA_M0*math.pow(1.0+z,3))/ez2445446returnOmega_Mz
450""" Calculates the dark energy density of the universe at redshift z.451452 @type z: float453 @param z: redshift454 @rtype: float455 @return: dark energy density of universe at redshift z456457 """458ez2=Ez2(z)459460returnOMEGA_L0/ez2
464""" Calculates the radiation density of the universe at redshift z.465466 @type z: float467 @param z: redshift468 @rtype: float469 @return: radiation density of universe at redshift z470471 """472ez2=Ez2(z)473474returnOMEGA_R0*math.pow(1+z,4)/ez2
478"""Calculates the density contrast of a virialised region S{Delta}V(z),479 assuming a S{Lambda}CDM-type flat cosmology. See, e.g., Bryan & Norman480 1998 (ApJ, 495, 80).481482 @type z: float483 @param z: redshift484 @rtype: float485 @return: density contrast of a virialised region at redshift z486487 @note: If OMEGA_M0+OMEGA_L0 is not equal to 1, this routine exits and488 prints an error489 message to the console.490491 """492493OMEGA_K=1.0-OMEGA_M0-OMEGA_L0494495ifOMEGA_K==0.0:496Omega_Mz=OmegaMz(z)497deltaVz=(18.0*math.pow(math.pi,2)+82.0*(Omega_Mz-1.0)-39.0*498math.pow(Omega_Mz-1,2))499returndeltaVz500else:501raiseException("cosmology is NOT flat.")
505"""Calculates the virial radius (in Mpc) of a galaxy cluster at redshift z506 with X-ray temperature kT, assuming self-similar evolution and a flat507 cosmology. See Arnaud et al. 2002 (A&A, 389, 1) and Bryan & Norman 1998508 (ApJ, 495, 80). A flat S{Lambda}CDM-type flat cosmology is assumed.509510 @type kT: float511 @param kT: cluster X-ray temperature in keV512 @type z: float513 @param z: redshift514 @type betaT: float515 @param betaT: the normalisation of the virial relation, for which Evrard et516 al. 1996 (ApJ,469, 494) find a value of 1.05517 @rtype: float518 @return: virial radius of cluster in Mpc519520 @note: If OMEGA_M0+OMEGA_L0 is not equal to 1, this routine exits and521 prints an error message to the console.522523 """524525OMEGA_K=1.0-OMEGA_M0-OMEGA_L0526527ifOMEGA_K==0.0:528Omega_Mz=OmegaMz(z)529deltaVz=(18.0*math.pow(math.pi,2)+82.0*(Omega_Mz-1.0)-39.0*530math.pow(Omega_Mz-1,2))531deltaz=(deltaVz*OMEGA_M0)/(18.0*math.pow(math.pi,2)*Omega_Mz)532533# The equation quoted in Arnaud, Aghanim & Neumann is for h50, so need534# to scale it535h50=H0/50.0536Rv=(3.80*math.sqrt(betaT)*math.pow(deltaz,-0.5)*537math.pow(1.0+z,(-3.0/2.0))*math.sqrt(kT/10.0)*(1.0/h50))538539returnRv540541else:542raiseException("cosmology is NOT flat.")
1"""module for performing calculations on Spectral Energy Distributions (SEDs) 2 3(c) 2007-2013 Matt Hilton 4 5U{http://astlib.sourceforge.net} 6 7This module provides classes for manipulating SEDs, in particular the Bruzual & Charlot 2003, Maraston 82005, and Percival et al 2009 stellar population synthesis models are currently supported. Functions are 9provided for calculating the evolution of colours and magnitudes in these models with redshift etc., and 10for fitting broadband photometry using these models. 11 12@var VEGA: The SED of Vega, used for calculation of magnitudes on the Vega system. 13@type VEGA: L{SED} object 14@var AB: Flat spectrum SED, used for calculation of magnitudes on the AB system. 15@type AB: L{SED} object 16@var SOL: The SED of the Sun. 17@type SOL: L{SED} object 18 19""" 20 21#------------------------------------------------------------------------------------------------------------ 22importsys 23importnumpy 24importmath 25importoperator 26try: 27fromscipyimportinterpolate 28fromscipyimportndimage 29fromscipyimportoptimize 30except: 31print("WARNING: astSED: failed to import scipy modules - some functions will not work.") 32importastLib 33fromastLibimportastCalc 34importos 35try: 36importmatplotlib 37frommatplotlibimportpylab 38matplotlib.interactive(False) 39except: 40print("WARNING: astSED: failed to import matplotlib - some functions will not work.") 41importglob 42 43#------------------------------------------------------------------------------------------------------------
45"""This class describes a filter transmission curve. Passband objects are created by loading data from 46 from text files containing wavelength in angstroms in the first column, relative transmission efficiency 47 in the second column (whitespace delimited). For example, to create a Passband object for the 2MASS J 48 filter: 49 50 passband=astSED.Passband("J_2MASS.res") 51 52 where "J_2MASS.res" is a file in the current working directory that describes the filter. 53 54 Wavelength units can be specified as 'angstroms', 'nanometres' or 'microns'; if either of the latter, 55 they will be converted to angstroms. 56 57 """
103"""Returns a two dimensional list of [wavelength, transmission], suitable for plotting by gnuplot. 104 105 @rtype: list 106 @return: list in format [wavelength, transmission] 107 108 """ 109 110listData=[] 111forl,finzip(self.wavelength,self.transmission): 112listData.append([l,f]) 113 114returnlistData
117"""Rescales the passband so that maximum value of the transmission is equal to maxTransmission. 118 Useful for plotting. 119 120 @type maxTransmission: float 121 @param maxTransmission: maximum value of rescaled transmission curve 122 123 """ 124 125self.transmission=self.transmission*(maxTransmission/self.transmission.max())
128"""Plots the passband, rescaling the maximum of the tranmission curve to maxTransmission if 129 required. 130 131 @type xmin: float or 'min' 132 @param xmin: minimum of the wavelength range of the plot 133 @type xmax: float or 'max' 134 @param xmax: maximum of the wavelength range of the plot 135 @type maxTransmission: float 136 @param maxTransmission: maximum value of rescaled transmission curve 137 138 """ 139 140ifmaxTransmission!=None: 141self.rescale(maxTransmission) 142 143pylab.matplotlib.interactive(True) 144pylab.plot(self.wavelength,self.transmission) 145 146ifxmin=='min': 147xmin=self.wavelength.min() 148ifxmax=='max': 149xmax=self.wavelength.max() 150 151pylab.xlim(xmin,xmax) 152pylab.xlabel("Wavelength") 153pylab.ylabel("Relative Flux")
156"""Calculates effective wavelength for the passband. This is the same as equation (3) of 157 Carter et al. 2009. 158 159 @rtype: float 160 @return: effective wavelength of the passband, in Angstroms 161 162 """ 163 164a=numpy.trapz(self.transmission*self.wavelength,self.wavelength) 165b=numpy.trapz(self.transmission/self.wavelength,self.wavelength) 166effWavelength=numpy.sqrt(a/b) 167 168returneffWavelength
177"""Generates a passband object with top hat response between wavelengthMin, wavelengthMax. 178 Units are assumed to be Angstroms. 179 180 @type wavelengthMin: float 181 @param wavelengthMin: minimum of the wavelength range of the passband 182 @type wavelengthMax: float 183 @param wavelengthMax: maximum of the wavelength range of the passband 184 @type normalise: bool 185 @param normalise: if True, scale such that total area under the passband over the wavelength 186 range is 1. 187 188 """ 189 190self.wavelength=numpy.arange(wavelengthMin,wavelengthMax+10,10,dtype=float) 191self.transmission=numpy.ones(self.wavelength.shape,dtype=float) 192 193ifnormalise==True: 194self.transmission=self.transmission/numpy.trapz(self.transmission,self.wavelength) 195 196# Store a ready-to-go interpolation object to speed calculation of fluxes up 197self.interpolator=interpolate.interp1d(self.wavelength,self.transmission,kind='linear')
202"""This class describes a Spectral Energy Distribution (SED). 203 204 To create a SED object, lists (or numpy arrays) of wavelength and relative flux must be provided. The SED 205 can optionally be redshifted. The wavelength units of SEDs are assumed to be Angstroms - flux 206 calculations using Passband and SED objects specified with different wavelength units will be incorrect. 207 208 The L{StellarPopulation} class (and derivatives) can be used to extract SEDs for specified ages from e.g. 209 the Bruzual & Charlot 2003 or Maraston 2005 models. 210 211 """ 212
214 215# We keep a copy of the wavelength, flux at z = 0, as it's more robust to copy that 216# to self.wavelength, flux and redshift it, rather than repeatedly redshifting the same 217# arrays back and forth 218self.z0wavelength=numpy.array(wavelength) 219self.z0flux=numpy.array(flux) 220self.wavelength=numpy.array(wavelength) 221self.flux=numpy.array(flux) 222self.z=z 223self.label=label# plain text label, handy for using in photo-z codes 224 225# Store the intrinsic (i.e. unextincted) flux in case we change extinction 226self.EBMinusV=0.0 227self.intrinsic_z0flux=numpy.array(flux) 228 229ifnormalise==True: 230self.normalise() 231 232ifz!=0.0: 233self.redshift(z) 234 235self.ageGyr=ageGyr
239"""Copies the SED, returning a new SED object 240 241 @rtype: L{SED} object 242 @return: SED 243 244 """ 245 246newSED=SED(wavelength=self.z0wavelength,flux=self.z0flux,z=self.z,ageGyr=self.ageGyr, 247normalise=False,label=self.label) 248 249returnnewSED
253"""Loads SED from a white space delimited file in the format wavelength, flux. Lines beginning with 254 # are ignored. 255 256 @type fileName: string 257 @param fileName: path to file containing wavelength, flux data 258 259 """ 260 261inFile=open(fileName,"r") 262lines=inFile.readlines() 263inFile.close() 264wavelength=[] 265flux=[] 266wholeLines=[] 267forlineinlines: 268ifline[0]!="#"andlen(line)>3: 269bits=line.split() 270wavelength.append(float(bits[0])) 271flux.append(float(bits[1])) 272 273# Sort SED so wavelength is in ascending order 274ifwavelength[0]>wavelength[-1]: 275wavelength.reverse() 276flux.reverse() 277 278self.z0wavelength=numpy.array(wavelength) 279self.z0flux=numpy.array(flux) 280self.wavelength=numpy.array(wavelength) 281self.flux=numpy.array(flux)
284"""Writes SED to a white space delimited file in the format wavelength, flux. 285 286 @type fileName: string 287 @param fileName: path to file 288 289 """ 290 291outFile=open(fileName,"w") 292forl,finzip(self.wavelength,self.flux): 293outFile.write(str(l)+" "+str(f)+"\n") 294outFile.close()
297"""Returns a two dimensional list of [wavelength, flux], suitable for plotting by gnuplot. 298 299 @rtype: list 300 @return: list in format [wavelength, flux] 301 302 """ 303 304listData=[] 305forl,finzip(self.wavelength,self.flux): 306listData.append([l,f]) 307 308returnlistData
311"""Produces a simple (wavelength, flux) plot of the SED. 312 313 @type xmin: float or 'min' 314 @param xmin: minimum of the wavelength range of the plot 315 @type xmax: float or 'max' 316 @param xmax: maximum of the wavelength range of the plot 317 318 """ 319 320pylab.matplotlib.interactive(True) 321pylab.plot(self.wavelength,self.flux) 322 323ifxmin=='min': 324xmin=self.wavelength.min() 325ifxmax=='max': 326xmax=self.wavelength.max() 327 328# Sensible y scale 329plotMask=numpy.logical_and(numpy.greater(self.wavelength,xmin),numpy.less(self.wavelength,xmax)) 330plotMax=self.flux[plotMask].max() 331pylab.ylim(0,plotMax*1.1) 332pylab.xlim(xmin,xmax) 333pylab.xlabel("Wavelength") 334pylab.ylabel("Relative Flux")
337"""Calculates flux in SED within given wavelength range. 338 339 @type wavelengthMin: float or 'min' 340 @param wavelengthMin: minimum of the wavelength range 341 @type wavelengthMax: float or 'max' 342 @param wavelengthMax: maximum of the wavelength range 343 @rtype: float 344 @return: relative flux 345 346 """ 347 348ifwavelengthMin=='min': 349wavelengthMin=self.wavelength.min() 350ifwavelengthMax=='max': 351wavelengthMax=self.wavelength.max() 352 353mask=numpy.logical_and(numpy.greater(self.wavelength,wavelengthMin), \ 354numpy.less(self.wavelength,wavelengthMax)) 355flux=numpy.trapz(self.flux[mask],self.wavelength[mask]) 356 357returnflux
360"""Smooths SED.flux with a uniform (boxcar) filter of width smoothPix. Cannot be undone. 361 362 @type smoothPix: int 363 @param smoothPix: size of uniform filter applied to SED, in pixels 364 365 """ 366smoothed=ndimage.uniform_filter1d(self.flux,smoothPix) 367self.flux=smoothed
370"""Redshifts the SED to redshift z. 371 372 @type z: float 373 @param z: redshift 374 375 """ 376 377# We have to conserve energy so the area under the redshifted SED has to be equal to 378# the area under the unredshifted SED, otherwise magnitude calculations will be wrong 379# when comparing SEDs at different zs 380self.wavelength=numpy.zeros(self.z0wavelength.shape[0]) 381self.flux=numpy.zeros(self.z0flux.shape[0]) 382self.wavelength=self.wavelength+self.z0wavelength 383self.flux=self.flux+self.z0flux 384 385z0TotalFlux=numpy.trapz(self.z0wavelength,self.z0flux) 386self.wavelength=self.wavelength*(1.0+z) 387zTotalFlux=numpy.trapz(self.wavelength,self.flux) 388self.flux=self.flux*(z0TotalFlux/zTotalFlux) 389self.z=z
392"""Normalises the SED such that the area under the specified wavelength range is equal to 1. 393 394 @type minWavelength: float or 'min' 395 @param minWavelength: minimum wavelength of range over which to normalise SED 396 @type maxWavelength: float or 'max' 397 @param maxWavelength: maximum wavelength of range over which to normalise SED 398 399 """ 400ifminWavelength=='min': 401minWavelength=self.wavelength.min() 402ifmaxWavelength=='max': 403maxWavelength=self.wavelength.max() 404 405lowCut=numpy.greater(self.wavelength,minWavelength) 406highCut=numpy.less(self.wavelength,maxWavelength) 407totalCut=numpy.logical_and(lowCut,highCut) 408sedFluxSlice=self.flux[totalCut] 409sedWavelengthSlice=self.wavelength[totalCut] 410 411self.flux=self.flux/numpy.trapz(abs(sedFluxSlice),sedWavelengthSlice)#self.wavelength)
414"""Normalises the SED to match the flux equivalent to the given AB magnitude in the given passband. 415 416 @type ABMag: float 417 @param ABMag: AB magnitude to which the SED is to be normalised at the given passband 418 @type passband: an L{Passband} object 419 @param passband: passband at which normalisation to AB magnitude is calculated 420 421 """ 422 423magFlux=mag2Flux(ABMag,0.0,passband) 424sedFlux=self.calcFlux(passband) 425norm=magFlux[0]/sedFlux 426self.flux=self.flux*norm 427self.z0flux=self.z0flux*norm
430"""Matches the flux in the wavelength range given by minWavelength, maxWavelength to the 431 flux in the same region in matchSED. Useful for plotting purposes. 432 433 @type matchSED: L{SED} object 434 @param matchSED: SED to match flux to 435 @type minWavelength: float 436 @param minWavelength: minimum of range in which to match flux of current SED to matchSED 437 @type maxWavelength: float 438 @param maxWavelength: maximum of range in which to match flux of current SED to matchSED 439 440 """ 441 442interpMatch=interpolate.interp1d(matchSED.wavelength,matchSED.flux,kind='linear') 443interpSelf=interpolate.interp1d(self.wavelength,self.flux,kind='linear') 444 445wavelengthRange=numpy.arange(minWavelength,maxWavelength,5.0) 446 447matchFlux=numpy.trapz(interpMatch(wavelengthRange),wavelengthRange) 448selfFlux=numpy.trapz(interpSelf(wavelengthRange),wavelengthRange) 449 450self.flux=self.flux*(matchFlux/selfFlux)
454"""Calculates flux in the given passband. 455 456 @type passband: L{Passband} object 457 @param passband: filter passband through which to calculate the flux from the SED 458 @rtype: float 459 @return: flux 460 461 """ 462lowCut=numpy.greater(self.wavelength,passband.wavelength.min()) 463highCut=numpy.less(self.wavelength,passband.wavelength.max()) 464totalCut=numpy.logical_and(lowCut,highCut) 465sedFluxSlice=self.flux[totalCut] 466sedWavelengthSlice=self.wavelength[totalCut] 467 468# Use linear interpolation to rebin the passband to the same dimensions as the 469# part of the SED we're interested in 470sedInBand=passband.interpolator(sedWavelengthSlice)*sedFluxSlice 471totalFlux=numpy.trapz(sedInBand*sedWavelengthSlice,sedWavelengthSlice) 472totalFlux=totalFlux/numpy.trapz(passband.interpolator(sedWavelengthSlice)\ 473*sedWavelengthSlice,sedWavelengthSlice) 474 475returntotalFlux
478"""Calculates magnitude in the given passband. If addDistanceModulus == True, 479 then the distance modulus (5.0*log10*(dl*1e5), where dl is the luminosity distance 480 in Mpc at the redshift of the L{SED}) is added. 481 482 @type passband: L{Passband} object 483 @param passband: filter passband through which to calculate the magnitude from the SED 484 @type addDistanceModulus: bool 485 @param addDistanceModulus: if True, adds 5.0*log10*(dl*1e5) to the mag returned, where 486 dl is the luminosity distance (Mpc) corresponding to the SED z 487 @type magType: string 488 @param magType: either "Vega" or "AB" 489 @rtype: float 490 @return: magnitude through the given passband on the specified magnitude system 491 492 """ 493f1=self.calcFlux(passband) 494ifmagType=="Vega": 495f2=VEGA.calcFlux(passband) 496elifmagType=="AB": 497f2=AB.calcFlux(passband) 498 499mag=-2.5*math.log10(f1/f2) 500ifmagType=="Vega": 501mag=mag+0.026# Add 0.026 because Vega has V=0.026 (e.g. Bohlin & Gilliland 2004) 502 503ifself.z>0.0andaddDistanceModulus==True: 504appMag=5.0*math.log10(astCalc.dl(self.z)*1e5)+mag 505else: 506appMag=mag 507 508returnappMag
511"""Calculates the colour passband1-passband2. 512 513 @type passband1: L{Passband} object 514 @param passband1: filter passband through which to calculate the first magnitude 515 @type passband2: L{Passband} object 516 @param passband1: filter passband through which to calculate the second magnitude 517 @type magType: string 518 @param magType: either "Vega" or "AB" 519 @rtype: float 520 @return: colour defined by passband1 - passband2 on the specified magnitude system 521 522 """ 523mag1=self.calcMag(passband1,magType=magType,addDistanceModulus=True) 524mag2=self.calcMag(passband2,magType=magType,addDistanceModulus=True) 525 526colour=mag1-mag2 527returncolour
530"""This is a convenience function for pulling out fluxes from a SED for a given set of passbands 531 in the same format as made by L{mags2SEDDict} - designed to make fitting code simpler. 532 533 @type passbands: list of L{Passband} objects 534 @param passbands: list of passbands through which fluxes will be calculated 535 536 """ 537 538flux=[] 539wavelength=[] 540forpinpassbands: 541flux.append(self.calcFlux(p)) 542wavelength.append(p.effectiveWavelength()) 543 544SEDDict={} 545SEDDict['flux']=numpy.array(flux) 546SEDDict['wavelength']=numpy.array(wavelength) 547 548returnSEDDict
551"""Applies the Calzetti et al. 2000 (ApJ, 533, 682) extinction law to the SED with the given 552 E(B-V) amount of extinction. R_v' = 4.05 is assumed (see equation (5) of Calzetti et al.). 553 554 @type EBMinusV: float 555 @param EBMinusV: extinction E(B-V), in magnitudes 556 557 """ 558 559self.EBMinusV=EBMinusV 560 561# All done in rest frame 562self.z0flux=self.intrinsic_z0flux 563 564# Allow us to set EBMinusV == 0 to turn extinction off 565ifEBMinusV>0: 566# Note that EBMinusV is assumed to be Es as in equations (2) - (5) 567# Note here wavelength units have to be microns for constants to make sense 568RvPrime=4.05# equation (5) of Calzetti et al. 2000 569shortWavelengthMask=numpy.logical_and(numpy.greater_equal(self.z0wavelength,1200), \ 570numpy.less(self.z0wavelength,6300)) 571longWavelengthMask=numpy.logical_and(numpy.greater_equal(self.z0wavelength,6300), \ 572numpy.less_equal(self.z0wavelength,22000)) 573wavelengthMicrons=numpy.array(self.z0wavelength/10000.0,dtype=numpy.float64) 574kPrime=numpy.zeros(self.z0wavelength.shape[0],dtype=numpy.float64) 575kPrimeLong=(2.659*(-1.857 \ 576+1.040/wavelengthMicrons \ 577))+RvPrime 578kPrimeShort=(2.659*(-2.156 \ 579+1.509/wavelengthMicrons \ 580-0.198/wavelengthMicrons**2 \ 581+0.011/wavelengthMicrons**3 \ 582))+RvPrime 583kPrime[longWavelengthMask]=kPrimeLong[longWavelengthMask] 584kPrime[shortWavelengthMask]=kPrimeShort[shortWavelengthMask] 585 586# Here we extrapolate kPrime in similar way to what HYPERZ does 587# Short wavelengths 588try: 589interpolator=interpolate.interp1d(self.z0wavelength,kPrimeShort,kind='linear') 590slope=(interpolator(1100.0)-interpolator(1200.0))/(1100.0-1200.0) 591intercept=interpolator(1200.0)-(slope*1200.0) 592mask=numpy.less(self.z0wavelength,1200.0) 593kPrime[mask]=slope*self.z0wavelength[mask]+intercept 594 595# Long wavelengths 596interpolator=interpolate.interp1d(self.z0wavelength,kPrimeLong,kind='linear') 597slope=(interpolator(21900.0)-interpolator(22000.0))/(21900.0-22000.0) 598intercept=interpolator(21900.0)-(slope*21900.0) 599mask=numpy.greater(self.z0wavelength,22000.0) 600kPrime[mask]=slope*self.z0wavelength[mask]+intercept 601except: 602raiseException("This SED has a wavelength range that doesn't cover ~1200-22000 Angstroms") 603 604# Never let go negative 605kPrime[numpy.less_equal(kPrime,0.0)]=1e-6 606 607reddening=numpy.power(10,0.4*EBMinusV*kPrime) 608self.z0flux=self.z0flux/reddening 609 610self.redshift(self.z)
614"""This class stores the SED of Vega, used for calculation of magnitudes on the Vega system. 615 616 The Vega SED used is taken from Bohlin 2007 (http://adsabs.harvard.edu/abs/2007ASPC..364..315B), and is 617 available from the STScI CALSPEC library (http://www.stsci.edu/hst/observatory/cdbs/calspec.html). 618 619 """ 620
652"""This class describes a stellar population model, either a Simple Stellar Population (SSP) or a 653 Composite Stellar Population (CSP), such as the models of Bruzual & Charlot 2003 or Maraston 2005. 654 655 The constructor for this class can be used for generic SSPs or CSPs stored in white space delimited text 656 files, containing columns for age, wavelength, and flux. Columns are counted from 0 ... n. Lines starting 657 with # are ignored. 658 659 The classes L{M05Model} (for Maraston 2005 models), L{BC03Model} (for Bruzual & Charlot 2003 models), and 660 L{P09Model} (for Percival et al. 2009 models) are derived from this class. The only difference between 661 them is the code used to load in the model data. 662 663 """
665 666inFile=open(fileName,"r") 667lines=inFile.readlines() 668inFile.close() 669 670self.fileName=fileName 671 672# Extract a list of model ages and valid wavelengths from the file 673self.ages=[] 674self.wavelengths=[] 675forlineinlines: 676ifline[0]!="#"andlen(line)>3: 677bits=line.split() 678age=float(bits[ageColumn]) 679wavelength=float(bits[wavelengthColumn]) 680ifagenotinself.ages: 681self.ages.append(age) 682ifwavelengthnotinself.wavelengths: 683self.wavelengths.append(wavelength) 684 685# Construct a grid of flux - rows correspond to each wavelength, columns to age 686self.fluxGrid=numpy.zeros([len(self.ages),len(self.wavelengths)]) 687forlineinlines: 688ifline[0]!="#"andlen(line)>3: 689bits=line.split() 690sedAge=float(bits[ageColumn]) 691sedWavelength=float(bits[wavelengthColumn]) 692sedFlux=float(bits[fluxColumn]) 693 694row=self.ages.index(sedAge) 695column=self.wavelengths.index(sedWavelength) 696 697self.fluxGrid[row][column]=sedFlux
700"""Extract a SED for given age. Do linear interpolation between models if necessary. 701 702 @type ageGyr: float 703 @param ageGyr: age of the SED in Gyr 704 @type z: float 705 @param z: redshift the SED from z = 0 to z = z 706 @type normalise: bool 707 @param normalise: normalise the SED to have area 1 708 @rtype: L{SED} object 709 @return: SED 710 711 """ 712 713ifageGyrinself.ages: 714 715flux=self.fluxGrid[self.ages.index(ageGyr)] 716sed=SED(self.wavelengths,flux,z=z,normalise=normalise,label=label) 717returnsed 718 719else: 720 721# Use interpolation, iterating over each wavelength column 722flux=[] 723foriinrange(len(self.wavelengths)): 724interpolator=interpolate.interp1d(self.ages,self.fluxGrid[:,i],kind='linear') 725sedFlux=interpolator(ageGyr) 726flux.append(sedFlux) 727sed=SED(self.wavelengths,flux,z=z,normalise=normalise,label=label) 728returnsed
731"""Calculates the evolution of the colour observed through passband1 - passband2 for the 732 StellarPopulation with redshift, from z = 0 to z = zFormation. 733 734 @type passband1: L{Passband} object 735 @param passband1: filter passband through which to calculate the first magnitude 736 @type passband2: L{Passband} object 737 @param passband2: filter passband through which to calculate the second magnitude 738 @type zFormation: float 739 @param zFormation: formation redshift of the StellarPopulation 740 @type zStepSize: float 741 @param zStepSize: size of interval in z at which to calculate model colours 742 @type magType: string 743 @param magType: either "Vega" or "AB" 744 @rtype: dictionary 745 @return: dictionary of numpy.arrays in format {'z', 'colour'} 746 747 """ 748 749zSteps=int(math.ceil(zFormation/zStepSize)) 750zData=[] 751colourData=[] 752foriinrange(1,zSteps): 753zc=i*zStepSize 754age=astCalc.tl(zFormation)-astCalc.tl(zc) 755sed=self.getSED(age,z=zc) 756colour=sed.calcColour(passband1,passband2,magType=magType) 757zData.append(zc) 758colourData.append(colour) 759 760zData=numpy.array(zData) 761colourData=numpy.array(colourData) 762 763return{'z':zData,'colour':colourData}
767"""Calculates the evolution with redshift (from z = 0 to z = zFormation) of apparent magnitude 768 in the observed frame through the passband for the StellarPopulation, normalised to magNormalisation 769 (apparent) at z = zNormalisation. 770 771 @type passband: L{Passband} object 772 @param passband: filter passband through which to calculate the magnitude 773 @type magNormalisation: float 774 @param magNormalisation: sets the apparent magnitude of the SED at zNormalisation 775 @type zNormalisation: float 776 @param zNormalisation: the redshift at which the magnitude normalisation is carried out 777 @type zFormation: float 778 @param zFormation: formation redshift of the StellarPopulation 779 @type zStepSize: float 780 @param zStepSize: size of interval in z at which to calculate model magnitudes 781 @type onePlusZSteps: bool 782 @param onePlusZSteps: if True, zSteps are (1+z)*zStepSize, otherwise zSteps are linear 783 @type magType: string 784 @param magType: either "Vega" or "AB" 785 @rtype: dictionary 786 @return: dictionary of numpy.arrays in format {'z', 'mag'} 787 788 """ 789 790# Count upwards in z steps as interpolation doesn't work if array ordered z decreasing 791zSteps=int(math.ceil(zFormation/zStepSize)) 792zData=[] 793magData=[] 794absMagData=[] 795zc0=0.0 796foriinrange(1,zSteps): 797ifonePlusZSteps==False: 798zc=i*zStepSize 799else: 800zc=zc0+(1+zc0)*zStepSize 801zc0=zc 802ifzc>=zFormation: 803break 804age=astCalc.tl(zFormation)-astCalc.tl(zc) 805sed=self.getSED(age,z=zc) 806mag=sed.calcMag(passband,magType=magType,addDistanceModulus=True) 807zData.append(zc) 808magData.append(mag) 809absMagData.append(sed.calcMag(passband,addDistanceModulus=False)) 810 811zData=numpy.array(zData) 812magData=numpy.array(magData) 813 814# Do the normalisation 815interpolator=interpolate.interp1d(zData,magData,kind='linear') 816modelNormMag=interpolator(zNormalisation) 817normConstant=magNormalisation-modelNormMag 818magData=magData+normConstant 819 820return{'z':zData,'mag':magData}
823"""Calculates the evolution correction in magnitudes in the rest frame through the passband 824 from redshift zFrom to redshift zTo, where the stellarPopulation is assumed to be formed 825 at redshift zFormation. 826 827 @type zFrom: float 828 @param zFormation: redshift to evolution correct from 829 @type zTo: float 830 @param zTo: redshift to evolution correct to 831 @type zFormation: float 832 @param zFormation: formation redshift of the StellarPopulation 833 @type passband: L{Passband} object 834 @param passband: filter passband through which to calculate magnitude 835 @type magType: string 836 @param magType: either "Vega" or "AB" 837 @rtype: float 838 @return: evolution correction in magnitudes in the rest frame 839 840 """ 841 842ageFrom=astCalc.tl(zFormation)-astCalc.tl(zFrom) 843ageTo=astCalc.tl(zFormation)-astCalc.tl(zTo) 844 845fromSED=self.getSED(ageFrom) 846toSED=self.getSED(ageTo) 847 848fromMag=fromSED.calcMag(passband,magType=magType,addDistanceModulus=False) 849toMag=toSED.calcMag(passband,magType=magType,addDistanceModulus=False) 850 851returnfromMag-toMag
855"""This class describes a Maraston 2005 stellar population model. To load a composite stellar population 856 model (CSP) for a tau = 0.1 Gyr burst of star formation, solar metallicity, Salpeter IMF: 857 858 m05csp = astSED.M05Model(M05_DIR+"/csp_e_0.10_z02_salp.sed_agb") 859 860 where M05_DIR is set to point to the directory where the Maraston 2005 models are stored on your system. 861 862 The file format of the Maraston 2005 simple stellar poulation (SSP) models is different to the file 863 format used for the CSPs, and this needs to be specified using the fileType parameter. To load a SSP with 864 solar metallicity, red horizontal branch morphology: 865 866 m05ssp = astSED.M05Model(M05_DIR+"/sed.ssz002.rhb", fileType = "ssp") 867 868 The wavelength units of SEDs from M05 models are Angstroms, with flux in units of erg/s/Angstrom. 869 870 """
872 873self.modelFamily="M05" 874 875inFile=open(fileName,"r") 876lines=inFile.readlines() 877inFile.close() 878 879self.fileName=fileName 880 881iffileType=="csp": 882ageColumn=0 883wavelengthColumn=1 884fluxColumn=2 885eliffileType=="ssp": 886ageColumn=0 887wavelengthColumn=2 888fluxColumn=3 889else: 890raiseException("fileType must be 'ssp' or 'csp'") 891 892# Extract a list of model ages and valid wavelengths from the file 893self.ages=[] 894self.wavelengths=[] 895forlineinlines: 896ifline[0]!="#"andlen(line)>3: 897bits=line.split() 898age=float(bits[ageColumn]) 899wavelength=float(bits[wavelengthColumn]) 900ifagenotinself.ages: 901self.ages.append(age) 902ifwavelengthnotinself.wavelengths: 903self.wavelengths.append(wavelength) 904 905# Construct a grid of flux - rows correspond to each wavelength, columns to age 906self.fluxGrid=numpy.zeros([len(self.ages),len(self.wavelengths)]) 907forlineinlines: 908ifline[0]!="#"andlen(line)>3: 909bits=line.split() 910sedAge=float(bits[ageColumn]) 911sedWavelength=float(bits[wavelengthColumn]) 912sedFlux=float(bits[fluxColumn]) 913 914row=self.ages.index(sedAge) 915column=self.wavelengths.index(sedWavelength) 916 917self.fluxGrid[row][column]=sedFlux
921"""This class describes a Bruzual & Charlot 2003 stellar population model, extracted from a GALAXEV .ised 922 file using the galaxevpl program that is included in GALAXEV. The file format is white space delimited, 923 with wavelength in the first column. Subsequent columns contain the model fluxes for SEDs of different 924 ages, as specified when running galaxevpl. The age corresponding to each flux column is taken from the 925 comment line beginning "# Age (yr)", and is converted to Gyr. 926 927 For example, to load a tau = 0.1 Gyr burst of star formation, solar metallicity, Salpeter IMF model 928 stored in a file (created by galaxevpl) called "csp_lr_solar_0p1Gyr.136": 929 930 bc03model = BC03Model("csp_lr_solar_0p1Gyr.136") 931 932 The wavelength units of SEDs from BC03 models are Angstroms. Flux is converted into units of 933 erg/s/Angstrom (the units in the files output by galaxevpl are LSun/Angstrom). 934 935 """ 936
938 939self.modelFamily="BC03" 940self.fileName=fileName 941 942inFile=open(fileName,"r") 943lines=inFile.readlines() 944inFile.close() 945 946# Extract a list of model ages - BC03 ages are in years, so convert to Gyr 947self.ages=[] 948forlineinlines: 949ifline.find("# Age (yr)")!=-1: 950rawAges=line[line.find("# Age (yr)")+10:].split() 951forageinrawAges: 952self.ages.append(float(age)/1e9) 953 954# Extract a list of valid wavelengths from the file 955# If we have many ages in the file, this is more complicated... 956lambdaLinesCount=0 957startFluxDataLine=None 958foriinrange(len(lines)): 959line=lines[i] 960if"# Lambda(A)"inline: 961lambdaLinesCount=lambdaLinesCount+1 962ifline[0]!="#"andlen(line)>3andstartFluxDataLine==None: 963startFluxDataLine=i 964self.wavelengths=[] 965foriinrange(startFluxDataLine,len(lines),lambdaLinesCount): 966line=lines[i] 967bits=line.split() 968self.wavelengths.append(float(bits[0])) 969 970# Construct a grid of flux - rows correspond to each wavelength, columns to age 971self.fluxGrid=numpy.zeros([len(self.ages),len(self.wavelengths)]) 972foriinrange(startFluxDataLine,len(lines),lambdaLinesCount): 973line=lines[i] 974bits=[] 975forkinrange(i,i+lambdaLinesCount): 976bits=bits+lines[k].split() 977ageFluxes=bits[1:] 978sedWavelength=float(bits[0]) 979column=self.wavelengths.index(sedWavelength) 980forrowinrange(len(ageFluxes)): 981sedFlux=float(ageFluxes[row]) 982self.fluxGrid[row][column]=sedFlux 983 984# Convert flux into erg/s/Angstrom - native units of galaxevpl files are LSun/Angstrom 985self.fluxGrid=self.fluxGrid*3.826e33
989"""This class describes a Percival et al 2009 (BaSTI; http://albione.oa-teramo.inaf.it) stellar 990 population model. We assume that the synthetic spectra for each model are unpacked under the directory 991 pointed to by fileName. 992 993 The wavelength units of SEDs from P09 models are converted to Angstroms. Flux is converted into units of 994 erg/s/Angstrom (the units in the BaSTI low-res spectra are 4.3607e-33 erg/s/m). 995 996 """ 997
9991000self.modelFamily="P09"10011002files=glob.glob(fileName+os.path.sep+"*.t??????")10031004self.fileName=fileName10051006# Map end of filenames to ages in Gyr1007extensionAgeMap={}1008self.ages=[]1009forfinfiles:1010ext=f.split(".")[-1]1011ageGyr=float(f[-5:])/1e31012self.ages.append(ageGyr)1013extensionAgeMap[ext]=ageGyr1014self.ages.sort()10151016# Construct a grid of flux - rows correspond to each wavelength, columns to age1017self.wavelengths=None1018self.fluxGrid=None1019foriinrange(len(self.ages)):1020foreinextensionAgeMap.keys():1021ifextensionAgeMap[e]==self.ages[i]:1022inFileName=glob.glob(fileName+os.path.sep+"*."+e)[0]1023inFile=open(inFileName,"r")1024lines=inFile.readlines()1025inFile.close()1026wavelength=[]1027flux=[]1028forlineinlines:1029bits=line.split()1030wavelength.append(float(bits[0])*10.0)# units in file are nm, not angstroms1031flux.append(float(bits[1]))1032ifself.wavelengths==None:1033self.wavelengths=wavelength1034ifself.fluxGrid==None:1035self.fluxGrid=numpy.zeros([len(self.ages),len(self.wavelengths)])1036self.fluxGrid[i]=flux10371038# Convert flux into erg/s/Angstrom - native units in BaSTI files are 4.3607e-33 erg/s/m1039self.fluxGrid=self.fluxGrid/4.3607e-33/1e10
1043"""This routine makes a list of SEDDict dictionaries (see L{mags2SEDDict}) for fitting using 1044 L{fitSEDDict}. This speeds up the fitting as this allows us to calculate model SED magnitudes only once, 1045 if all objects to be fitted are at the same redshift. We add some meta data to the modelSEDDicts (e.g.1046 the model file names).10471048 The effect of extinction by dust (assuming the Calzetti et al. 2000 law) can be included by giving a list 1049 of E(B-V) values.10501051 If forceYoungerThanUniverse == True, ages which are older than the universe at the given z will not be1052 included.10531054 @type modelList: list of L{StellarPopulation} model objects1055 @param modelList: list of StellarPopulation models to include1056 @type z: float1057 @param z: redshift to apply to all stellar population models in modelList1058 @type EBMinusVList: list1059 @param EBMinusVList: list of E(B-V) extinction values to apply to all models, in magnitudes1060 @type labelsList: list1061 @param labelsList: optional list used for labelling passbands in output SEDDicts1062 @type forceYoungerThanUniverse: bool1063 @param forceYoungerThanUniverse: if True, do not allow models that exceed the age of the universe at z1064 @rtype: list1065 @return: list of dictionaries containing model fluxes, to be used as input to L{fitSEDDict}.10661067 """10681069# Otherwise if this is the case we won't actually make any model SEDDicts ...1070ifEBMinusVList==[]:1071EBMinusVList=[0.0]10721073modelSEDDictList=[]1074forminrange(len(modelList)):1075testAges=numpy.array(modelList[m].ages)1076ifforceYoungerThanUniverse==True:1077testAges=testAges[numpy.logical_and(numpy.less(testAges,astCalc.tz(z)),numpy.greater(testAges,0))]1078fortintestAges:1079s=modelList[m].getSED(t,z=z,label=modelList[m].fileName+" - age="+str(t)+" Gyr")1080forEBMinusVinEBMinusVList:1081try:1082s.extinctionCalzetti(EBMinusV)1083except:1084raiseException("Model %s has a wavelength range that doesn't cover ~1200-22000 Angstroms"%(modelList[m].fileName))1085modelSEDDict=s.getSEDDict(passbandsList)1086modelSEDDict['labels']=labelsList1087modelSEDDict['E(B-V)']=EBMinusV1088modelSEDDict['ageGyr']=t1089modelSEDDict['z']=z1090modelSEDDict['fileName']=modelList[m].fileName1091modelSEDDict['modelListIndex']=m1092modelSEDDictList.append(modelSEDDict)10931094returnmodelSEDDictList
1098"""Fits the given SED dictionary (made using L{mags2SEDDict}) with the given list of model SED 1099 dictionaries. The latter should be made using L{makeModelSEDDictList}, and entries for fluxes should1100 correspond directly between the model and SEDDict.11011102 Returns a dictionary with best fit values.11031104 @type SEDDict: dictionary, in format of L{mags2SEDDict}1105 @param SEDDict: dictionary of observed fluxes and uncertainties, in format of L{mags2SEDDict}1106 @type modelSEDDictList: list of dictionaries, in format of L{makeModelSEDDictList}1107 @param modelSEDDictList: list of dictionaries containing fluxes of models to be fitted to the observed1108 fluxes listed in the SEDDict. This should be made using L{makeModelSEDDictList}.1109 @rtype: dictionary1110 @return: results of the fitting - keys: 1111 - 'minChiSq': minimum chi squared value of best fit1112 - 'chiSqContrib': corresponding contribution at each passband to the minimum chi squared value1113 - 'ageGyr': the age in Gyr of the best fitting model1114 - 'modelFileName': the file name of the stellar population model corresponding to the best fit1115 - 'modelListIndex': the index of the best fitting model in the input modelSEDDictList1116 - 'norm': the normalisation that the best fit model should be multiplied by to match the SEDDict1117 - 'z': the redshift of the best fit model1118 - 'E(B-V)': the extinction, E(B-V), in magnitudes, of the best fit model11191120 """11211122modelFlux=[]1123formodelSEDDictinmodelSEDDictList:1124modelFlux.append(modelSEDDict['flux'])1125modelFlux=numpy.array(modelFlux)1126sedFlux=numpy.array([SEDDict['flux']]*len(modelSEDDictList))1127sedFluxErr=numpy.array([SEDDict['fluxErr']]*len(modelSEDDictList))11281129# Analytic expression below is for normalisation at minimum chi squared (see note book)1130norm=numpy.sum((modelFlux*sedFlux)/(sedFluxErr**2),axis=1)/numpy.sum(modelFlux**2/sedFluxErr**2,axis=1)1131norms=numpy.array([norm]*modelFlux.shape[1]).transpose()1132chiSq=numpy.sum(((sedFlux-norms*modelFlux)**2)/sedFluxErr**2,axis=1)1133chiSq[numpy.isnan(chiSq)]=1e6# throw these out, should check this out and handle more gracefully1134minChiSq=chiSq.min()1135bestMatchIndex=numpy.equal(chiSq,minChiSq).nonzero()[0][0]1136bestNorm=norm[bestMatchIndex]1137bestChiSq=minChiSq1138bestChiSqContrib=((sedFlux[bestMatchIndex]-norms[bestMatchIndex]*modelFlux[bestMatchIndex])**2)\ 1139/sedFluxErr[bestMatchIndex]**211401141resultsDict={'minChiSq':bestChiSq,1142'chiSqContrib':bestChiSqContrib,1143'allChiSqValues':chiSq,1144'ageGyr':modelSEDDictList[bestMatchIndex]['ageGyr'],1145'modelFileName':modelSEDDictList[bestMatchIndex]['fileName'],1146'modelListIndex':modelSEDDictList[bestMatchIndex]['modelListIndex'],1147'norm':bestNorm,1148'z':modelSEDDictList[bestMatchIndex]['z'],1149'E(B-V)':modelSEDDictList[bestMatchIndex]['E(B-V)']}11501151returnresultsDict
1155"""Takes a set of corresponding AB magnitudes, uncertainties, and passbands, and1156 returns a dictionary with keys 'flux', 'fluxErr', 'wavelength'. Fluxes are in units of 1157 erg/s/cm^2/Angstrom, wavelength in Angstroms. These dictionaries are the staple diet of the1158 L{fitSEDDict} routine.11591160 @type ABMags: list or numpy array1161 @param ABMags: AB magnitudes, specified in corresponding order to passbands and ABMagErrs1162 @type ABMagErrs: list or numpy array1163 @param ABMagErrs: AB magnitude errors, specified in corresponding order to passbands and ABMags1164 @type passbands: list of L{Passband} objects1165 @param passbands: passband objects, specified in corresponding order to ABMags and ABMagErrs1166 @rtype: dictionary1167 @return: dictionary with keys {'flux', 'fluxErr', 'wavelength'}, suitable for input to L{fitSEDDict}11681169 """11701171flux=[]1172fluxErr=[]1173wavelength=[]1174form,e,pinzip(ABMags,ABMagErrs,passbands):1175f,err=mag2Flux(m,e,p)1176flux.append(f)1177fluxErr.append(err)1178wavelength.append(p.effectiveWavelength())11791180SEDDict={}1181SEDDict['flux']=numpy.array(flux)1182SEDDict['fluxErr']=numpy.array(fluxErr)1183SEDDict['wavelength']=numpy.array(wavelength)11841185returnSEDDict
1189"""Converts given AB magnitude and uncertainty into flux, in erg/s/cm^2/Angstrom.11901191 @type ABMag: float1192 @param ABMag: magnitude on AB system in passband1193 @type ABMagErr: float1194 @param ABMagErr: uncertainty in AB magnitude in passband1195 @type passband: L{Passband} object1196 @param passband: L{Passband} object at which ABMag was measured1197 @rtype: list1198 @return: [flux, fluxError], in units of erg/s/cm^2/Angstrom11991200 """12011202fluxJy=(10**23.0)*10**(-(ABMag+48.6)/2.5)# AB mag1203aLambda=3e-13# for conversion to erg s-1 cm-2 angstrom-1 with lambda in microns1204effLMicron=passband.effectiveWavelength()*(1e-10/1e-6)1205fluxWLUnits=aLambda*fluxJy/effLMicron**212061207fluxJyErr=(10**23.0)*10**(-(ABMag-ABMagErr+48.6)/2.5)# AB mag1208fluxWLUnitsErr=aLambda*fluxJyErr/effLMicron**21209fluxWLUnitsErr=fluxWLUnitsErr-fluxWLUnits12101211return[fluxWLUnits,fluxWLUnitsErr]
1215"""Converts given flux and uncertainty in erg/s/cm^2/Angstrom into AB magnitudes.12161217 @type flux: float1218 @param flux: flux in erg/s/cm^2/Angstrom in passband1219 @type fluxErr: float1220 @param fluxErr: uncertainty in flux in passband, in erg/s/cm^2/Angstrom1221 @type passband: L{Passband} object1222 @param passband: L{Passband} object at which ABMag was measured1223 @rtype: list1224 @return: [ABMag, ABMagError], in AB magnitudes12251226 """12271228# aLambda = 3x10-5 for effective wavelength in angstroms1229aLambda=3e-13# for conversion to erg s-1 cm-2 angstrom-1 with lambda in microns1230effLMicron=passband.effectiveWavelength()*(1e-10/1e-6)12311232fluxJy=(flux*effLMicron**2)/aLambda1233mag=-2.5*numpy.log10(fluxJy/10**23)-48.612341235fluxErrJy=(fluxErr*effLMicron**2)/aLambda1236magErr=mag-(-2.5*numpy.log10((fluxJy+fluxErrJy)/10**23)-48.6)12371238return[mag,magErr]
1242"""Converts an AB magnitude into flux density in Jy12431244 @type ABMag: float1245 @param ABMag: AB magnitude1246 @rtype: float1247 @return: flux density in Jy12481249 """12501251fluxJy=((10**23)*10**(-(float(ABMag)+48.6)/2.5))12521253returnfluxJy
1258"""Converts flux density in Jy into AB magnitude12591260 @type fluxJy: float1261 @param fluxJy: flux density in Jy1262 @rtype: float1263 @return: AB magnitude12641265 """12661267ABMag=-2.5*(numpy.log10(fluxJy)-23.0)-48.612681269returnABMag
12701271#------------------------------------------------------------------------------------------------------------1272# Data1273VEGA=VegaSED()12741275# AB SED has constant flux density 3631 Jy1276AB=SED(wavelength=numpy.logspace(1,8,1e5),flux=numpy.ones(1e6))1277AB.flux=(3e-5*3631)/(AB.wavelength**2)1278AB.z0flux=AB.flux[:]12791280# Solar SED from HST CALSPEC (http://www.stsci.edu/hst/observatory/cdbs/calspec.html)1281SOL=SED()1282SOL.loadFromFile(astLib.__path__[0]+os.path.sep+"data"+os.path.sep+"sun_reference_stis_001.ascii")1283
This class stores the SED of Vega, used for calculation of magnitudes
on the Vega system.
The Vega SED used is taken from Bohlin 2007
(http://adsabs.harvard.edu/abs/2007ASPC..364..315B), and is available
from the STScI CALSPEC library
(http://www.stsci.edu/hst/observatory/cdbs/calspec.html).
This module provides classes for manipulating SEDs, in particular the
Bruzual & Charlot 2003, Maraston 2005, and Percival et al 2009
stellar population synthesis models are currently supported. Functions
are provided for calculating the evolution of colours and magnitudes in
these models with redshift etc., and for fitting broadband photometry
using these models.
Passband
This class describes a filter transmission curve.
TopHatPassband
This class generates a passband with a top hat response between the
given wavelengths.
SED
This class describes a Spectral Energy Distribution (SED).
VegaSED
This class stores the SED of Vega, used for calculation of
magnitudes on the Vega system.
StellarPopulation
This class describes a stellar population model, either a Simple
Stellar Population (SSP) or a Composite Stellar Population (CSP),
such as the models of Bruzual & Charlot 2003 or Maraston 2005.
M05Model
This class describes a Maraston 2005 stellar population model.
BC03Model
This class describes a Bruzual & Charlot 2003 stellar
population model, extracted from a GALAXEV .ised file using the
galaxevpl program that is included in GALAXEV.
P09Model
This class describes a Percival et al 2009 (BaSTI;
http://albione.oa-teramo.inaf.it) stellar population model.
makeModelSEDDictList(modelList,
z,
passbandsList,
labelsList=[],
EBMinusVList=[0.0],
forceYoungerThanUniverse=True)
This routine makes a list of SEDDict dictionaries (see mags2SEDDict) for fitting using fitSEDDict.
mags2SEDDict(ABMags,
ABMagErrs,
passbands)
Takes a set of corresponding AB magnitudes, uncertainties, and
passbands, and returns a dictionary with keys 'flux', 'fluxErr',
'wavelength'.
This routine makes a list of SEDDict dictionaries (see mags2SEDDict) for fitting using fitSEDDict.
This speeds up the fitting as this allows us to calculate model SED
magnitudes only once, if all objects to be fitted are at the same
redshift. We add some meta data to the modelSEDDicts (e.g. the model file
names).
The effect of extinction by dust (assuming the Calzetti et al. 2000
law) can be included by giving a list of E(B-V) values.
If forceYoungerThanUniverse == True, ages which are older than the
universe at the given z will not be included.
Parameters:
modelList (list of StellarPopulation model objects) - list of StellarPopulation models to include
z (float) - redshift to apply to all stellar population models in modelList
EBMinusVList (list) - list of E(B-V) extinction values to apply to all models, in
magnitudes
labelsList (list) - optional list used for labelling passbands in output SEDDicts
forceYoungerThanUniverse (bool) - if True, do not allow models that exceed the age of the universe
at z
Returns: list
list of dictionaries containing model fluxes, to be used as input
to fitSEDDict.
Fits the given SED dictionary (made using mags2SEDDict) with the given list of model SED
dictionaries. The latter should be made using makeModelSEDDictList, and entries for fluxes should
correspond directly between the model and SEDDict.
Returns a dictionary with best fit values.
Parameters:
SEDDict (dictionary, in format of mags2SEDDict) - dictionary of observed fluxes and uncertainties, in format of mags2SEDDict
modelSEDDictList (list of dictionaries, in format of makeModelSEDDictList) - list of dictionaries containing fluxes of models to be fitted to
the observed fluxes listed in the SEDDict. This should be made
using makeModelSEDDictList.
Returns: dictionary
results of the fitting - keys:
'minChiSq': minimum chi squared value of best fit
'chiSqContrib': corresponding contribution at each passband
to the minimum chi squared value
'ageGyr': the age in Gyr of the best fitting model
'modelFileName': the file name of the stellar population
model corresponding to the best fit
'modelListIndex': the index of the best fitting model in the
input modelSEDDictList
'norm': the normalisation that the best fit model should be
multiplied by to match the SEDDict
'z': the redshift of the best fit model
'E(B-V)': the extinction, E(B-V), in magnitudes, of the best
fit model
Takes a set of corresponding AB magnitudes, uncertainties, and
passbands, and returns a dictionary with keys 'flux', 'fluxErr',
'wavelength'. Fluxes are in units of erg/s/cm^2/Angstrom, wavelength in
Angstroms. These dictionaries are the staple diet of the fitSEDDict
routine.
Parameters:
ABMags (list or numpy array) - AB magnitudes, specified in corresponding order to passbands and
ABMagErrs
ABMagErrs (list or numpy array) - AB magnitude errors, specified in corresponding order to
passbands and ABMags
passbands (list of Passband objects) - passband objects, specified in corresponding order to ABMags and
ABMagErrs
Returns: dictionary
dictionary with keys {'flux', 'fluxErr', 'wavelength'}, suitable
for input to fitSEDDict
float or numpy array, depending upon type of RADeg2, decDeg2
calcAngSepDeg(RADeg1,
decDeg1,
RADeg2,
decDeg2)
Calculates the angular separation of two positions on the sky
(specified in decimal degrees) in decimal degrees, assuming a tangent
plane projection (so separation has to be <90 deg).
calcRADecSearchBox(RADeg,
decDeg,
radiusSkyDeg)
Calculates minimum and maximum RA, dec coords needed to define a box
enclosing a circle of radius radiusSkyDeg around the given RADeg,
decDeg coordinates.
Calculates the angular separation of two positions on the sky
(specified in decimal degrees) in decimal degrees, assuming a tangent
plane projection (so separation has to be <90 deg). Note that RADeg2,
decDeg2 can be numpy arrays.
Parameters:
RADeg1 (float) - R.A. in decimal degrees for position 1
decDeg1 (float) - dec. in decimal degrees for position 1
RADeg2 (float or numpy array) - R.A. in decimal degrees for position 2
decDeg2 (float or numpy array) - dec. in decimal degrees for position 2
Returns: float or numpy array, depending upon type of RADeg2, decDeg2
Computes new right ascension and declination shifted from the original
by some delta RA and delta DEC. Input position is decimal degrees. Shifts
(deltaRA, deltaDec) are arcseconds, and output is decimal degrees. Based
on an IDL routine of the same name.
Calculates minimum and maximum RA, dec coords needed to define a box
enclosing a circle of radius radiusSkyDeg around the given RADeg, decDeg
coordinates. Useful for freeform queries of e.g. SDSS, UKIDSS etc.. Uses
calcAngSepDeg, so has the same limitations.
Parameters:
RADeg (float) - RA coordinate of centre of search region
decDeg (float) - dec coordinate of centre of search region
radiusSkyDeg (float) - radius in degrees on the sky used to define search region
For images rotated with North at the top, East at the left (as can be
done using astImages.clipRotatedImageSectionWCS or astImages.resampleToTanProjection, WCS coordinate axes
can be plotted, with tick marks set appropriately for the image size.
Otherwise, a compass can be plotted showing the directions of North and
East in the image.
RGB images are also supported.
The plot can of course be tweaked further after creation using
matplotlib/pylab commands.
__init__(self,
imageData,
imageWCS,
axes=[0.1,0.1,0.8,0.8],
cutLevels=["smart",99.5],
colorMapName="gray",
title=None,
axesLabels="sexagesimal",
axesFontFamily="serif",
axesFontSize=12.0,
RATickSteps="auto",
decTickSteps="auto",
colorBar=False,
interpolation="bilinear")
Makes an ImagePlot from the given image array and astWCS.
addContourOverlay(self,
contourImageData,
contourWCS,
tag,
levels=["linear","min","max",5],
width=1,
color="white",
smooth=0,
highAccuracy=False)
Adds image data to the ImagePlot as a contour overlay.
addCompass(self,
location,
sizeArcSec,
color="white",
fontSize=12,
width=20.0)
Adds a compass to the ImagePlot at the given location ('N', 'NE',
'E', 'SE', 'S', 'SW', 'W', or 'NW').
addScaleBar(self,
location,
sizeArcSec,
color="white",
fontSize=12,
width=20.0)
Adds a scale bar to the ImagePlot at the given location ('N', 'NE',
'E', 'SE', 'S', 'SW', 'W', or 'NW').
If imageData is given as a list in the format [r, g, b], a color RGB
plot will be made. However, in this case the cutLevels must be specified
manually for each component as a list - i.e. cutLevels = [[r min, r max],
[g min, g max], [b min, b max]]. In this case of course, the colorMap
will be ignored. All r, g, b image arrays must have the same
dimensions.
Set axesLabels = None to make a plot without coordinate axes
plotted.
The axes can be marked in either sexagesimal or decimal celestial
coordinates. If RATickSteps or decTickSteps are set to "auto",
the appropriate axis scales will be determined automatically from the
size of the image array and associated WCS. The tick step sizes can be
overidden. If the coordinate axes are in sexagesimal format a dictionary
in the format {'deg', 'unit'} is needed (see RA_TICK_STEPS and DEC_TICK_STEPS for examples). If the coordinate axes are
in decimal format, the tick step size is specified simply in RA, dec
decimal degrees.
Parameters:
imageData (numpy array or list) - image data array or list of numpy arrays [r, g, b]
imageWCS (astWCS.WCS) - astWCS.WCS object
axes (list) - specifies where in the current figure to draw the finder chart
(see pylab.axes)
cutLevels (list) - sets the image scaling - available options:
pixel values: cutLevels=[low value, high value].
histogram equalisation: cutLevels=["histEq", number
of bins ( e.g. 1024)]
relative: cutLevels=["relative", cut per cent level
(e.g. 99.5)]
smart: cutLevels=["smart", cut per cent level (e.g.
99.5)]
["smart", 99.5] seems to provide good scaling over a
range of different images. Note that for RGB images, cut levels
must be specified manually i.e. as a list: [[r min, rmax], [g
min, g max], [b min, b max]]
colorMapName (string) - name of a standard matplotlib colormap, e.g. "hot",
"cool", "gray" etc. (do
"help(pylab.colormaps)" in the Python interpreter to
see available options)
title (string) - optional title for the plot
axesLabels (string) - either "sexagesimal" (for H:M:S, D:M:S),
"decimal" (for decimal degrees) or None (for no
coordinate axes labels)
axesFontFamily (string) - matplotlib fontfamily, e.g. 'serif', 'sans-serif' etc.
axesFontSize (float) - font size of axes labels and titles (in points)
colorBar (bool) - if True, plot a vertical color bar at the side of the image
indicating the intensity scale.
interpolation (string) - interpolation to apply to the image plot (see the documentation
for the matplotlib.pylab.imshow command)
Adds image data to the ImagePlot as a contour overlay. The contours
can be removed using removeContourOverlay. If a contour overlay already
exists with this tag, it will be replaced.
Parameters:
contourImageData (numpy array) - image data array from which contours are to be generated
contourWCS (astWCS.WCS) - astWCS.WCS object for the image to be contoured
tag (string) - identifying tag for this set of contours
levels (list) - sets the contour levels - available options:
values: contourLevels=[list of values specifying each level]
linear spacing: contourLevels=['linear', min level value, max
level value, number of levels] - can use "min",
"max" to automatically set min, max levels from
image data
log spacing: contourLevels=['log', min level value, max level
value, number of levels] - can use "min",
"max" to automatically set min, max levels from
image data
width (int) - width of the overlaid contours
color (string) - color of the overlaid contours, specified by the name of a
standard matplotlib color, e.g., "black",
"white", "cyan" etc. (do
"help(pylab.colors)" in the Python interpreter to see
available options)
smooth (float) - standard deviation (in arcsec) of Gaussian filter for
pre-smoothing of contour image data (set to 0 for no smoothing)
highAccuracy (bool) - if True, sample every corresponding pixel in each image;
otherwise, sample every nth pixel, where n = the ratio of the
image scales.
Add objects with RA, dec coords objRAs, objDecs to the ImagePlot. Only
objects that fall within the image boundaries will be plotted.
symbol specifies the type of symbol with which to mark the object in
the image. The following values are allowed:
"circle"
"box"
"cross"
"diamond"
size specifies the diameter in arcsec of the symbol (if plotSymbol ==
"circle"), or the width of the box in arcsec (if plotSymbol ==
"box")
width specifies the thickness of the symbol lines in pixels
color can be any valid matplotlib color (e.g. "red",
"green", etc.)
The objects can be removed from the plot by using removePlotObjects(),
and then calling draw(). If the ImagePlot already has a set of
plotObjects with the same tag, they will be replaced.
Parameters:
objRAs (numpy array or list) - object RA coords in decimal degrees
objDecs (numpy array or list) - corresponding object Dec. coords in decimal degrees
tag (string) - identifying tag for this set of objects
symbol (string) - either "circle", "box", "cross", or
"diamond"
size (float) - size of symbols to plot (radius in arcsec, or width of box)
width (float) - width of symbols in pixels
color (string) - any valid matplotlib color string, e.g. "red",
"green" etc.
objLabels (list) - text labels to plot next to objects in figure
objLabelSize (float) - size of font used for object labels (in points)
Adds a compass to the ImagePlot at the given location ('N', 'NE', 'E',
'SE', 'S', 'SW', 'W', or 'NW'). Note these aren't directions on the WCS
coordinate grid, they are relative positions on the plot - so N is top
centre, NE is top right, SW is bottom right etc.. Alternatively, pixel
coordinates (x, y) in the image can be given.
Parameters:
location (string or tuple) - location in the plot where the compass is drawn:
string: N, NE, E, SE, S, SW, W or NW
tuple: (x, y)
sizeArcSec (float) - length of the compass arrows on the plot in arc seconds
color (string) - any valid matplotlib color string
fontSize (float) - size of font used to label N and E, in points
width (float) - width of arrows used to mark compass
Adds a scale bar to the ImagePlot at the given location ('N', 'NE',
'E', 'SE', 'S', 'SW', 'W', or 'NW'). Note these aren't directions on the
WCS coordinate grid, they are relative positions on the plot - so N is
top centre, NE is top right, SW is bottom right etc.. Alternatively,
pixel coordinates (x, y) in the image can be given.
Parameters:
location (string or tuple) - location in the plot where the compass is drawn:
string: N, NE, E, SE, S, SW, W or NW
tuple: (x, y)
sizeArcSec (float) - scale length to indicate on the plot in arc seconds
color (string) - any valid matplotlib color string
fontSize (float) - size of font used to label N and E, in points
This function calculates the positions of coordinate labels for the RA
and Dec axes of the ImagePlot. The tick steps are calculated
automatically unless self.RATickSteps, self.decTickSteps are set to
values other than "auto" (see ImagePlot.__init__).
The ImagePlot must be redrawn for changes to be applied.
Parameters:
axesLabels (string) - either "sexagesimal" (for H:M:S, D:M:S),
"decimal" (for decimal degrees), or None for no
coordinate axes labels
Chooses the appropriate WCS coordinate tick steps for the plot based
on its size. Whether the ticks are decimal or sexagesimal is set by
self.axesLabels.
Note: minor ticks not used at the moment.
Returns: dictionary
tick step sizes for major, minor plot ticks, in format {'major',
'minor'}
This class describes a Bruzual & Charlot 2003 stellar population
model, extracted from a GALAXEV .ised file using the galaxevpl program
that is included in GALAXEV. The file format is white space delimited,
with wavelength in the first column. Subsequent columns contain the model
fluxes for SEDs of different ages, as specified when running galaxevpl.
The age corresponding to each flux column is taken from the comment line
beginning "# Age (yr)", and is converted to Gyr.
For example, to load a tau = 0.1 Gyr burst of star formation, solar
metallicity, Salpeter IMF model stored in a file (created by galaxevpl)
called "csp_lr_solar_0p1Gyr.136":
bc03model = BC03Model("csp_lr_solar_0p1Gyr.136")
The wavelength units of SEDs from BC03 models are Angstroms. Flux is
converted into units of erg/s/Angstrom (the units in the files output by
galaxevpl are LSun/Angstrom).
When javascript is enabled, this page will redirect URLs of
the form redirect.html#dotted.name to the
documentation for the object with the given fully-qualified
dotted name.