├── .gitignore ├── Algorithms ├── Functions.py ├── KernelFit.py └── __init__.py ├── Data ├── Atomic.py ├── Elodie.py ├── __init__.py └── archives │ ├── AtomicData.py │ ├── SweetCat.cat │ ├── __init__.py │ ├── elodie.csv │ └── elodie_telluric_standards.cat ├── Figures ├── HD192640.png ├── MultiFit.png ├── Pleiades.png ├── Profile-Extract.png └── Profile-Fit.png ├── Framework ├── Argument.py ├── Command.py ├── Display.py ├── Measurement.py ├── Options.py └── __init__.py ├── LICENSE ├── README.md ├── SLiPy ├── .gitignore ├── Correlate.py ├── Fits.py ├── Measure.py ├── Montage.py ├── Observatory.py ├── Plot.py ├── Profile.py ├── Simbad.py ├── Spectrum.py ├── Telluric.py ├── Velocity.py └── __init__.py ├── __init__.py └── astrolibpy ├── LICENSE ├── README ├── __init__.py ├── astrolib ├── __init__.py ├── baryvel.py ├── bprecess.py ├── convolve.py ├── cosmo_param.py ├── cv_coord.py ├── daycnv.py ├── euler.py ├── gal_uvw.py ├── galage.py ├── helcorr.py ├── helio_jd.py ├── lumdist.py ├── mwrfits.py ├── precess.py ├── precess_xyz.py ├── premat.py ├── readcol.py ├── sphdist.py ├── xyz.py └── zang.py ├── mpfit ├── mpfit.py ├── mpfitexpr.py └── tests │ ├── __init__.py │ └── test_mpfit.py ├── my_utils ├── adabinner.py ├── cacheFunc.py ├── collect_arr.py ├── correct_pm.py ├── from_hex.py ├── get_dss.py ├── idlplotInd.py ├── match_lists.py ├── my_healpy.py ├── pg2hdf5.py ├── quick_hist.py ├── resolve.py ├── sphere_rotate.py ├── staralt.py ├── wav2RGB.py └── window_func.py ├── plotting ├── healmap.py ├── idlplot.py └── lasso_plot.py └── utils ├── derivator.py ├── fminpara.py ├── idlsave.py ├── sqlutil.py └── workerpool.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | *~ 4 | NSFplot.py 5 | HSTplot.py 6 | .DS_store 7 | __pycache__ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /Algorithms/Functions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Right Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/Algorithms/Functions.py 4 | """ 5 | Analytical functions used in SLiPy. 6 | """ 7 | 8 | import numpy as np 9 | from scipy.special import wofz as w # Faddeeva function 10 | 11 | # No FunctionsError implimented yet 12 | 13 | def Gaussian(x, *params): 14 | """ 15 | The Gaussian function. The function template has `*params` to work with 16 | scipy...curve_fit; the user *must* specify `A, mu, sigma = params`. 17 | """ 18 | A, mu, sigma = params 19 | return A * np.exp( -0.5 * (x - mu)**2 / sigma**2 ) 20 | 21 | def NormalizedGaussian(x, *params): 22 | """ 23 | The normalized Gaussian function. 24 | """ 25 | mu, sigma = params 26 | return np.exp( -0.5 * (x - mu)**2 / sigma**2 ) / (sigma * 27 | np.sqrt(2 * np.pi)) 28 | 29 | def InvertedGaussian(x, *params): 30 | """ 31 | An inverted Gaussian function (i.e., 1 - Guassian()). The function 32 | template has `*params` to work with scipy...curve_fit; the user *must* 33 | specify `A, mu, sigma = params`. 34 | """ 35 | return 1 - Gaussian(x, *params) 36 | 37 | def Lorentzian(x, *params): 38 | """ 39 | The Lorentzian function. Typically used to describe an absorption line profile. 40 | """ 41 | x0, gamma = params 42 | return 1 / ((2 * (x - x0) / gamma)**2 + 1) 43 | 44 | def NormalizedLorentzian(x, *params): 45 | """ 46 | The Lorentzian function, normalized. 47 | """ 48 | x0, gamma = params 49 | return 2 * Lorentzian(x, *params) / (np.pi * gamma) 50 | 51 | def NormalizedVoigt(x, *params): 52 | """ 53 | The Voigt function. The result of the convolution of a Lorentzian with one or more 54 | Gaussians. Often used to describe an intrinsically Lorentzian absorption profile blurred 55 | by Gaussian broadening from thermal motions and turbulance and another Gaussian instrument 56 | profile. The result is also the real part of the Faddevva function (the complex probability 57 | function), implemented by Scipy as wofz in scipy.special 58 | 59 | This returns a normalized profile. The name `NormalizedVoigt` is to keep the convention 60 | for the rest of this module. The amplitude controlling Voigt profile `Voigt` evaluates 61 | this function first for `x - x0 = 0` to empirically `un-normalize` it before scaling by the 62 | requested amplitude 63 | 64 | Reference: 65 | Olivero, J.J; R.L. Longbothum. ``Empirical fits to the Voigt line width: 66 | A brief review''. Journal of Quantitative Spectroscopy and Radiative Transfer. Vol 17. 67 | Issue 2. pages 223-236. Feb 1977 68 | 69 | Parameters: `x0, sigma, gamma = params` 70 | 71 | x0 -> center of the profile 72 | sigma -> the Gaussian width 73 | gamma -> the Lorentzian width 74 | """ 75 | x0, sigma, gamma = params 76 | return w( ((x-x0) + 1j*gamma) / (sigma * np.sqrt(np.pi)) ).real / ( 77 | sigma * np.sqrt(2 * np.pi) ) 78 | 79 | def Voigt(x, *params): 80 | """ 81 | The Voigt line profile. See ..Algorithms.Function.NormalizedVoigt for more information. 82 | This function returns an amplitude controlled Voigt profile. 83 | 84 | Parameters: `A, x0, sigma, gamma = params` 85 | 86 | A -> amplitude of the profile 87 | x0 -> center of the profile 88 | sigma -> the Gaussian width 89 | gamma -> the Lorentzian width 90 | """ 91 | return params[0] * NormalizedVoigt(x, *params[1:]) / NormalizedVoigt(0, 0, *params[2:]) 92 | 93 | 94 | def InvertedLorentzian(x, *params): 95 | """ 96 | An inverted Lorentzian (i.e, A - Lorentzian()). 97 | """ 98 | A, x0, gamma = params 99 | 100 | return A - Lorentzian(x, x0, gamma) 101 | -------------------------------------------------------------------------------- /Algorithms/KernelFit.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Right Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/Algorithms/KernelFit.py 4 | """ 5 | Non-parametric Kernel Regression. 6 | """ 7 | 8 | import numpy as np 9 | 10 | from .. import SlipyError 11 | from ..Framework.Options import Options, OptionsError 12 | 13 | from .Functions import Gaussian 14 | 15 | class KernelFitError(SlipyError): 16 | """ 17 | Exception specific to the KernelFit module 18 | """ 19 | 20 | class KernelFit1D(): 21 | """ 22 | One dimensional kernel regression. 23 | """ 24 | def __init__(self, x, y, kernel = Gaussian, **kwargs): 25 | """ 26 | Keep the input `x` and `y` arrays. Define parameters. 27 | The default kernel function is the Guassian. Alternatives should 28 | have the same signature as slipy.Algorithms.Functions.Gaussian 29 | """ 30 | if not hasattr(x, '__iter__') or not hasattr(y, '__iter__'): 31 | raise KernelFitError('Both `x` and `y` are expected to ' 32 | 'be array-like!') 33 | 34 | if len(np.shape(x)) != 1 or len(np.shape(y)) != 1: 35 | raise KernelFitError('Both `x` and `y` are expected to ' 36 | 'be one-dimensional objects!') 37 | 38 | if len(x) != len(y): 39 | raise KernelFitError('Both `x` and `y` should be of the ' 40 | 'same length!') 41 | 42 | if not hasattr(x, 'copy') or not hasattr(y, 'copy'): 43 | raise KernelFitError('Both `x` and `y` should have a ' 44 | 'copy() method implimented!') 45 | 46 | if type(x) is not type(y): 47 | raise KernelFitError('Both `x` and `y` are expected to ' 48 | 'be of the same type!') 49 | 50 | if hasattr(x, 'unit') and not hasattr(y, 'unit'): 51 | raise KernelFitError('The `x` array given has units but the `y` ' 52 | 'array doesn`t!, maybe give `y` u.dimensionless_unscaled?') 53 | 54 | if hasattr(y, 'unit') and not hasattr(x, 'unit'): 55 | raise KernelFitError('The `y` array given has units but the `x` ' 56 | 'array doesn`t!, maybe give `x` u.dimensionless_unscaled?') 57 | 58 | # the default `bandwidth` is 1/10 the domain of `x`, this 59 | # is likely to be a bad choice! 60 | bandwidth = 0.1 * (x.max() - x.min()) 61 | 62 | try: 63 | 64 | options = Options( kwargs, { 65 | 66 | 'bandwidth' : bandwidth # for the kernel function 67 | }) 68 | 69 | self.kernel = kernel 70 | self.bandwidth = options('bandwidth') # not necessarily with units 71 | 72 | except OptionsError as err: 73 | print(' --> OptionsError:', err) 74 | raise KernelFitError('Unrecognized option for KernelFit1D()!') 75 | 76 | self.x = x.copy() 77 | self.y = y.copy() 78 | 79 | def mean(self, x): 80 | """ 81 | Solve for smooth profile through the data on the new `x` array. 82 | This is essentially a weighted mean. 83 | """ 84 | 85 | if not hasattr(x, '__iter__'): 86 | raise KernelFitError('`x` is expected to be array-like!') 87 | 88 | if not hasattr(x, 'copy'): 89 | raise KernelFitError('`x` is expected to have a copy() method!') 90 | 91 | if hasattr(x, 'unit') and not hasattr(self.x, 'unit'): 92 | raise KernelFitError('The provided array has units but the ' 93 | 'original domain does not!') 94 | 95 | if hasattr(self.x, 'unit') and not hasattr(x, 'unit'): 96 | raise KernelFitError('The provided array does not have units ' 97 | 'but the original domain did!') 98 | 99 | # copy `x` such that the return type is the same as the input type 100 | y = x.copy() 101 | 102 | # fix units though 103 | if hasattr(x, 'unit'): 104 | y = y.value * self.y.unit 105 | 106 | for a, point in enumerate(x): 107 | 108 | weights = Gaussian(point, 1, self.x, self.bandwidth) 109 | 110 | # weights should be dimensionless 111 | if hasattr(x, 'unit'): 112 | weights = weights.decompose() 113 | 114 | args = np.where(np.isfinite(self.y)) 115 | y[a] = np.sum(weights[args] * self.y[args]) / np.sum(weights[args]) 116 | 117 | return y 118 | -------------------------------------------------------------------------------- /Algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Right Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/Algorithms/__init__.py 4 | """ 5 | Algorithms used in SLiPy - not necessarily related to astronomy. 6 | """ -------------------------------------------------------------------------------- /Data/Elodie.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # AstroPython/Data/Elodie.py 4 | """ 5 | Methods for data retrieval from the Elodie Archive. 6 | """ 7 | 8 | import os, shutil, numpy as np 9 | from urllib.request import urlopen 10 | 11 | from .. import SlipyError 12 | from ..Framework.Options import Options, OptionsError 13 | from ..Framework.Display import Monitor, DisplayError 14 | 15 | 16 | class ElodieError(SlipyError): 17 | """ 18 | Exception specific to Elodie module. 19 | """ 20 | pass 21 | 22 | class Archive: 23 | """ 24 | Import and parse ascii catalog of Elodie archive files. The complete 25 | archive is stored in the member `data`. It's organized in a dictionary 26 | by unique target names. Each target has a list of pairs consisting of the 27 | name of the file and the signal to noise for that spectrum. The reduced 28 | archive, accessed with `files`, contains identified `HD`, `HR`, `BD`, `GC`, 29 | and `GJ` objects, choosing the file pertaining to the spectra with the 30 | highest signal-to-noise ratio available. 31 | """ 32 | def __init__(self, **kwargs): 33 | try: 34 | 35 | # default input file 36 | default_infile = os.path.join( os.path.dirname(__file__), 37 | 'Archives/Elodie.csv') 38 | 39 | # parameter defaults 40 | options = Options( kwargs, 41 | { 42 | 'infile' : default_infile , # path to input file 43 | 'catalogs': ['HD','BD','HR','GC','GJ'] # catalogues to keep 44 | }) 45 | 46 | # parameter assignments 47 | infile = options('infile') 48 | catalogs = options('catalogs') 49 | 50 | except OptionsError as err: 51 | print(' --> OptionsError:', err) 52 | raise ElodieError('Failed keyword assignment from Archive.__init__') 53 | 54 | # import data from archive 55 | with open(infile, 'r') as archive: 56 | data = [ line.split(',') for line in archive.readlines() ] 57 | 58 | # strip elements of white space 59 | data = [ [x.strip() for x in line] for line in data ] 60 | 61 | # build empty dictionary of unique names with empty lists 62 | targets = { name:[] for name in set([ line[0] for line in data ]) } 63 | 64 | # compile list of spectra files organized by identifier w/ S/N 65 | for line in data: 66 | targets[line[0]].append( line[2:] ) 67 | 68 | # reject files from spectra not in catalogues 69 | targets = { k:v for k, v in targets.items() if k[:2] in catalogs } 70 | 71 | files = {} 72 | for target, options in targets.items(): 73 | # choose best S/N 74 | idx = np.array( np.array(options)[:,-1], dtype=np.int_ ).argmax() 75 | files[target] = options[idx][0] 76 | 77 | # members 78 | self.data = targets 79 | self.files = files 80 | self.names = [ 81 | '-'.join('-'.join(x.split(':')).split('/')) + '.fits' 82 | for x in files.values() 83 | ] 84 | 85 | def Script(filename, pipeline=''): 86 | """ 87 | Construct url script for Elodie archive given `filename` and optionally 88 | `pipeline` instructions (e.g., `&z=wrs|fca[1,nor]`). 89 | """ 90 | return ''.join(['http://atlas.obs-hp.fr/elodie/E.cgi?&c=i&o=', 91 | filename, pipeline, '&a=mime:application/x-fits']) 92 | 93 | def Download( *files, **kwargs ): 94 | """ 95 | Download `files` from Elodie archive via url scripts. The spectra can be 96 | further reduced via Elodie`s pipeline with the following options. 97 | 98 | kwargs = { 99 | 'verbose' : True , # display messages, progress 100 | 'resample' : (min, max, res), # resample spectra (no default) 101 | 'normalize': True , # continuum normalization 102 | 'outpath' : './' , # directory for downloaded files 103 | 'names' : [] # alternative output names for `files` 104 | } 105 | """ 106 | try: 107 | 108 | # function parameter defaults 109 | options = Options( kwargs, 110 | { 111 | 'verbose' : True , # display messages, progress 112 | 'resample' : (-1,-1,-1), # handled by Elodie 113 | 'normalize': True , # continuum normalization 114 | 'outpath' : './' , # directory for downloaded files 115 | 'names' : [] # alternative output names for `files` 116 | }) 117 | 118 | # function parameter assignments 119 | verbose = options('verbose') 120 | resample = options('resample') 121 | normalize = options('normalize') 122 | outpath = options('outpath') 123 | names = options('names') 124 | 125 | # check for `resampled` assignment 126 | if 'resample' not in kwargs: 127 | resample = None 128 | elif len(resample) != 3: 129 | raise ElodieError('Download() expects `resample` to be of length ' 130 | 'three.') 131 | 132 | # set default names 133 | if not names: 134 | names = [ '-'.join(fname.split(':')) for fname in files ] 135 | names = [ '-'.join(fname.split('/')) for fname in names ] 136 | names = [ fname + '.fits' for fname in names ] 137 | 138 | elif len(names) != len(files): 139 | raise ElodieError('Download() expects `names` option to be of ' 140 | 'length equal to that of the number of `files` arguments.') 141 | 142 | except OptionsError as err: 143 | print(' --> OptionsError:', err) 144 | raise ElodieError('Failed keyword assignment from Download().') 145 | 146 | if not resample and not normalize: 147 | pipeline = '' 148 | else: 149 | pipeline = '&z=wrs' 150 | 151 | if normalize: 152 | pipeline += '|fca[1,nor]' 153 | 154 | if resample: 155 | resample = [ str(x) for x in resample ] 156 | pipeline += '|wrs[1,' + ','.join(resample) + ']' 157 | 158 | if verbose: 159 | display = Monitor(ETC=True) 160 | nfiles = len(files) 161 | print('\n Downloading {} files from Elodie ...'.format(nfiles)) 162 | 163 | for a, spectra in enumerate(files): 164 | 165 | # show progress 166 | if verbose: display.progress(a + 1, nfiles) 167 | 168 | # download file 169 | with urlopen( Script(spectra, pipeline) ) as response, open( 170 | os.path.join(outpath, names[a]), 'wb') as outfile: 171 | shutil.copyfileobj(response, outfile) 172 | 173 | if verbose: 174 | display.complete() 175 | display.elapsed() 176 | -------------------------------------------------------------------------------- /Data/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # SLiPy/Data/__init__.py 4 | -------------------------------------------------------------------------------- /Data/archives/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # SLiPy/Data/Archives/__init__.py 4 | 5 | """ 6 | This __init__ module allows import from .AtomicData.py 7 | """ 8 | -------------------------------------------------------------------------------- /Data/archives/elodie_telluric_standards.cat: -------------------------------------------------------------------------------- 1 | HD000003A 2 | HD000144 3 | HD000560 4 | HD001280 5 | HD001337 6 | HD001404 7 | HD001976 8 | HD003901 9 | HD005394 10 | HD009531 11 | HD010516 12 | HD011946 13 | HD014055 14 | HD014191 15 | HD016908 16 | HD016970 17 | HD017573 18 | HD017769 19 | HD020365 20 | HD020677 21 | HD020809 22 | HD021686 23 | HD022192 24 | HD022928 25 | HD023302 26 | HD023338 27 | HD023480 28 | HD023630 29 | HD023850 30 | HD023862 31 | HD024760 32 | HD024912 33 | HD025642 34 | HD030739 35 | HD031295 36 | HD032630 37 | HD034787 38 | HD035715 39 | HD036486 40 | HD037020 41 | HD037022 42 | HD037023A 43 | HD037041A 44 | HD037043 45 | HD037202 46 | HD037490 47 | HD037742 48 | HD040111 49 | HD042111 50 | HD042545 51 | HD043285 52 | HD055185 53 | HD058715 54 | HD071155 55 | HD074280 56 | HD080081 57 | HD087901A 58 | HD088960 59 | HD092825 60 | HD102647 61 | HD103287 62 | HD106591 63 | HD109387 64 | HD110411 65 | HD116160 66 | HD120315 67 | HD125162 68 | HD130109 69 | HD135502 70 | HD136849 71 | HD137422 72 | HD138749 73 | HD139006 74 | HD140436 75 | HD141003 76 | HD142926 77 | HD143187 78 | HD143894 79 | HD145454 80 | HD149212 81 | HD149757 82 | HD150117 83 | HD152614 84 | HD156633 85 | HD156729 86 | HD158148 87 | HD161289 88 | HD161868 89 | HD162579 90 | HD163472 91 | HD164284 92 | HD166014 93 | HD168797 94 | HD173370 95 | HD173495 96 | HD177724 97 | HD177756 98 | HD179791 99 | HD183656 100 | HD188001 101 | HD192425 102 | HD192685 103 | HD194244 104 | HD196740 105 | HD196867 106 | HD198183 107 | HD199579 108 | HD200120 109 | HD202904 110 | HD203467 111 | HD208057 112 | HD208727 113 | HD209481 114 | HD210418 115 | HD211096 116 | HD211211 117 | HD212076 118 | HD213558 119 | HD213998 120 | HD214923 121 | HD217891 122 | HD218045 123 | HD222439 124 | -------------------------------------------------------------------------------- /Figures/HD192640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glentner/SLiPy/5984908b56f427e8c9d50bd613a31ad9460ea027/Figures/HD192640.png -------------------------------------------------------------------------------- /Figures/MultiFit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glentner/SLiPy/5984908b56f427e8c9d50bd613a31ad9460ea027/Figures/MultiFit.png -------------------------------------------------------------------------------- /Figures/Pleiades.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glentner/SLiPy/5984908b56f427e8c9d50bd613a31ad9460ea027/Figures/Pleiades.png -------------------------------------------------------------------------------- /Figures/Profile-Extract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glentner/SLiPy/5984908b56f427e8c9d50bd613a31ad9460ea027/Figures/Profile-Extract.png -------------------------------------------------------------------------------- /Figures/Profile-Fit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glentner/SLiPy/5984908b56f427e8c9d50bd613a31ad9460ea027/Figures/Profile-Fit.png -------------------------------------------------------------------------------- /Framework/Argument.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/Framework/Arguments.py 4 | """ 5 | Module contains `Argument` class for handling conversions and type 6 | checking for function/class keyword argument options. 7 | """ 8 | 9 | from .. import SlipyError 10 | 11 | class ArgumentError(SlipyError): 12 | """ 13 | Exception specific to Argument module. 14 | """ 15 | pass 16 | 17 | class Argument: 18 | """ 19 | `Argument` object has type and value management. 20 | """ 21 | def __init__(self, value, name = 'unspecified', **kwargs ): 22 | """ 23 | Build Argument `value`, `type`, and set `options`. 24 | """ 25 | options = { 26 | 'lock' : False # disallow all type conversions 27 | } 28 | for arg in kwargs: 29 | if arg not in options: 30 | raise ArgumentError('`{}` is not an option for Argument.' 31 | .format(arg)) 32 | if type(kwarg[arg]) is not type(options[arg]): 33 | raise ArgumentError('Option `{}` expects {}, given {}' 34 | .format(arg, type(options[arg]), type(kwargs[arg])) ) 35 | # accept reassignment 36 | options[arg] = kwargs[arg] 37 | 38 | self.lock = options['lock'] 39 | self.T = type(value) 40 | self.value = value 41 | self.name = str(name) 42 | if type(name) is not str: 43 | raise ArgumentError('Argument expects type str for name.') 44 | 45 | def __call__(self, value): 46 | 47 | if self.lock and type(value) is not self.T: 48 | raise ArgumentError('Argument `{}` is locked as {}, ' 49 | 'but new value has {}.' 50 | .format(self.name, self.T, type(value)) ) 51 | 52 | if self.T is bool: 53 | # special rules for `bool` conversions 54 | 55 | if type(value) is str: 56 | if value != "True" and value != "False": 57 | raise ArgumentError('Invalid conversion from {} ' 58 | 'for Argument `{}`.'.format(self.T, self.name)) 59 | self.value = True if value == 'True' else False 60 | 61 | elif type(value) is int: 62 | if value != 0 and value != 1: 63 | raise ArgumentError('Invalid conversion from {} ' 64 | 'for Argument `{}`.'.format(self.T, self.name) ) 65 | else: 66 | self.value = True if value else False 67 | 68 | elif type(value) is not bool: 69 | raise Error('Invalid conversion from {} ' 70 | 'for Argument `{}`.'.format(self.T, self.name)) 71 | 72 | else: self.value = value 73 | 74 | else: 75 | 76 | try: 77 | self.value = self.T(value) 78 | 79 | except ValueError as error: 80 | raise ArgumentError('Cannot convert {} to {} for `{}`' 81 | ' Argument.'.format(self.T, type(value), self.name)) 82 | -------------------------------------------------------------------------------- /Framework/Command.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3). 3 | # slipy/Framework/Interface.py 4 | """ 5 | Command line interface tools. 6 | """ 7 | 8 | from .. import SlipyError 9 | from .Options import Options, OptionsError 10 | 11 | class CommandError(SlipyError): 12 | """ 13 | Exception specific to Command module. 14 | """ 15 | pass 16 | 17 | def Parse( clargs, **kwargs ): 18 | """ 19 | Parse command line arguments, `clargs` (i.e., sys.argv). 20 | """ 21 | if type(clargs) is not list: 22 | raise CommandError('Parse function expects a list for `clargs`.') 23 | try: 24 | options = Options( kwargs, 25 | { 26 | 'exe' : True # search first argument for '@func' pattern 27 | }) 28 | 29 | if options('exe'): 30 | function = clargs[0].split('@') 31 | if len(function) != 2 or function[0]: 32 | raise CommandError('Incorrect formatting in function call.') 33 | function = function[1] 34 | del(clargs[0]) 35 | 36 | # args should not have an assignment 37 | args = [ x for x in clargs if '=' not in x ] 38 | 39 | # remaining clargs should be kwargs 40 | kwargs = { 41 | key : value for key, value in [ 42 | arg.split('=') for arg in set(clargs) - set(args) 43 | ] 44 | } 45 | 46 | if options('exe'): 47 | return function, args, kwargs 48 | 49 | else: 50 | return args, kwargs 51 | 52 | except OptionsError as err: 53 | print('\n --> OptionsError:', err) 54 | raise CommandError('from Parse') 55 | 56 | except ValueError as key: 57 | raise CommandError('Incorrect formatting of keyword arguments.') 58 | -------------------------------------------------------------------------------- /Framework/Display.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/Framework/Display.py 4 | """ 5 | Display - Python module for displaying content to the terminal. 6 | """ 7 | 8 | import os, sys, math 9 | from time import time 10 | from datetime import datetime, timedelta 11 | 12 | from .. import SlipyError 13 | from .Options import Options, OptionsError 14 | 15 | class DisplayError(SlipyError): 16 | """ 17 | Exception specific to the Display module. 18 | """ 19 | pass 20 | 21 | class Monitor: 22 | """ 23 | Class for displaying a progress bar during iterative tasks. 24 | """ 25 | def __init__(self, **kwargs ): 26 | try: 27 | # available keyword options 28 | self.options = Options( kwargs, 29 | { 30 | 'width' : 45 , # number of characters wide 31 | 'numbers' : True , # display numberical percent 32 | 'template' : '[=>]', # template for progress bars 33 | 'freq' : 0.25 , # refresh rate 34 | 'ETC' : False , # display estimated time of completion 35 | 'inline' : True # vanish after completion 36 | }) 37 | 38 | # give assignments 39 | self.width = self.options('width') 40 | self.numbers = self.options('numbers') 41 | self.freq = self.options('freq') 42 | self.ETC = self.options('ETC') 43 | self.inline = self.options('inline') 44 | self.left, self.char, self.tip, self.right = self.options('template') 45 | 46 | # start clocks 47 | self.start = time() 48 | self.last = time() 49 | 50 | except OptionsError as err: 51 | print( '\n --> OptionsError:', err.msg ) 52 | raise DisplayError('Failed to initialize Monitor.') 53 | 54 | except ValueError as err: 55 | raise DisplayError( 56 | '`template` option requires exactly 4 characters.') 57 | 58 | def __EstimatedCompletionTime(self): 59 | """ 60 | Estimated time of completion, based on percent complete and 61 | current total elapsed time. 62 | """ 63 | if self.percent > 0: 64 | elapsed = time() - self.start 65 | remaining = elapsed * (1 / self.percent - 1) 66 | etc = datetime.today() + timedelta(seconds=remaining) 67 | return etc.strftime(' ETC: %Y-%m-%d @ %H:%M ') 68 | 69 | def __build(self): 70 | """ 71 | Build the progress bar 72 | """ 73 | bars = self.char * math.floor( self.percent * self.width ) 74 | empty = ' ' * (self.width - len(bars) - 1) 75 | display = self.left + bars + self.tip + empty + self.right 76 | 77 | if self.numbers: 78 | display += '{:>8.2f} % '.format( self.percent * 100 ) 79 | 80 | if self.ETC: 81 | display += self.__EstimatedCompletionTime() 82 | 83 | sys.stdout.write('\r \033[K \r {}'.format(display)) 84 | sys.stdout.flush() 85 | 86 | def progress(self, i, imax): 87 | """ 88 | Request a progress bar. 89 | """ 90 | if time() - self.last > self.freq: 91 | # refresh rate surpassed, 92 | # update time of last call and percent complete 93 | self.last = time() 94 | self.percent = float(i) / float(imax) 95 | # display progress bar 96 | self.__build() 97 | 98 | def complete(self): 99 | """ 100 | Call to finalize the progress bar. 101 | """ 102 | if self.inline: 103 | sys.stdout.write('\r\033[K\r') 104 | sys.stdout.flush() 105 | 106 | else: 107 | self.percent = 1 108 | self.numbers = False 109 | self.ETC = False 110 | self.__build() 111 | sys.stdout.write(' complete\n') 112 | sys.stdout.flush() 113 | 114 | def elapsed(self): 115 | """ 116 | Display total time elapsed since instantiation. 117 | """ 118 | total = time() - self.start 119 | abrv = [ 'd', 'h', 'm', 's'] 120 | unit = { 'd': 86400, 'h':3600, 'm':60 } 121 | count = { 'd':0, 'h':0, 'm':0, 's':0 } 122 | 123 | for item in abrv: 124 | if item in unit: 125 | while total > unit[item]: 126 | total -= unit[item] 127 | count[item] += 1 128 | else: count[item] = math.floor(total) 129 | 130 | total = [ 131 | '{} {}'.format( v, u ) 132 | for u, v in zip( abrv, [ count[v] for v in abrv ] ) 133 | if count[u] 134 | ] 135 | total = ' Time Elapsed: ' + ' '.join(total) 136 | total = ' ' + '-' * (len(total)+5) + '\n ' + total 137 | 138 | sys.stdout.write(total) 139 | sys.stdout.flush() 140 | -------------------------------------------------------------------------------- /Framework/Measurement.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/Framework/Measurement.py 4 | """ 5 | The `Measurement` is a container for a `value` and `error`. 6 | 7 | Astropy already has a very useful object, `Quantity` that is expanded into 8 | a `Constant` class. Something with a value, a name, abbreviation, uncertainty, 9 | and a reference. A `Measurement` is nothing more than a `Constant` by 10 | a different name. It functions just like a Quantity/Constant, only we don't 11 | want to be calling it a "constant" and we want to be able to have many of them. 12 | """ 13 | 14 | from astropy.units import Quantity 15 | 16 | class Measurement(Quantity): 17 | """ 18 | A container for a `result` and an `error`. This object is meant to be functionally 19 | equivalent to the astropy.constant.Constant, without the instance checking (we can 20 | have many `Measurement`s). There are `notes` instead of `references`. 21 | """ 22 | def __new__(cls, value, error=None, name=None, notes=None): 23 | 24 | instance = super().__new__(cls, value) 25 | 26 | instance.error = error 27 | instance.name = name 28 | instance.notes = notes 29 | 30 | return instance 31 | 32 | # These operations are `broken` because they would otherwise yield a `Measurement`. 33 | # The right handed operations are not affected, these are all that is needed I think. 34 | def __truediv__(self, other): 35 | return Quantity(super().__truediv__(other)) 36 | 37 | def __mul__(self, other): 38 | return Quantity(super().__mul__(other)) 39 | 40 | def __add__(self, other): 41 | return Quantity(super().__add__(other)) 42 | 43 | def __sub__(self, other): 44 | return Quantity(super().__sub__(other)) 45 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 46 | 47 | def __repr__(self): 48 | return '<' + ' '.join([ label + str(attr) for attr, label in zip(['Measurement', 49 | self.value * self.unit, self.error, self.name, self.notes], 50 | ['', '', '| error = ', '| name = ', '| notes = ']) if attr]) + '>' 51 | 52 | def __str__(self): 53 | attr = [ self.name, self.value*self.unit, self.error, self.notes ] 54 | name = [' Name = {}', ' Value = {}', ' Error = {}', ' Notes = {}'] 55 | show = [ a for a in attr if a ] 56 | return '\n'.join([n for a, n in zip(attr, name) if a]).format(*show) 57 | -------------------------------------------------------------------------------- /Framework/Options.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/Framework/Options.py 4 | """ 5 | Class object for handling kwargs in classes and functions. 6 | """ 7 | 8 | from .. import SlipyError 9 | from .Argument import Argument as Arg, ArgumentError 10 | 11 | class OptionsError(SlipyError): 12 | """ 13 | Exception specific to Options module. 14 | """ 15 | pass 16 | 17 | class Options: 18 | """ 19 | Class object for handling kwargs in classes and functions. 20 | """ 21 | def __init__(self, kwargs, options ): 22 | """ 23 | Check types and build options dictionary 24 | """ 25 | try: 26 | # initial assignment 27 | self.options = { 28 | name : Arg(value, name) 29 | for name, value in options.items() 30 | } 31 | # attempted reassignment 32 | for key, value in kwargs.items(): 33 | self.options[key](value) 34 | 35 | except AttributeError as err: 36 | raise OptionsError( 37 | 'Options object expects dictionary types.') 38 | 39 | except KeyError as key: 40 | raise OptionsError( 41 | '{} was not a recognized option.'.format(key)) 42 | 43 | except ArgumentError as err: 44 | print('\n --> ArgumentError:', err.msg ) 45 | raise OptionsError('Failed assignment.') 46 | 47 | def __call__(self, option): 48 | """ 49 | Retrieve value of `option`. 50 | """ 51 | try: 52 | return self.options[option].value 53 | except KeyError as key: 54 | raise OptionsError('{} was not recognized.'.format(key)) 55 | 56 | def items(self): 57 | """ 58 | Access options.items() values. 59 | """ 60 | return { k:v.value for k,v in self.options.items() }.items() 61 | -------------------------------------------------------------------------------- /Framework/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # Python/Framework/__init__.py 4 | """ 5 | Framework - programming tools for the rest of the package. 6 | """ -------------------------------------------------------------------------------- /SLiPy/.gitignore: -------------------------------------------------------------------------------- 1 | NSFplot.py 2 | HSTplot.py 3 | -------------------------------------------------------------------------------- /SLiPy/Correlate.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/SLiPy/Correlate.py 4 | """ 5 | Correlate - Module of correlation functions for astronomical data. 6 | """ 7 | import numpy as np 8 | 9 | from .. import SlipyError 10 | from .Spectrum import Spectrum, SpectrumError 11 | from ..Framework.Options import Options, OptionsError 12 | 13 | class CorrelateError(SlipyError): 14 | """ 15 | Exception specific to Correlate module. 16 | """ 17 | pass 18 | 19 | def RMS( array ): 20 | """ 21 | RMS( array ): 22 | 23 | Return the root mean square of an `array`. 24 | """ 25 | if type(array) is not np.ndarray or len(np.shape(array)) != 1: 26 | raise CorrelateError('RMS() expects 1D numpy.ndarray`s.') 27 | 28 | return np.sqrt( ( array**2 ).sum() ) / len(array) 29 | 30 | def Xcorr( spectrumA, spectrumB, **kwargs ): 31 | """ 32 | Xcorr( spectrumA, spectrumB, **kwargs ): 33 | 34 | Cross correlate two spectra of equal pixel length. The function returns 35 | an integer value representing the best shift within a `lag` based on 36 | the computed RMS of each configuration. 37 | """ 38 | try: 39 | # define `options` for Xcorr() 40 | options = Options( kwargs, 41 | { 42 | 'lag' : 25 # pixels to shift for correlation 43 | }) 44 | 45 | # check argument types, values 46 | if ( type(spectrumA) is not Spectrum or 47 | type(spectrumB) is not Spectrum ): 48 | raise CorrelateError('Xcorr() expects `Spectrum` arguments.') 49 | 50 | elif len(spectrumA.data) != len(spectrumB.data): 51 | raise CorrelateError('Xcorr() expects `Spectrum` arguments to ' 52 | 'be of equal length.') 53 | 54 | # assign `lag` and the pixel length of the spectra 55 | lag = options('lag') 56 | npix = len(spectrumA.data) 57 | 58 | # resample `B` to wavelength space of `A` 59 | spectrumB.resample(spectrumA) 60 | 61 | # arrays to correlate 62 | A = spectrumA.data.value 63 | B = spectrumB.data.value 64 | 65 | # shift spectra `left` over each other 66 | left = np.array([ RMS(diff) for diff in [ A[-shift:] - 67 | B[:shift] for shift in range(-lag,0) ] ]) 68 | 69 | # shift spectra `right` over each other 70 | right = np.array([ RMS(diff) for diff in [ A[:-shift] - 71 | B[shift:] for shift in range(1,lag+1) ] ]) 72 | 73 | # include `zero` shift in rms vector. 74 | rms = np.hstack((left, RMS(A - B), right)) 75 | 76 | # return the shift corresponding to the minimum RMS 77 | return rms.argmin() - lag 78 | 79 | except OptionsError as err: 80 | print(' --> OptionsError:', err) 81 | raise CorrelateError('Inappropiate keyword arguments passed ' 82 | ' to Xcorr().') 83 | -------------------------------------------------------------------------------- /SLiPy/Measure.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/SLiPy/Measure.py 4 | 5 | ''' 6 | Measure - Collection of measurement/calculation tools for spectra, etc. 7 | ''' 8 | # import as TkAgg for widget tools 9 | 10 | from matplotlib.widgets import RectangleSelector 11 | from pylab import * 12 | 13 | from .Plot import SPlot, PlotError 14 | from .Spectrum import Spectrum, SpectrumError 15 | 16 | class MeasureError(Exception): 17 | ''' 18 | Exception specific to the Measure module. 19 | ''' 20 | pass 21 | 22 | # 23 | # The below widgets were adapted from the example usage at 24 | # matplotlib.org/api/widgets_api.html 25 | # 26 | 27 | # global variables accessable to the `selector` functions 28 | regions = [ ] 29 | plot = None 30 | 31 | def OnSelect_Deblend(eclick, erelease): 32 | '''Selector function specific to the Deblend() function.''' 33 | 34 | # region selected 35 | x1, x2 = eclick.xdata, erelease.xdata 36 | 37 | # make sure we are in the correct order 38 | if x2 < x1: 39 | x1, x2 = x2, x1 40 | 41 | # grab `regions` list 42 | global regions 43 | global plot 44 | 45 | # x1 *= plot.wave[0].unit 46 | # x2 *= plot.wave[0].unit 47 | 48 | regions.append([x1, x2]) 49 | print(x1, x2) 50 | 51 | # create new Spectrum of region 52 | # data = plot.data[0].copy() 53 | # wave = plot.wave[0].copy() 54 | # data = data[ wave[wave < x2] > x1 ] 55 | # wave = wave[ wave[wave < x2] > x1 ] 56 | # 57 | # this_region = Spectrum(data, wave) 58 | 59 | # append selected region to list 60 | # regions.append( SPlot(this_region) ) 61 | 62 | # add it to the current `SPlot` 63 | # plot.overlay( regions[-1] ) 64 | 65 | # make it a red dashed line 66 | # plot.marker[-1] = 'r-' 67 | 68 | # push updates 69 | # plot.refresh() 70 | 71 | def toggle_selector(event): 72 | '''activate/deactivate selection tool''' 73 | if event.key in ['Q', 'q'] and toggle_selector.RS.active: 74 | print(' Selector deactivated.') 75 | toggle_selector.RS.set_active(False) 76 | if event.key in ['A', 'a'] and not toggle_selector.RS.active: 77 | print(' Selector activated.') 78 | toggle_selector.RS.set_active(True) 79 | 80 | def Deblend(spectrum, **kwargs): 81 | ''' 82 | Remove feature from `spectrum` via division. The user manually selects 83 | regions in the `spectrum` and a `line` is fit to and subtracted. The 84 | original `spectrum` object is modified. 85 | 86 | If `spectrum` is actually a Spectrum object (Spectrum.Spectrum), a `SPlot` 87 | figure is created for it (without a label). Alternatively, a SPlot object 88 | can be directly given. 89 | ''' 90 | if type(spectrum) is Spectrum: 91 | # create a SPlot object out of the spectrum 92 | spectrum = SPlot(spectrum) 93 | elif type(spectrum) is not SPlot: 94 | raise MeasureError('From Deblend(), argument must be either of type ' 95 | 'Spectrum or SPlot!') 96 | 97 | # make spectrum window active 98 | spectrum.draw() 99 | 100 | # pass off spectrum to global plot object 101 | global plot 102 | global regions 103 | plot = spectrum 104 | 105 | domain = ' Select a region, the press enter: ' 106 | # while domain: 107 | 108 | selector = RectangleSelector(plot.ax, OnSelect_Deblend, 109 | minspanx=1, minspany=1) 110 | 111 | #connect('key_press_event', toggle_selector) 112 | # selector = RectangleSelector(plot.ax, OnSelect_Deblend, 113 | # drawtype='box', useblit=True, 114 | # button=[1,3], # don't use middle button 115 | # minspanx=5, minspany=5, 116 | # spancoords='pixels') 117 | 118 | #domain = input(domain) 119 | #print('Region -> ', regions[-1]) 120 | 121 | #print('Select regions for continuum fitting: ') 122 | return plot 123 | -------------------------------------------------------------------------------- /SLiPy/Simbad.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 3 | # slipy/SLiPy/Simbad.py 4 | """ 5 | usage: Simbad.py @Attribute [**kwargs] 6 | 7 | This module allows the user to query the SIMBAD astronomical database from 8 | inside Python or shell commands/scripts. 9 | 10 | The 'Attribute' points to a function within this module and indicates 11 | what is to be run. Execute 'Simbad.py @Attribute help' for usage details of 12 | a specific function. Currently available attributes are: `Position`, 13 | `Distance`, and `Sptype`. 14 | 15 | The identifier names can be anything recognized by SIMBAD (e.g., Regulus, 16 | "alpha leo", "HD 121475", "del cyg", etc ...) if the name is two parts make 17 | sure to use quotation to enclose it. 18 | 19 | The **kwargs is the conventional reference to Python keyword arguments. 20 | These should be specific to the 'Attribute' being pointed to. 21 | """ 22 | from sys import argv, exit # , version_info 23 | from urllib.request import urlopen 24 | 25 | from astropy import units as u 26 | 27 | from .. import SlipyError 28 | from ..Framework.Command import Parse, CommandError 29 | from ..Framework.Options import Options, OptionsError 30 | from ..Framework.Measurement import Measurement 31 | 32 | 33 | class SimbadError(SlipyError): 34 | """ 35 | Exception specific to the Simbad module. 36 | """ 37 | pass 38 | 39 | 40 | def URLEncoded(url): 41 | """ 42 | URLEncoded( string ): 43 | Return a string with reserved url characters encoded. 44 | """ 45 | # check argument type 46 | if type(url) is not str: 47 | raise SimbadError('URLEncoded function expects type str!') 48 | 49 | # percent-encoded pair dictionary 50 | encoded = { 51 | ' ': '%20', 52 | '%': '%25', 53 | '#': '%23', 54 | '(': '%28', 55 | ')': '%29', 56 | '|': '%7c', 57 | '+': '%2b' 58 | } 59 | 60 | return ''.join([ 61 | encoded[character] if character in encoded else character 62 | for character in list(url)]) 63 | 64 | 65 | def Script(identifier, criteria): 66 | """ 67 | Script( criteria ): 68 | 69 | URL script for the SIMBAD astronomical database with added 70 | URLEncoded(criteria). 71 | """ 72 | script = [ 73 | 'http://simbad.u-strasbg.fr/simbad/sim-script?', 74 | 'script=format%20object%20%22', URLEncoded(criteria), 75 | '%22%0a', URLEncoded(identifier) 76 | ] 77 | 78 | return ''.join(script) 79 | 80 | 81 | class Query: 82 | """ 83 | Query( identifier, criteria, **kwargs ): 84 | 85 | Class for querying the SIMBAD astronomical database for 'citeria' 86 | of 'identifier'. 87 | 88 | kwargs = { 89 | 'parse' : True, # extract relavent data from SIMBAD return file 90 | 'dtype' : float, # output datatype 91 | } 92 | """ 93 | def __init__(self, identifier, criteria, default=float, **kwargs): 94 | """ 95 | Initiate query to SIMBAD database. 96 | """ 97 | # check argument types 98 | if type(identifier) is not str or type(criteria) is not str: 99 | raise SimbadError('Simbad.Query function expects str' 100 | 'types for arguments.') 101 | 102 | try: 103 | # keyword argument options for Query 104 | self.options = Options( kwargs, 105 | { 106 | 'parse' : True , # parse SIMBAD return file 107 | 'full' : False , # return full line of info 108 | 'dtype' : default , # convert return data 109 | 'is_main': False # called from Main() 110 | }) 111 | 112 | # assignments 113 | self.parse = self.options('parse') 114 | self.full = self.options('full') 115 | self.dtype = self.options('dtype') 116 | self.is_main = self.options('is_main') 117 | 118 | # query SIMBAD database 119 | with urlopen( Script(identifier, criteria) ) as response: 120 | self.data = str( response.read().decode('utf-8') ).strip() 121 | 122 | except OptionsError as err: 123 | print('\n --> OptionsError:', err.msg ) 124 | raise SimbadError('Simbad.Query was not constructed ' 125 | 'for `{}`'.format(identifier)) 126 | 127 | except URLError as error: 128 | raise SimbadError('Failed to contact SIMBAD database for' 129 | ' `{}`'.format(identifier) ) 130 | 131 | if 'not found' in self.data or 'error' in self.data: 132 | raise SimbadError('`{}` could not be resolved by SIMBAD.' 133 | .format(identifier)) 134 | 135 | if self.parse: 136 | # pre-parse operation common to all criteria 137 | self.data = self.data.split('data')[-1] 138 | 139 | def __call__(self): 140 | """ 141 | Retrieve data from Query 142 | """ 143 | return self.data 144 | 145 | def Position( identifier, **kwargs ): 146 | """ 147 | Position( identifier, **kwargs ): 148 | 149 | Handle to the Query class with criteria='%C00(d;C)'. 150 | """ 151 | query = Query( identifier, '%COO(d;C)', **kwargs ) 152 | 153 | if query.full: 154 | query.data = query.data.split('\n')[-1] 155 | 156 | elif query.parse: 157 | # extract relavent data 158 | query.data = query.data.split()[-1].split('+') 159 | if len( query.data ) == 1: 160 | # dec had '-' not '+' 161 | query.data = query.data[0].split('-') 162 | query.data[1] = '-' + query.data[1] 163 | # return formatted data type 164 | query.data = [ query.dtype(pos) * u.degree for pos in query.data ] 165 | 166 | if query.is_main: 167 | if query.full or not query.parse: 168 | print( query() ) 169 | else: 170 | print('{0:.2f} {1:.2f}'.format(*query())) 171 | 172 | else: return query.data 173 | 174 | def Distance( identifier, **kwargs ): 175 | """ 176 | Distance( identifier, **kwargs ): 177 | 178 | Handle to the Query class with criteria='%PLX' 179 | """ 180 | query = Query( identifier, '%PLX', **kwargs ) 181 | 182 | if query.full: 183 | data = query.data.split('\n')[-1] 184 | 185 | elif query.parse: 186 | 187 | # extract relavent data 188 | data = query.data.split() 189 | 190 | if data[1] == '~': 191 | # nothing found! 192 | raise SimbadError('No distance found for `{}`'.format(identifier)) 193 | try: 194 | 195 | # convert milli-arcseconds to parsecs 196 | result = u.pc / ( query.dtype(data[1]) / 1000.0 ) 197 | 198 | except ValueError as err: 199 | raise SimbadError('Use a numeric type for Simbad.Distance!') 200 | 201 | if data[2][0] == '[' and data[2][-1] == ']': 202 | # TODO: understand SIMBAD error format 203 | # an uncertainty was given by SIMBAD 204 | # uncertainty = u.pc * 0.001 * query.dtype(data[1]) * query.dtype(data[2][1:-1]) / ( 205 | # 0.001 * query.dtype(data[1]) )**2 206 | # uncertainty = result * query.dtype(data[2][1:-1]) 207 | uncertainty = None 208 | 209 | else: uncertainty = None 210 | 211 | else: data = query.data 212 | 213 | if query.is_main: 214 | if query.full or not query.parse: 215 | print( data ) 216 | else: 217 | print( '{0:.2f}'.format( data ) ) 218 | 219 | elif not query.parse: 220 | return data 221 | 222 | else: return result 223 | 224 | # Measurement(result, error=uncertainty, name='Distance', 225 | # notes='Retrieved from SIMBAD database for `{}`'.format(identifier)) 226 | 227 | def Sptype(identifier, **kwargs): 228 | """ 229 | Handle to the Query class with criteria='%SP'. 230 | """ 231 | query = Query(identifier, '%SP', **kwargs) 232 | 233 | if query.full: 234 | # return last full line of query 235 | query.data = query.data.split('\n')[-1] 236 | 237 | elif query.parse: 238 | # extract relavent data 239 | query.data = query.data.split()[1] 240 | 241 | if query.is_main: 242 | print( query() ) 243 | 244 | else: return query() 245 | 246 | def IDList(identifier, **kwargs): 247 | """ 248 | Handle to the Query class with criteria='%IDLIST'. 249 | With `parse` = True, return a list of alternate IDs for 250 | the `identifier` provided. 251 | """ 252 | query = Query(identifier, '%IDLIST', **kwargs) 253 | 254 | if query.parse: 255 | # extract relavent data 256 | query.data = query.data.split(':')[-1].strip().split('\n') 257 | 258 | if query.is_main: 259 | for line in query.data: 260 | print(line) 261 | 262 | else: return query() 263 | 264 | def Main( clargs ): 265 | """ 266 | Main function. See __doc__ for details. 267 | """ 268 | 269 | if len(clargs) < 2: 270 | # show usage 271 | print( __doc__ ) 272 | return 0 273 | 274 | # Available functions for execution 275 | executable = { 276 | 'Distance' : Distance, # search for parsecs 277 | 'Position' : Position, # search for ra, dec 278 | 'Sptype' : Sptype , # search for spectral types 279 | 'IDList' : IDList # search for IDs 280 | } 281 | 282 | 283 | try: 284 | 285 | # Parse command line arguments 286 | function, args, kwargs = Parse( clargs[1:] ) 287 | 288 | if not args and not kwargs or args[0] == 'help': 289 | # show function usage 290 | print( executable[function].__doc__ ) 291 | return 0 292 | 293 | # run execution 294 | for identifier in args: 295 | executable[function]( identifier, is_main=True, **kwargs ) 296 | 297 | return 0 298 | 299 | except CommandError as err: 300 | print(' --> CommandError:', err.msg) 301 | return 1 302 | 303 | except KeyError as key: 304 | print(' --> {} was not a recognized function.'.format(key)) 305 | return 1 306 | 307 | except SimbadError as err: 308 | # don't let uncaught self exception pass if from main. 309 | print(' --> SimbadError:', err.msg) 310 | return 1 311 | 312 | except Exception as err: 313 | print(' --> Unrecognized error with query for `{}`' 314 | .format(args[0])) 315 | print(' --> Exception: `{}`'.format(err)) 316 | return 1 317 | 318 | if __name__ == '__main__': 319 | # Main return 0 or 1 320 | exit( Main( argv ) ) 321 | -------------------------------------------------------------------------------- /SLiPy/Telluric.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/SLiPy/Tellucic.Py 4 | """ 5 | Telluric - Corrections for atmospheric absoption lines. 6 | """ 7 | import numpy as np 8 | from astropy import units as u 9 | 10 | from .. import SlipyError 11 | from ..Framework.Options import Options, OptionsError 12 | from .Correlate import Xcorr, CorrelateError 13 | from .Spectrum import Spectrum, SpectrumError 14 | 15 | class TelluricError(SlipyError): 16 | """ 17 | Exception specific to the Telluric module. 18 | """ 19 | pass 20 | 21 | def Correct(spectrum, *calibration, **kwargs): 22 | """ 23 | Correct(spectrum, *calibration, **kwargs): 24 | 25 | Perform a telluric correction on `spectrum` with one or more 26 | `calibration` spectra. If more than one calibration spectrum is 27 | provided, the one with the best fit after performing both a 28 | horizontal cross correlation and a vertical amplitude fit is used. 29 | The spectrum and all the calibration spectra must have the same 30 | number of pixels (elements). If a horizontal shift in the calibration 31 | spectra is appropriate, only the corresponding range of the spectrum 32 | is divided out! 33 | 34 | kwargs = { 35 | lag : 25 , # pixel shift limit for XCorr() 36 | range:(0.5, 2.0, 151), # numpy.linspace for amplitude trials 37 | } 38 | """ 39 | try: 40 | # default keyword arguments 41 | options = Options( kwargs, 42 | { 43 | 'lag' : 25 , # pixel shift limit for XCorr() 44 | 'range':(0.5, 2.0, 151) # numpy.linspace for amplitude trials 45 | }) 46 | 47 | # check arguments 48 | if not calibration: 49 | raise TelluricError('At least one `calibration` spectrum ' 50 | 'needs to be provided for Telluric.Correction().') 51 | 52 | if type(spectrum) is not Spectrum: 53 | raise TelluricError('Telluric.Correct() expects all arguments ' 54 | 'of type Spectrum.') 55 | 56 | for cal in calibration: 57 | if type(cal) is not Spectrum: 58 | raise TelluricError('Telluric.Correct() expects all ' 59 | 'arguments to be of type Spectrum.') 60 | if spectrum not in cal: 61 | raise TelluricError('Telluric.Correct() expects the ' 62 | '`spectrum` domain to be equivalent to or at least ' 63 | 'contained within each `calibration` spectrum.') 64 | 65 | if len(options('range')) != 3: 66 | raise OptionsError('`range` expects a tuple of length 3.') 67 | 68 | # assign parameters 69 | lag = options('lag') 70 | amp = np.linspace( *options('range') ) 71 | trials = len(amp) 72 | npix = len(spectrum) 73 | 74 | except OptionsError as err: 75 | print(' --> OptionsError:', err) 76 | raise TelluricError('Inappropriate keyword arguments in ' 77 | 'Telluric.Correct().') 78 | 79 | # quit if too big of a task 80 | if trials*npix > 1e8: 81 | raise TelluricError('Telluric.Correct() is programmed to quit ' 82 | 'if it detects a request to operate on matrices with more ' 83 | 'than 10**8 elements.') 84 | 85 | # resample all the calibration spectra 86 | for cal in calibration: 87 | cal.resample(spectrum) 88 | 89 | # find best XCorr and amplitude adjustment 90 | best = None 91 | for cal in calibration: 92 | 93 | # best horizontal pixel shift 94 | shift = Xcorr( spectrum, cal, lag=lag) 95 | 96 | # build matrices with identical rows (len=npix-shift) 97 | if shift < 0: 98 | 99 | calmatrix = np.tile(cal.data[:shift], (trials, 1)) 100 | objmatrix = np.tile(spectrum.data[-shift:], (trials, 1)) 101 | 102 | elif shift > 0: 103 | 104 | calmatrix = np.tile(cal.data[shift:], (trials, 1)) 105 | objmatrix = np.tile(spectrum.data[:-shift], (trials, 1)) 106 | 107 | else: 108 | 109 | calmatrix = np.tile(cal.data, (trials, 1)) 110 | objmatrix = np.tile(spectrum.data, (trials, 1)) 111 | 112 | # amplitude matrix has identical columns 113 | size = np.shape(calmatrix)[1] 114 | ampmatrix = np.tile(amp, (size,1)).T 115 | 116 | # remove units for dimensionless operations 117 | calmatrix = calmatrix.value 118 | objmatrix = objmatrix.value 119 | 120 | # flip arrays for amplification 121 | diff = objmatrix - (1 - (1 - calmatrix) * ampmatrix) 122 | 123 | # compute the RMS for each trial 124 | rmsvector = np.sqrt(( diff**2 ).sum(axis=1) / size) 125 | 126 | if not best: 127 | 128 | # if first pass, assign to `best` 129 | best = ( rmsvector.min(), rmsvector.argmin(), shift, cal ) 130 | 131 | elif rmsvector.min() < best[0]: 132 | 133 | # this fit is better, save it to `best` 134 | best = ( rmsvector.min(), rmsvector.argmin(), shift, cal ) 135 | 136 | # results of calibration fitting 137 | index = best[1] # amplitude 138 | shift = best[2] # XCorr 139 | cal = best[3] # which calibration spectrum 140 | 141 | # we can't update an attribute... 142 | update = spectrum.data.value 143 | 144 | # divide spectrum 145 | if shift < 0: 146 | update[-shift:] /= 1 - (1 - cal.data[:shift].value) * amp[index] 147 | elif shift > 0: 148 | update[:-shift] /= 1 - (1 - cal.data[shift:].value) * amp[index] 149 | else: 150 | update /= 1 - (1 - cal.data.value) * amp[index] 151 | 152 | # re-incorporate units 153 | spectrum.data = update * u.Unit(spectrum.yunits) 154 | -------------------------------------------------------------------------------- /SLiPy/Velocity.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/SLiPy/Velocity.Py 4 | """ 5 | Radial velocity corrections for 1D spectra. 6 | """ 7 | 8 | from astropy.io import fits as pyfits 9 | from astropy.constants import c 10 | from astropy import units as u 11 | 12 | from .. import SlipyError 13 | from ..astrolibpy.astrolib.helcorr import helcorr 14 | from .Fits import Find, RFind 15 | from .Observatory import Observatory 16 | from .Spectrum import Spectrum 17 | from ..Framework.Options import Options, OptionsError 18 | from ..Framework.Display import Monitor, DisplayError 19 | 20 | class VelocityError(SlipyError): 21 | """ 22 | Exception specific to the Velocity module 23 | """ 24 | pass 25 | 26 | def HeaderInfo( fpath ): 27 | """ 28 | HeaderInfo( fpath ): 29 | 30 | Helper function of IrafInput. 31 | 32 | Return a formatted string containing the year, month, and day of 33 | observation from the FITS file with name 'fpath', as well as the 34 | univeral time of observation and the right ascension and declination 35 | of the target. 36 | """ 37 | try: 38 | with pyfits.open(fpath) as hdulist: 39 | ra = ':'.join( hdulist[0].header['alpha'].split() ) 40 | dec = ':'.join( hdulist[0].header['delta'].split() ) 41 | date_obs = hdulist[0].header['date-obs'] 42 | date, UT = date_obs.split('T') 43 | year, month, day = [ x.lstrip('0') for x in date.split('-') ] 44 | 45 | info = '{:>5} {:>2} {:>2} {:>8} {:>11} {:>12}\n'.format( 46 | year, month, day, UT, ra, dec ) 47 | 48 | except IOError as error: 49 | raise VelocityError('Failure in `{}` from HeaderInfo()'.format(fpath)) 50 | 51 | return info 52 | 53 | def IrafInput( *files, **kwargs): 54 | """ 55 | IrafInput( *args, **kwargs ): 56 | 57 | Build an input file for IRAF's rvcorrect task. 58 | 59 | `files` should be a list of FITS file names to build the output table for. 60 | The user can optionally specify a 'toplevel' directory to search 61 | (recursively!) under fitting the 'pattern' (default=*.fits). This results 62 | of this pattern search will be added to the list of file names in 'args' 63 | (if any given). 64 | 65 | kwargs = { 66 | 'toplevel' : '' , # search `toplevel` directory for files 67 | 'pattern' : '*.fits', # files under `toplevel` fitting `pattern` 68 | 'recursive': False , # search recusively under `toplevel` 69 | 'outfile' : '' # write lines to file named `outfile` 70 | } 71 | """ 72 | try: 73 | # dictionary of options 74 | options = Options( kwargs, 75 | { 76 | 'toplevel' : '' , # search `toplevel` directory for files 77 | 'pattern' : '*.fits', # files under `toplevel` fitting `pattern` 78 | 'recursive': False , # search recursively under `toplevel` 79 | 'outfile' : '' # write lines to file named `outfile` 80 | }) 81 | 82 | # convert options types 83 | toplevel = options('toplevel') 84 | pattern = options('pattern') 85 | recursive = options('recursive') 86 | outfile = options('outfile') 87 | 88 | files = list(files) 89 | 90 | if toplevel: 91 | # search for files matching `pattern` 92 | find = RFind if recursive else Find 93 | files += find( toplevel, pattern ) 94 | 95 | # get info from files 96 | info = [ HeaderInfo(fpath) for fpath in files ] 97 | 98 | if outfile: 99 | with open( outfile, 'w' ) as fp: 100 | fp.writelines( info ) 101 | 102 | return info 103 | 104 | except OptionsError as err: 105 | print(' --> OptionsError:', err) 106 | raise VelocityError('Failed to construct table information in ' 107 | 'IrafInput().') 108 | 109 | def HelioCorrect( obs, *spectra, **kwargs ): 110 | """ 111 | Perform heliocentric velocity corrects on `spectra` based on 112 | `obs`ervatory information (longitude, latitude, altitude) and the 113 | member attributes, ra (right ascension), dec (declination), and jd 114 | (julian date) from the `spectra`. 115 | """ 116 | try: 117 | 118 | # define function parameters 119 | options = Options( kwargs, 120 | { 121 | 'verbose': False # display messages, progress 122 | }) 123 | 124 | # assign parameters 125 | verbose = options('verbose') 126 | 127 | # check `obs` type 128 | if not issubclass( type(obs), Observatory): 129 | raise VelocityError('HelioCorrect() expects its first argument to ' 130 | 'be derived from the Observatory class.') 131 | elif ( not hasattr(obs, 'latitude') or not hasattr(obs,'longitude') or 132 | not hasattr(obs, 'altitude') ): 133 | raise VelocityError('HelioCorrect expects `obs`ervatory to have ' 134 | 'all three: latitude, longitude, and altitude attributes.') 135 | 136 | # check `spectra` arguments 137 | for a, spectrum in enumerate(spectra): 138 | if type(spectrum) is not Spectrum: 139 | raise VelocityError('HelioCorrect() expects all `spectrum` ' 140 | 'arguments to be of type Spectrum.') 141 | if not spectrum.ra or not spectrum.dec or not spectrum.jd: 142 | raise VelocityError('Spectrum {} lacks one or all of `ra`, ' 143 | '`dec`, and `jd`; from HelioCorrect().'.format(a)) 144 | if not hasattr(spectrum.ra, 'unit'): 145 | raise VelocityError('From HelioCorrect(), in spectrum {}, ' 146 | '`ra` doesn`t have units!'.format(a)) 147 | if not hasattr(spectrum.dec, 'unit'): 148 | raise VelocityError('From HelioCorrect(), in spectrum {}, ' 149 | '`dec` doesn`t have units!'.format(a)) 150 | if not hasattr(spectrum.jd, 'unit'): 151 | raise VelocityError('From HelioCorrect(), in spectrum {}, ' 152 | '`jd` doesn`t have units!'.format(a)) 153 | 154 | if verbose: 155 | display = Monitor() 156 | print(' Running HelioCorrect on {} spectra ...' 157 | .format(len(spectra))) 158 | 159 | for a, spectrum in enumerate(spectra): 160 | 161 | # heliocentric velocity correction in km s^-1, 162 | # the `astrolibpy...helcorr` function doesn't work with units, 163 | # so I convert to appropriate units and strip them. 164 | hcorr = helcorr( 165 | obs.longitude.to(u.degree).value, 166 | obs.latitude.to(u.degree).value, 167 | obs.altitude.to(u.meter).value, 168 | spectrum.ra.to(u.hourangle).value, 169 | spectrum.dec.to(u.degree).value, 170 | spectrum.jd.to(u.day).value 171 | )[1] * u.km / u.second 172 | 173 | # apply correction to wave vector, 174 | # del[Lambda] / Lambda = del[V] / c 175 | spectrum.wave -= spectrum.wave * hcorr / c.to(u.km / u.second) 176 | 177 | # show progress if desired 178 | if verbose: display.progress(a, len(spectra)) 179 | 180 | # finalize progress bar (erase) 181 | if verbose: display.complete() 182 | 183 | except OptionsError as err: 184 | print(' --> OptionsError:', err) 185 | raise VelocityError('Failed to perform HelioCorrect() task.') 186 | 187 | except DisplayError as err: 188 | print(' --> DisplayError:', err) 189 | raise VelocityError('Exception from Display.Monitor in HelioCorrect().') 190 | 191 | 192 | def BaryCorrect( obs, *spectra, **kwargs ): 193 | """ 194 | Perform barycentric velocity corrects on `spectra` based on 195 | `obs`ervatory information (longitude, latitude, altitude) and the 196 | member attributes, ra (right ascension), dec (declination), and jd 197 | (julian date) from the `spectra`. 198 | """ 199 | try: 200 | 201 | # define function parameters 202 | options = Options( kwargs, 203 | { 204 | 'verbose': False # display messages, progress 205 | }) 206 | 207 | # assign parameters 208 | verbose = options('verbose') 209 | 210 | # check `obs` type 211 | if not issubclass( type(obs), Observatory): 212 | raise VelocityError('HelioCorrect() expects its first argument to ' 213 | 'be derived from the Observatory class.') 214 | elif ( not hasattr(obs, 'latitude') or not hasattr(obs,'longitude') or 215 | not hasattr(obs, 'altitude') ): 216 | raise VelocityError('HelioCorrect expects `obs`ervatory to have ' 217 | 'all three: latitude, longitude, and altitude attributes.') 218 | 219 | # check `spectra` arguments 220 | for a, spectrum in enumerate(spectra): 221 | if type(spectrum) is not Spectrum: 222 | raise VelocityError('HelioCorrect() expects all `spectrum` ' 223 | 'arguments to be of type Spectrum.') 224 | if not spectrum.ra or not spectrum.dec or not spectrum.jd: 225 | raise VelocityError('Spectrum {} lacks one or all of `ra`, ' 226 | '`dec`, and `jd`; from HelioCorrect().'.format(a)) 227 | 228 | if verbose: 229 | display = Monitor() 230 | print(' Running HelioCorrect on {} spectra ...' 231 | .format(len(spectra))) 232 | 233 | for a, spectrum in enumerate(spectra): 234 | 235 | # heliocentric velocity correction in km s^-1, 236 | # the `astrolibpy...helcorr` function doesn't work with units, 237 | # so I convert to appropriate units and strip them. 238 | hcorr = helcorr( 239 | obs.longitude.to(u.degree).value, 240 | obs.latitude.to(u.degree).value, 241 | obs.altitude.to(u.meter).value, 242 | spectrum.ra.to(u.hourangle).value, 243 | spectrum.dec.to(u.degree).value, 244 | spectrum.jd.to(u.second).value 245 | )[0] * u.Unit('km s-1') 246 | 247 | # apply correction to wave vector, 248 | # del[Lambda] / Lambda = del[V] / c 249 | spectrum.wave -= spectrum.wave * hcorr / c 250 | 251 | # show progress if desired 252 | if verbose: display.progress(a, len(spectra)) 253 | 254 | # finalize progress bar (erase) 255 | if verbose: display.complete() 256 | 257 | except OptionsError as err: 258 | print(' --> OptionsError:', err) 259 | raise VelocityError('Failed to perform HelioCorrect() task.') 260 | 261 | except DisplayError as err: 262 | print(' --> DisplayError:', err) 263 | raise VelocityError('Exception from Display.Monitor in HelioCorrect().') 264 | -------------------------------------------------------------------------------- /SLiPy/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Right Reserved. 2 | # See LICENSE (GPLv3) 3 | # slipy/SLiPy/__init__.py 4 | """ 5 | SLiPy - subroutines for spectroscopy and astrophysics. 6 | """ -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Geoffrey Lentner 2015. All Rights Reserved. 2 | # See LICENSE (GPLv3) 3 | # SLiPy/__init__.py 4 | """ 5 | SLiPy - A Spectroscopy and astrophysics Library for Python 3 6 | 7 | This Python package is an expanding code base for doing computational 8 | astronomy, particularly spectroscopy. It contains both a *Spectrum* class 9 | for handling spectra as objects (with +, -, \*, /, etc... operations defined) 10 | and a growing suite of analysis tools. 11 | """ 12 | 13 | # base exception class for whole project, module exception classes will 14 | # be derived from here. 15 | class SlipyError(Exception): 16 | pass 17 | 18 | # exposed modules 19 | from .SLiPy import Fits, Simbad, Correlate, Telluric, Velocity, \ 20 | Observatory, Montage, Plot, Spectrum, Measure, Profile 21 | 22 | # elevate `Spectrum` to the package level 23 | from .SLiPy.Spectrum import Spectrum 24 | -------------------------------------------------------------------------------- /astrolibpy/README: -------------------------------------------------------------------------------- 1 | This library contains a few useful routines I wrote or I converted from IDL. 2 | 3 | My contacts are: 4 | Sergey Koposov koposov@ast.cam.ac.uk 5 | Institute of Astronomy, University of Cambridge 6 | Madingley road, CB3 0HA, Cambridge, UK 7 | 8 | If you have found a bug or have a patch, you can send them to me. 9 | With my library I do not promise a stable interface, so beware. 10 | 11 | The licensing for the programs I wrote myself is GPL3. For all other 12 | programs (mainly converted from IDL) I guess the license is either BSD or 13 | they are in public domain. 14 | 15 | Here is the quick list of the functions I implemented: 16 | TBW -------------------------------------------------------------------------------- /astrolibpy/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Python sub-packages included here 3 | # 4 | 5 | # 1) astro/ 6 | # 2) etc/ 7 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Python sub-packages included here 3 | # 4 | 5 | # 1) astro/ 6 | # 2) etc/ 7 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/bprecess.py: -------------------------------------------------------------------------------- 1 | from numpy import array, sqrt, zeros, sin, cos, arange, arcsin,\ 2 | arctan2, transpose, concatenate, ndarray, pi, dot, deg2rad,\ 3 | rad2deg 4 | 5 | def bprecess(ra0, dec0, mu_radec=None, parallax=None, rad_vel=None, epoch=None): 6 | """ 7 | NAME: 8 | BPRECESS 9 | PURPOSE: 10 | Precess positions from J2000.0 (FK5) to B1950.0 (FK4) 11 | EXPLANATION: 12 | Calculates the mean place of a star at B1950.0 on the FK4 system from 13 | the mean place at J2000.0 on the FK5 system. 14 | 15 | CALLING SEQUENCE: 16 | bprecess, ra, dec, ra_1950, dec_1950, [ MU_RADEC = , PARALLAX = 17 | RAD_VEL =, EPOCH = ] 18 | 19 | INPUTS: 20 | RA,DEC - Input J2000 right ascension and declination in *degrees*. 21 | Scalar or N element vector 22 | 23 | OUTPUTS: 24 | RA_1950, DEC_1950 - The corresponding B1950 right ascension and 25 | declination in *degrees*. Same number of elements as 26 | RA,DEC but always double precision. 27 | 28 | OPTIONAL INPUT-OUTPUT KEYWORDS 29 | MU_RADEC - 2xN element double precision vector containing the proper 30 | motion in seconds of arc per tropical *century* in right 31 | ascension and declination. 32 | PARALLAX - N_element vector giving stellar parallax (seconds of arc) 33 | RAD_VEL - N_element vector giving radial velocity in km/s 34 | 35 | The values of MU_RADEC, PARALLAX, and RADVEL will all be modified 36 | upon output to contain the values of these quantities in the 37 | B1950 system. The parallax and radial velocity will have a very 38 | minor influence on the B1950 position. 39 | 40 | EPOCH - scalar giving epoch of original observations, default 2000.0d 41 | This keyword value is only used if the MU_RADEC keyword is not set. 42 | NOTES: 43 | The algorithm is taken from the Explanatory Supplement to the 44 | Astronomical Almanac 1992, page 186. 45 | Also see Aoki et al (1983), A&A, 128,263 46 | 47 | BPRECESS distinguishes between the following two cases: 48 | (1) The proper motion is known and non-zero 49 | (2) the proper motion is unknown or known to be exactly zero (i.e. 50 | extragalactic radio sources). In this case, the reverse of 51 | the algorithm in Appendix 2 of Aoki et al. (1983) is used to 52 | ensure that the output proper motion is exactly zero. Better 53 | precision can be achieved in this case by inputting the EPOCH 54 | of the original observations. 55 | 56 | The error in using the IDL procedure PRECESS for converting between 57 | B1950 and J1950 can be up to 12", mainly in right ascension. If 58 | better accuracy than this is needed then BPRECESS should be used. 59 | 60 | An unsystematic comparison of BPRECESS with the IPAC precession 61 | routine (http://nedwww.ipac.caltech.edu/forms/calculator.html) always 62 | gives differences less than 0.15". 63 | EXAMPLE: 64 | The SAO2000 catalogue gives the J2000 position and proper motion for 65 | the star HD 119288. Find the B1950 position. 66 | 67 | RA(2000) = 13h 42m 12.740s Dec(2000) = 8d 23' 17.69'' 68 | Mu(RA) = -.0257 s/yr Mu(Dec) = -.090 ''/yr 69 | 70 | IDL> mu_radec = 100D* [ -15D*.0257, -0.090 ] 71 | IDL> ra = ten(13, 42, 12.740)*15.D 72 | IDL> dec = ten(8, 23, 17.69) 73 | IDL> bprecess, ra, dec, ra1950, dec1950, mu_radec = mu_radec 74 | IDL> print, adstring(ra1950, dec1950,2) 75 | ===> 13h 39m 44.526s +08d 38' 28.63" 76 | 77 | REVISION HISTORY: 78 | Written, W. Landsman October, 1992 79 | Vectorized, W. Landsman February, 1994 80 | Treat case where proper motion not known or exactly zero November 1994 81 | Handling of arrays larger than 32767 Lars L. Christensen, march, 1995 82 | Converted to IDL V5.0 W. Landsman September 1997 83 | Fixed bug where A term not initialized for vector input 84 | W. Landsman February 2000 85 | Converted to python Sergey Koposov july 2010 86 | """ 87 | 88 | scal = True 89 | if isinstance(ra0, ndarray): 90 | ra = ra0 91 | dec = dec0 92 | n = ra.size 93 | scal = False 94 | else: 95 | n = 1 96 | ra = array([ra0]) 97 | dec = array([dec0]) 98 | 99 | if rad_vel is None: 100 | rad_vel = zeros(n) 101 | else: 102 | if not isinstance(rad_vel, ndarray): 103 | rad_vel = array([rad_vel],dtype=float) 104 | if rad_vel.size != n: 105 | raise Exception('ERROR - RAD_VEL keyword vector must be of the same length as RA and DEC') 106 | 107 | if (mu_radec is not None): 108 | if (array(mu_radec).size != 2 * n): 109 | raise Exception('ERROR - MU_RADEC keyword (proper motion) be dimensioned (2,' + strtrim(n, 2) + ')') 110 | mu_radec = mu_radec * 1. 111 | 112 | if parallax is None: 113 | parallax = zeros(n) 114 | else: 115 | if not isinstance(parallax, ndarray): 116 | parallax = array([parallax],dtype=float) 117 | 118 | if epoch is None: 119 | epoch = 2000.0e0 120 | 121 | radeg = 180.e0 / pi 122 | sec_to_radian = lambda x : deg2rad(x/3600.) 123 | 124 | m = array([array([+0.9999256795e0, -0.0111814828e0, -0.0048590040e0, -0.000551e0, -0.238560e0, +0.435730e0]), 125 | array([+0.0111814828e0, +0.9999374849e0, -0.0000271557e0, +0.238509e0, -0.002667e0, -0.008541e0]), 126 | array([+0.0048590039e0, -0.0000271771e0, +0.9999881946e0, -0.435614e0, +0.012254e0, +0.002117e0]), 127 | array([-0.00000242389840e0, +0.00000002710544e0, +0.00000001177742e0, +0.99990432e0, -0.01118145e0, -0.00485852e0]), 128 | array([-0.00000002710544e0, -0.00000242392702e0, +0.00000000006585e0, +0.01118145e0, +0.99991613e0, -0.00002716e0]), 129 | array([-0.00000001177742e0, +0.00000000006585e0, -0.00000242404995e0, +0.00485852e0, -0.00002717e0, +0.99996684e0])]) 130 | 131 | a_dot = 1e-3 * array([1.244e0, -1.579e0, -0.660e0]) #in arc seconds per century 132 | 133 | ra_rad = deg2rad(ra) 134 | dec_rad = deg2rad(dec) 135 | cosra = cos(ra_rad) 136 | sinra = sin(ra_rad) 137 | cosdec = cos(dec_rad) 138 | sindec = sin(dec_rad) 139 | 140 | dec_1950 = dec * 0. 141 | ra_1950 = ra * 0. 142 | 143 | for i in range(n): 144 | 145 | # Following statement moved inside loop in Feb 2000. 146 | a = 1e-6 * array([-1.62557e0, -0.31919e0, -0.13843e0]) #in radians 147 | 148 | r0 = array([cosra[i] * cosdec[i], sinra[i] * cosdec[i], sindec[i]]) 149 | 150 | if (mu_radec is not None): 151 | 152 | mu_a = mu_radec[i,0] 153 | mu_d = mu_radec[i,1] 154 | r0_dot = array([-mu_a * sinra[i] * cosdec[i] - mu_d * cosra[i] * sindec[i], mu_a * cosra[i] * cosdec[i] - mu_d * sinra[i] * sindec[i], mu_d * cosdec[i]]) + 21.095e0 * rad_vel[i] * parallax[i] * r0 155 | 156 | else: 157 | r0_dot = array([0.0e0, 0.0e0, 0.0e0]) 158 | 159 | r_0 = concatenate((r0, r0_dot)) 160 | r_1 = transpose(dot(transpose(m), transpose(r_0))) 161 | 162 | # Include the effects of the E-terms of aberration to form r and r_dot. 163 | 164 | r1 = r_1[0:3] 165 | r1_dot = r_1[3:6] 166 | 167 | if mu_radec is None: 168 | r1 = r1 + sec_to_radian ( r1_dot * (epoch - 1950.0e0) / 100. ) 169 | a = a + sec_to_radian ( a_dot * (epoch - 1950.0e0) / 100. ) 170 | 171 | x1 = r_1[0] ; y1 = r_1[1] ; z1 = r_1[2] 172 | rmag = sqrt(x1 ** 2 + y1 ** 2 + z1 ** 2) 173 | 174 | 175 | s1 = r1 / rmag ; s1_dot = r1_dot / rmag 176 | 177 | s = s1 178 | for j in arange(0, 3): 179 | r = s1 + a - ((s * a).sum()) * s 180 | s = r / rmag 181 | x = r[0] ; y = r[1] ; z = r[2] 182 | r2 = x ** 2 + y ** 2 + z ** 2 183 | rmag = sqrt(r2) 184 | 185 | if mu_radec is not None: 186 | r_dot = s1_dot + a_dot - ((s * a_dot).sum()) * s 187 | x_dot = r_dot[0] ; y_dot = r_dot[1] ; z_dot = r_dot[2] 188 | mu_radec[i,0] = (x * y_dot - y * x_dot) / (x ** 2 + y ** 2) 189 | mu_radec[i,1] = (z_dot * (x ** 2 + y ** 2) - z * (x * x_dot + y * y_dot)) / (r2 * sqrt(x ** 2 + y ** 2)) 190 | 191 | dec_1950[i] = arcsin(z / rmag) 192 | ra_1950[i] = arctan2(y, x) 193 | 194 | if parallax[i] > 0.: 195 | rad_vel[i] = (x * x_dot + y * y_dot + z * z_dot) / (21.095 * parallax[i] * rmag) 196 | parallax[i] = parallax[i] / rmag 197 | 198 | neg = (ra_1950 < 0) 199 | if neg.any() > 0: 200 | ra_1950[neg] = ra_1950[neg] + 2.e0 * pi 201 | 202 | ra_1950 = rad2deg(ra_1950) 203 | dec_1950 = rad2deg(dec_1950) 204 | 205 | # Make output scalar if input was scalar 206 | if scal: 207 | return ra_1950[0],dec_1950[0] 208 | else: 209 | return ra_1950, dec_1950 210 | 211 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/convolve.py: -------------------------------------------------------------------------------- 1 | from numpy import * 2 | from numpy.fft import fft2, ifft2 3 | 4 | def convolve(image, psf, ft_psf=None, ft_image=None, no_ft=None, correlate=None, auto_correlation=None): 5 | """ 6 | NAME: 7 | CONVOLVE 8 | PURPOSE: 9 | Convolution of an image with a Point Spread Function (PSF) 10 | EXPLANATION: 11 | The default is to compute the convolution using a product of 12 | Fourier transforms (for speed). 13 | 14 | CALLING SEQUENCE: 15 | 16 | imconv = convolve( image1, psf, FT_PSF = psf_FT ) 17 | or: 18 | correl = convolve( image1, image2, /CORREL ) 19 | or: 20 | correl = convolve( image, /AUTO ) 21 | 22 | INPUTS: 23 | image = 2-D array (matrix) to be convolved with psf 24 | psf = the Point Spread Function, (size < or = to size of image). 25 | 26 | OPTIONAL INPUT KEYWORDS: 27 | 28 | FT_PSF = passes out/in the Fourier transform of the PSF, 29 | (so that it can be re-used the next time function is called). 30 | FT_IMAGE = passes out/in the Fourier transform of image. 31 | 32 | /CORRELATE uses the conjugate of the Fourier transform of PSF, 33 | to compute the cross-correlation of image and PSF, 34 | (equivalent to IDL function convol() with NO rotation of PSF) 35 | 36 | /AUTO_CORR computes the auto-correlation function of image using FFT. 37 | 38 | /NO_FT overrides the use of FFT, using IDL function convol() instead. 39 | (then PSF is rotated by 180 degrees to give same result) 40 | METHOD: 41 | When using FFT, PSF is centered & expanded to size of image. 42 | HISTORY: 43 | written, Frank Varosi, NASA/GSFC 1992. 44 | Appropriate precision type for result depending on input image 45 | Markus Hundertmark February 2006 46 | Fix the bug causing the recomputation of FFT(psf) and/or FFT(image) 47 | Sergey Koposov December 2006 48 | """ 49 | 50 | n_params = 2 51 | psf_ft = ft_psf 52 | imft = ft_image 53 | noft = no_ft 54 | auto = auto_correlation 55 | 56 | sp = array(shape(psf_ft)) 57 | sif = array(shape(imft)) 58 | sim = array(shape(image)) 59 | sc = sim / 2 60 | npix = array(image, copy=0).size 61 | 62 | if image.ndim!=2 or noft!=None: 63 | if (auto is not None): 64 | message("auto-correlation only for images with FFT", inf=True) 65 | return image 66 | else: 67 | if (correlate is not None): 68 | return convol(image, psf) 69 | else: 70 | return convol(image, rotate(psf, 2)) 71 | 72 | if imft==None or (imft.ndim!=2) or imft.shape!=im.shape: #add the type check 73 | imft = ifft2(image) 74 | 75 | if (auto is not None): 76 | return roll(roll(npix * real(fft2(imft * conjugate(imft))), sc[0], 0),sc[1],1) 77 | 78 | if (ft_psf==None or ft_psf.ndim!=2 or ft_psf.shape!=image.shape or 79 | ft_psf.dtype!=image.dtype): 80 | sp = array(shape(psf)) 81 | 82 | loc = maximum((sc - sp / 2), 0) #center PSF in new array, 83 | s = maximum((sp / 2 - sc), 0) #handle all cases: smaller or bigger 84 | l = minimum((s + sim - 1), (sp - 1)) 85 | psf_ft = conjugate(image) * 0 #initialise with correct size+type according 86 | #to logic of conj and set values to 0 (type of ft_psf is conserved) 87 | psf_ft[loc[1]:loc[1]+l[1]-s[1]+1,loc[0]:loc[0]+l[0]-s[0]+1] = \ 88 | psf[s[1]:(l[1])+1,s[0]:(l[0])+1] 89 | psf_ft = ifft2(psf_ft) 90 | 91 | if (correlate is not None): 92 | conv = npix * real(fft2(imft * conjugate(psf_ft))) 93 | else: 94 | conv = npix * real(fft2(imft * psf_ft)) 95 | 96 | sc = sc + (sim % 2) #shift correction for odd size images. 97 | 98 | return roll(roll(conv, sc[0],0), sc[1],1) 99 | 100 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/cosmo_param.py: -------------------------------------------------------------------------------- 1 | def cosmo_param(omega_m=None, omega_lambda=None, omega_k=None, q0=None): 2 | """ 3 | NAME: 4 | COSMO_PARAM 5 | PURPOSE: 6 | Derive full set of cosmological density parameters from a partial set 7 | EXPLANATION: 8 | This procedure is called by LUMDIST and GALAGE to allow the user a choice 9 | in defining any two of four cosmological density parameters. 10 | 11 | Given any two of the four input parameters -- (1) the normalized matter 12 | density Omega_m (2) the normalized cosmological constant, Omega_lambda 13 | (3) the normalized curvature term, Omega_k and (4) the deceleration 14 | parameter q0 -- this program will derive the remaining two. Here 15 | "normalized" means divided by the closure density so that 16 | Omega_m + Omega_lambda + Omega_k = 1. For a more 17 | precise definition see Carroll, Press, & Turner (1992, ArAA, 30, 499). 18 | 19 | If less than two parameters are defined, this procedure sets default 20 | values of Omega_k=0 (flat space), Omega_lambda = 0.7, Omega_m = 0.3 21 | and q0 = -0.55 22 | CALLING SEQUENCE: 23 | COSMO_PARAM, Omega_m, Omega_lambda, Omega_k, q0 24 | 25 | INPUT-OUTPUTS: 26 | Omega_M - normalized matter energy density, non-negative numeric scalar 27 | Omega_Lambda - Normalized cosmological constant, numeric scalar 28 | Omega_k - normalized curvature parameter, numeric scalar. This is zero 29 | for a flat universe 30 | q0 - Deceleration parameter, numeric scalar = -R*(R'')/(R')^2 31 | = 0.5*Omega_m - Omega_lambda 32 | NOTES: 33 | If more than two parameters are defined upon input (overspecification), 34 | then the first two defined parameters in the ordered list Omega_m, 35 | Omega_lambda, Omega_k, q0 are used to define the cosmology. 36 | EXAMPLE: 37 | Suppose one has Omega_m = 0.3, and Omega_k = 0.5 then to determine 38 | Omega_lambda and q0 39 | 40 | IDL> cosmo_param, 0.3, omega_lambda, 0.5, q0 41 | 42 | which will return omega_lambda = 0.2 and q0 = -2.45 43 | REVISION HISTORY: 44 | W. Landsman Raytheon ITSS April 2000 45 | """ 46 | 47 | nk = omega_k is not None 48 | nlambda = omega_lambda is not None 49 | nomega = omega_m is not None 50 | nq0 = q0 is not None 51 | # Check which two parameters are defined, and then determine the other two 52 | 53 | if nomega and nlambda: 54 | if not nk: 55 | omega_k = 1 - omega_m - omega_lambda 56 | if not nq0: 57 | q0 = omega_m / 2. - omega_lambda 58 | 59 | if nomega and nk: 60 | if not nlambda: 61 | omega_lambda = 1. - omega_m - omega_k 62 | if not nq0: 63 | q0 = -1 + omega_k + 3 * omega_m / 2 64 | 65 | if nlambda and nk: 66 | if not nomega: 67 | omega_m = 1. - omega_lambda - omega_k 68 | if not nq0: 69 | q0 = (1 - omega_k - 3. * omega_lambda) / 2. 70 | 71 | if nomega and nq0: 72 | if not nk: 73 | omega_k = 1 + q0 - 3 * omega_m / 2. 74 | if not nlambda: 75 | omega_lambda = 1. - omega_m - omega_k 76 | 77 | if nlambda and nq0: 78 | if not nk: 79 | omega_k = 1 - 2 * q0 - 3 * omega_lambda 80 | if not nomega: 81 | omega_m = 1. - omega_lambda - omega_k 82 | 83 | if nk and nq0: 84 | if not nomega: 85 | omega_m = (1 + q0 - omega_k) * 2 / 3. 86 | if not nlambda: 87 | omega_lambda = 1. - omega_m - omega_k 88 | 89 | #Set default values 90 | if omega_k is None: 91 | omega_k = 0 #Default is flat space 92 | if omega_lambda is None: 93 | omega_lambda = 0.7 94 | if omega_m is None: 95 | omega_m = 1 - omega_lambda 96 | if q0 is None: 97 | q0 = (1 - omega_k - 3 * omega_lambda) / 2. 98 | 99 | return omega_m, omega_lambda, omega_k, q0 100 | 101 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/cv_coord.py: -------------------------------------------------------------------------------- 1 | from numpy import sin,cos,deg2rad,rad2deg,arctan2,sqrt 2 | 3 | def cv_coord(a,b,c,fr=None,to=None,degr=False): 4 | if degr: 5 | degrad = deg2rad 6 | raddeg = rad2deg 7 | else: 8 | degrad = lambda x: x 9 | raddeg = lambda x: x 10 | if fr=='sph': 11 | x=c*cos(degrad(a))*cos(degrad(b)) 12 | y=c*sin(degrad(a))*cos(degrad(b)) 13 | z=c*sin(degrad(b)) 14 | elif fr=='rect': 15 | x=a 16 | y=b 17 | z=c 18 | elif fr is None: 19 | raise Exception('You must specify the input coordinate system') 20 | else: 21 | raise Exception('Unknown input coordinate system') 22 | if to=='rect': 23 | return (x,y,z) 24 | elif to=='sph': 25 | ra = raddeg(arctan2(y,x)) 26 | dec = raddeg(arctan2(z,sqrt(x**2+y**2))) 27 | rad = sqrt(x**2+y**2+z**2) 28 | return (ra,dec,rad) 29 | elif to is None: 30 | raise Exception('You must specify the output coordinate system') 31 | else: 32 | raise Exception('Unknown output coordinate system') 33 | 34 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/daycnv.py: -------------------------------------------------------------------------------- 1 | from numpy import array 2 | 3 | def daycnv(xjd): 4 | """ 5 | NAME: 6 | DAYCNV 7 | PURPOSE: 8 | Converts Julian dates to Gregorian calendar dates 9 | 10 | CALLING SEQUENCE: 11 | DAYCNV, XJD, YR, MN, DAY, HR 12 | 13 | INPUTS: 14 | XJD = Julian date, positive double precision scalar or vector 15 | 16 | OUTPUTS: 17 | YR = Year (Integer) 18 | MN = Month (Integer) 19 | DAY = Day (Integer) 20 | HR = Hours and fractional hours (Real). If XJD is a vector, 21 | then YR,MN,DAY and HR will be vectors of the same length. 22 | 23 | EXAMPLE: 24 | IDL> DAYCNV, 2440000.D, yr, mn, day, hr 25 | 26 | yields yr = 1968, mn =5, day = 23, hr =12. 27 | 28 | WARNING: 29 | Be sure that the Julian date is specified as double precision to 30 | maintain accuracy at the fractional hour level. 31 | 32 | METHOD: 33 | Uses the algorithm of Fliegel and Van Flandern (1968) as reported in 34 | the "Explanatory Supplement to the Astronomical Almanac" (1992), p. 604 35 | Works for all Gregorian calendar dates with XJD > 0, i.e., dates after 36 | -4713 November 23. 37 | REVISION HISTORY: 38 | Converted to IDL from Yeoman's Comet Ephemeris Generator, 39 | B. Pfarr, STX, 6/16/88 40 | Converted to IDL V5.0 W. Landsman September 1997 41 | """ 42 | 43 | # Adjustment needed because Julian day starts at noon, calendar day at midnight 44 | 45 | jd = array(xjd).astype(int) #Truncate to integral day 46 | frac = array(xjd).astype(float) - jd + 0.5 #Fractional part of calendar day 47 | after_noon = (frac >= 1.0) 48 | 49 | if after_noon.any(): #Is it really the next calendar day? 50 | if frac.ndim>0: # proper array 51 | frac[after_noon] = frac[after_noon] - 1.0 52 | jd[after_noon] = jd[after_noon] + 1 53 | else: # scalar 54 | frac = frac - 1.0 55 | jd = jd + 1 56 | hr = frac * 24.0 57 | l = jd + 68569 58 | n = 4 * l / 146097 59 | l = l - (146097 * n + 3) / 4 60 | yr = 4000 * (l + 1) / 1461001 61 | l = l - 1461 * yr / 4 + 31 #1461 = 365.25 * 4 62 | mn = 80 * l / 2447 63 | day = l - 2447 * mn / 80 64 | l = mn / 11 65 | mn = mn + 2 - 12 * l 66 | yr = 100 * (n - 49) + yr + l 67 | return (yr, mn, day, hr) -------------------------------------------------------------------------------- /astrolibpy/astrolib/euler.py: -------------------------------------------------------------------------------- 1 | from numpy import array, sin, cos, pi, deg2rad, rad2deg, arctan2, arcsin, minimum 2 | 3 | def euler(ai, bi, select=1, fk4=False): 4 | """ 5 | NAME: 6 | EULER 7 | PURPOSE: 8 | Transform between Galactic, celestial, and ecliptic coordinates. 9 | EXPLANATION: 10 | Use the procedure ASTRO to use this routine interactively 11 | 12 | CALLING SEQUENCE: 13 | AO, BO = EULER(AI, BI, [SELECT=1, FK4=False]) 14 | 15 | INPUTS: 16 | AI - Input Longitude in DEGREES, scalar or vector. If only two 17 | parameters are supplied, then AI and BI will be modified to 18 | contain the output longitude and latitude. 19 | BI - Input Latitude in DEGREES 20 | 21 | OPTIONAL INPUT: 22 | SELECT - Integer (1-6) specifying type of coordinate transformation. 23 | 24 | SELECT From To | SELECT From To 25 | 1 RA-Dec (2000) Galactic | 4 Ecliptic RA-Dec 26 | 2 Galactic RA-DEC | 5 Ecliptic Galactic 27 | 3 RA-Dec Ecliptic | 6 Galactic Ecliptic 28 | 29 | If not supplied as a parameter or keyword, then EULER will prompt for 30 | the value of SELECT 31 | Celestial coordinates (RA, Dec) should be given in equinox J2000 32 | unless the /FK4 keyword is set. 33 | OUTPUTS: 34 | AO - Output Longitude in DEGREES 35 | BO - Output Latitude in DEGREES 36 | 37 | INPUT KEYWORD: 38 | /FK4 - If this keyword is set and non-zero, then input and output 39 | celestial and ecliptic coordinates should be given in equinox 40 | B1950. 41 | /SELECT - The coordinate conversion integer (1-6) may alternatively be 42 | specified as a keyword 43 | NOTES: 44 | EULER was changed in December 1998 to use J2000 coordinates as the 45 | default, ** and may be incompatible with earlier versions***. 46 | REVISION HISTORY: 47 | Written W. Landsman, February 1987 48 | Adapted from Fortran by Daryl Yentis NRL 49 | Converted to IDL V5.0 W. Landsman September 1997 50 | Made J2000 the default, added /FK4 keyword W. Landsman December 1998 51 | Add option to specify SELECT as a keyword W. Landsman March 2003 52 | """ 53 | 54 | # J2000 coordinate conversions are based on the following constants 55 | # (see the Hipparcos explanatory supplement). 56 | # eps = 23.4392911111d Obliquity of the ecliptic 57 | # alphaG = 192.85948d Right Ascension of Galactic North Pole 58 | # deltaG = 27.12825d Declination of Galactic North Pole 59 | # lomega = 32.93192d Galactic longitude of celestial equator 60 | # alphaE = 180.02322d Ecliptic longitude of Galactic North Pole 61 | # deltaE = 29.811438523d Ecliptic latitude of Galactic North Pole 62 | # Eomega = 6.3839743d Galactic longitude of ecliptic equator 63 | 64 | if fk4: 65 | equinox = '(B1950)' 66 | psi = array ([0.57595865315e0, 4.9261918136e0, 0.00000000000e0, 0.0000000000e0, 0.11129056012e0, 4.7005372834e0]) 67 | stheta = array ([0.88781538514e0, -0.88781538514e0, 0.39788119938e0, -0.39788119938e0, 0.86766174755e0, -0.86766174755e0]) 68 | ctheta = array([0.46019978478e0, 0.46019978478e0, 0.91743694670e0, 0.91743694670e0, 0.49715499774e0, 0.49715499774e0]) 69 | phi = array([4.9261918136e0, 0.57595865315e0, 0.0000000000e0, 0.00000000000e0, 4.7005372834e0, 0.11129056012e0]) 70 | else: 71 | equinox = '(J2000)' 72 | psi = array([0.57477043300e0, 4.9368292465e0, 0.00000000000e0, 0.0000000000e0, 0.11142137093e0, 4.71279419371e0]) 73 | stheta = array([0.88998808748e0, -0.88998808748e0, 0.39777715593e0, -0.39777715593e0, 0.86766622025e0, -0.86766622025e0]) 74 | ctheta = array([0.45598377618e0, 0.45598377618e0, 0.91748206207e0, 0.91748206207e0, 0.49714719172e0, 0.49714719172e0]) 75 | phi = array([4.9368292465e0, 0.57477043300e0, 0.0000000000e0, 0.00000000000e0, 4.71279419371e0, 0.11142137093e0]) 76 | if select not in [1,2,3,4,5,6]: 77 | raise ValueError('Select parameter should be an integer between 1 and 6') 78 | i = select - 1 79 | a = deg2rad(ai) - phi[i] 80 | b = deg2rad(bi) 81 | sb = sin(b) 82 | cb = cos(b) 83 | cbsa = cb * sin(a) 84 | b = -stheta[i] * cbsa + ctheta[i] * sb 85 | bo = rad2deg(arcsin(minimum(b, 1.0))) 86 | del b 87 | a = arctan2(ctheta[i] * cbsa + stheta[i] * sb, cb * cos(a)) 88 | del cb, cbsa, sb 89 | ao = rad2deg(((a + psi[i] + 4 * pi ) % (2 * pi) ) ) 90 | 91 | return (ao, bo) 92 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/gal_uvw.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | def gal_uvw(distance=None, lsr=None, ra=None, dec=None, pmra=None, pmdec=None, vrad=None, plx=None): 4 | """ 5 | NAME: 6 | GAL_UVW 7 | PURPOSE: 8 | Calculate the Galactic space velocity (U,V,W) of star 9 | EXPLANATION: 10 | Calculates the Galactic space velocity U, V, W of star given its 11 | (1) coordinates, (2) proper motion, (3) distance (or parallax), and 12 | (4) radial velocity. 13 | CALLING SEQUENCE: 14 | GAL_UVW [/LSR, RA=, DEC=, PMRA= ,PMDEC=, VRAD= , DISTANCE= 15 | PLX= ] 16 | OUTPUT PARAMETERS: 17 | U - Velocity (km/s) positive toward the Galactic *anti*center 18 | V - Velocity (km/s) positive in the direction of Galactic rotation 19 | W - Velocity (km/s) positive toward the North Galactic Pole 20 | REQUIRED INPUT KEYWORDS: 21 | User must supply a position, proper motion,radial velocity and distance 22 | (or parallax). Either scalars or vectors can be supplied. 23 | (1) Position: 24 | RA - Right Ascension in *Degrees* 25 | Dec - Declination in *Degrees* 26 | (2) Proper Motion 27 | PMRA = Proper motion in RA in arc units (typically milli-arcseconds/yr) 28 | PMDEC = Proper motion in Declination (typically mas/yr) 29 | (3) Radial Velocity 30 | VRAD = radial velocity in km/s 31 | (4) Distance or Parallax 32 | DISTANCE - distance in parsecs 33 | or 34 | PLX - parallax with same distance units as proper motion measurements 35 | typically milliarcseconds (mas) 36 | 37 | OPTIONAL INPUT KEYWORD: 38 | /LSR - If this keyword is set, then the output velocities will be 39 | corrected for the solar motion (U,V,W)_Sun = (-8.5, 13.38, 6.49) 40 | (Coskunoglu et al. 2011 MNRAS) to the local standard of rest. 41 | Note that the value of the solar motion through the LSR remains 42 | poorly determined. 43 | EXAMPLE: 44 | (1) Compute the U,V,W coordinates for the halo star HD 6755. 45 | Use values from Hipparcos catalog, and correct to the LSR 46 | ra = ten(1,9,42.3)*15. & dec = ten(61,32,49.5) 47 | pmra = 627.89 & pmdec = 77.84 ;mas/yr 48 | dis = 144 & vrad = -321.4 49 | gal_uvw,u,v,w,ra=ra,dec=dec,pmra=pmra,pmdec=pmdec,vrad=vrad,dis=dis,/lsr 50 | ===> u=154 v = -493 w = 97 ;km/s 51 | 52 | (2) Use the Hipparcos Input and Output Catalog IDL databases (see 53 | http://idlastro.gsfc.nasa.gov/ftp/zdbase/) to obtain space velocities 54 | for all stars within 10 pc with radial velocities > 10 km/s 55 | 56 | dbopen,'hipparcos,hic' ;Need Hipparcos output and input catalogs 57 | list = dbfind('plx>100,vrad>10') ;Plx > 100 mas, Vrad > 10 km/s 58 | dbext,list,'pmra,pmdec,vrad,ra,dec,plx',pmra,pmdec,vrad,ra,dec,plx 59 | ra = ra*15. ;Need right ascension in degrees 60 | GAL_UVW,u,v,w,ra=ra,dec=dec,pmra=pmra,pmdec=pmdec,vrad=vrad,plx = plx 61 | forprint,u,v,w ;Display results 62 | METHOD: 63 | Follows the general outline of Johnson & Soderblom (1987, AJ, 93,864) 64 | except that U is positive outward toward the Galactic *anti*center, and 65 | the J2000 transformation matrix to Galactic coordinates is taken from 66 | the introduction to the Hipparcos catalog. 67 | REVISION HISTORY: 68 | Written, W. Landsman December 2000 69 | fix the bug occuring if the input arrays are longer than 32767 70 | and update the Sun velocity Sergey Koposov June 2008 71 | vectorization of the loop -- performance on large arrays 72 | is now 10 times higher Sergey Koposov December 2008 73 | """ 74 | 75 | n_params = 3 76 | 77 | if n_params == 0: 78 | print 'Syntax - GAL_UVW, U, V, W, [/LSR, RA=, DEC=, PMRA= ,PMDEC=, VRAD=' 79 | print ' Distance=, PLX=' 80 | print ' U, V, W - output Galactic space velocities (km/s)' 81 | return None 82 | 83 | if ra is None or dec is None: 84 | raise Exception('ERROR - The RA, Dec (J2000) position keywords must be supplied (degrees)') 85 | if plx is None and distance is None: 86 | raise Exception('ERROR - Either a parallax or distance must be specified') 87 | if distance is not None: 88 | if numpy.any(distance==0): 89 | raise Exception('ERROR - All distances must be > 0') 90 | plx = 1e3 / distance #Parallax in milli-arcseconds 91 | if plx is not None and numpy.any(plx==0): 92 | raise Exception('ERROR - Parallaxes must be > 0') 93 | 94 | cosd = numpy.cos(numpy.deg2rad(dec)) 95 | sind = numpy.sin(numpy.deg2rad(dec)) 96 | cosa = numpy.cos(numpy.deg2rad(ra)) 97 | sina = numpy.sin(numpy.deg2rad(ra)) 98 | 99 | k = 4.74047 #Equivalent of 1 A.U/yr in km/s 100 | a_g = numpy.array([[0.0548755604, +0.4941094279, -0.8676661490], 101 | [0.8734370902, -0.4448296300, -0.1980763734], 102 | [0.4838350155, 0.7469822445, +0.4559837762]]) 103 | 104 | vec1 = vrad 105 | vec2 = k * pmra / plx 106 | vec3 = k * pmdec / plx 107 | 108 | u = (a_g[0,0] * cosa * cosd + a_g[1,0] * sina * cosd + a_g[2,0] * sind) * vec1 + (-a_g[0,0] * sina + a_g[1,0] * cosa) * vec2 + (-a_g[0,0] * cosa * sind - a_g[1,0] * sina * sind + a_g[2,0] * cosd) * vec3 109 | v = (a_g[0,1] * cosa * cosd + a_g[1,1] * sina * cosd + a_g[2,1] * sind) * vec1 + (-a_g[0,1] * sina + a_g[1,1] * cosa) * vec2 + (-a_g[0,1] * cosa * sind - a_g[1,1] * sina * sind + a_g[2,1] * cosd) * vec3 110 | w = (a_g[0,2] * cosa * cosd + a_g[1,2] * sina * cosd + a_g[2,2] * sind) * vec1 + (-a_g[0,2] * sina + a_g[1,2] * cosa) * vec2 + (-a_g[0,2] * cosa * sind - a_g[1,2] * sina * sind + a_g[2,2] * cosd) * vec3 111 | 112 | lsr_vel = numpy.array([-8.5, 13.38, 6.49]) # notice the sign of the first velocity 113 | #component, it is negative because 114 | # in this program U points toward anticenter 115 | if (lsr is not None): 116 | u = u + lsr_vel[0] 117 | v = v + lsr_vel[1] 118 | w = w + lsr_vel[2] 119 | 120 | return (u,v,w) 121 | 122 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/galage.py: -------------------------------------------------------------------------------- 1 | from numpy import array, sqrt, ndarray 2 | from cosmo_param import cosmo_param 3 | from scipy.integrate import quad 4 | 5 | def dtdz(z, lambda0=None, q0=None): 6 | 7 | term1 = (1.0e0 + z) 8 | term2 = 2.0e0 * (q0 + lambda0) * z + 1.0e0 - lambda0 9 | term3 = (1.0e0 + z) * (1.0e0 + z) 10 | return 1.0 / (term1 * sqrt(term2 * term3 + lambda0)) 11 | 12 | 13 | 14 | def galage(z, zform, h0=None, omega_m=None, lambda0=None, k=None, q0=None, silent=None): 15 | """ NAME: 16 | GALAGE 17 | 18 | PURPOSE: 19 | Determine the age of a galaxy given its redshift and a formation redshift. 20 | 21 | CALLING SEQUENCE: 22 | age = galage(z, [zform, H0 =, k=, lambda0 =, Omega_m= , q0 =, /SILENT])' 23 | 24 | INPUTS: 25 | z - positive numeric vector or scalar of measured redshifts 26 | zform - redshift of galaxy formation (> z), numeric positive scalar 27 | To determine the age of the universe at a given redshift, set zform 28 | to a large number (e.g. ~1000). 29 | 30 | OPTIONAL KEYWORD INPUTS: 31 | H0 - Hubble constant in km/s/Mpc, positive scalar, default is 70 32 | /SILENT - If set, then the adopted cosmological parameters are not 33 | displayed at the terminal. 34 | 35 | No more than two of the following four parameters should be 36 | specified. None of them need be specified -- the adopted defaults 37 | are given. 38 | k - curvature constant, normalized to the closure density. Default is 39 | 0, (indicating a flat universe) 40 | Omega_m - Matter density, normalized to the closure density, default 41 | is 0.3. Must be non-negative 42 | Lambda0 - Cosmological constant, normalized to the closure density, 43 | default is 0.7 44 | q0 - Deceleration parameter, numeric scalar = -R*(R'')/(R')^2, default 45 | is -0.55 46 | 47 | OUTPUTS: 48 | age - age of galaxy in years, will have the same number of elements 49 | as the input Z vector 50 | 51 | EXAMPLE: 52 | (1) Determine the age of a galaxy observed at z = 1.5 in a cosmology with 53 | Omega_matter = 0.3 and Lambda = 0.0. Assume the formation redshift was 54 | at z = 25, and use the default Hubble constant (=70 km/s/Mpc) 55 | 56 | IDL> print,galage(1.5,25,Omega_m=0.3, Lambda = 0) 57 | ===> 3.35 Gyr 58 | 59 | (2) Plot the age of a galaxy in Gyr out to a redshift of z = 5, assuming 60 | the default cosmology (omega_m=0.3, lambda=0.7), and zform = 100 61 | 62 | IDL> z = findgen(50)/10. 63 | IDL> plot,z,galage(z,100)/1e9,xtit='z',ytit = 'Age (Gyr)' 64 | 65 | PROCEDURE: 66 | For a given formation time zform and a measured z, integrate dt/dz from 67 | zform to z. Analytic formula of dt/dz in Gardner, PASP 110:291-305, 1998 68 | March (eq. 7) 69 | 70 | COMMENTS: 71 | (1) Integrates using the IDL Astronomy Library procedure QSIMP. (The 72 | intrinsic IDL QSIMP() function is not called because of its ridiculous 73 | restriction that only scalar arguments can be passed to the integrating 74 | function.) The function 'dtdz' is defined at the beginning of the 75 | routine (so it can compile first). 76 | 77 | (2) Should probably be fixed to use a different integrator from QSIMP when 78 | computing age from an "infinite" redshift of formation. But using a 79 | large value of zform seems to work adequately. 80 | 81 | (3) An alternative set of IDL procedures for computing cosmological 82 | parameters is available at 83 | http://cerebus.as.arizona.edu/~ioannis/research/red/ 84 | PROCEDURES CALLED: 85 | COSMO_PARAM, QSIMP 86 | HISTORY: 87 | STIS version by P. Plait (ACC) June 1999 88 | IDL Astro Version W. Landsman (Raytheon ITSS) April 2000 89 | Avoid integer overflow for more than 32767 redshifts July 2001 90 | Convert to python S. Koposov 2010 91 | """ 92 | if h0 is None: 93 | h0 = 70.0 94 | omega_m, lambda0, k, q0 = cosmo_param(omega_m, lambda0, k, q0) 95 | 96 | if silent is not None: 97 | print 'GALAGE: H0:', h0, ' Omega_m:', omega_m, ' Lambda0', lambda0, ' q0: ', q0, ' k: ', k#, format='(A,I3,A,f5.2,A,f5.2,A,f5.2,A,F5.2)' 98 | 99 | scal = False 100 | if isinstance(z, list): 101 | z = array(z) 102 | elif isinstance(z, ndarray): 103 | pass 104 | else: 105 | z = array([z]) 106 | scal = True 107 | 108 | nz = len(z) 109 | age = z * 0. #Return same dimensions and data type as Z 110 | 111 | # 112 | # use qsimp to integrate dt/dz to get age for each z 113 | # watch out for null case of z >= zform 114 | # 115 | 116 | for i in range(nz): 117 | if (z[i] >= zform): 118 | age_z = 0 119 | else: 120 | #qsimp('dtdz', z[i], zform, age_z, q0=q0, lambda0=lambda0) 121 | age_z = quad(dtdz, z[i], zform, args=(lambda0, q0))[0] 122 | age[i] = age_z 123 | 124 | # convert units of age: km/s/Mpc to years, divide by H0 125 | # 3.085678e19 km --> 1 Mpc 126 | # 3.15567e+07 sec --> 1 year 127 | if scal: 128 | age = age[0] 129 | return age * 3.085678e+19 / 3.15567e+7 / h0 130 | 131 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/helcorr.py: -------------------------------------------------------------------------------- 1 | from numpy import * 2 | from .baryvel import baryvel 3 | from .daycnv import daycnv 4 | from .precess import precess 5 | from .helio_jd import helio_jd 6 | _radeg = 180.0 / pi 7 | 8 | def helcorr(obs_long, obs_lat, obs_alt, ra2000, dec2000, jd, debug=False): 9 | 10 | #calculates heliocentric Julian date, baricentric and heliocentric radial 11 | #velocity corrections from: 12 | # 13 | #INPUT: 14 | # Longitude of observatory (degrees, western direction is positive) 15 | # Latitude of observatory (degrees) 16 | # Altitude of observatory (meters) 17 | # Right ascension of object for epoch 2000.0 (hours) 18 | # Declination of object for epoch 2000.0 (degrees) 19 | # Julian date for the middle of exposure 20 | #[DEBUG=] set keyword to get additional results for debugging 21 | # 22 | #OUTPUT: 23 | # baricentric correction - correction for rotation of earth, 24 | # rotation of earth center about the eart-moon barycenter, eart-moon 25 | # barycenter about the center of the Sun. 26 | # Heliocentric Julian date for middle of exposure 27 | # 28 | #Algorithms used are taken from the IRAF task noao.astutils.rvcorrect 29 | #and some procedures of the IDL Astrolib are used as well. 30 | #Accuracy is about 0.5 seconds in time and about 1 m/s in velocity. 31 | # 32 | #History: 33 | #written by Peter Mittermayer, Nov 8,2003 34 | #2005-January-13 Kudryavtsev Made more accurate calculation of the sideral time. 35 | # Conformity with MIDAS compute/barycorr is checked. 36 | #2005-June-20 Kochukhov Included precession of RA2000 and DEC2000 to current epoch 37 | 38 | 39 | #covert JD to Gregorian calendar date 40 | xjd = array(2400000.).astype(float) + jd 41 | year,month,day,ut=daycnv(xjd) 42 | 43 | #current epoch 44 | epoch = year + month / 12. + day / 365. 45 | 46 | #precess ra2000 and dec2000 to current epoch 47 | ra,dec=precess(ra2000*15., dec2000, 2000.0, epoch) 48 | #calculate heliocentric julian date 49 | hjd = array(helio_jd(jd, ra, dec)).astype(float) 50 | 51 | #DIURNAL VELOCITY (see IRAF task noao.astutil.rvcorrect) 52 | #convert geodetic latitude into geocentric latitude to correct 53 | #for rotation of earth 54 | dlat = -(11. * 60. + 32.743) * sin(2 * obs_lat / _radeg) + 1.1633 * sin(4 * obs_lat / _radeg) - 0.0026 * sin(6 * obs_lat / _radeg) 55 | lat = obs_lat + dlat / 3600 56 | 57 | #calculate distance of observer from earth center 58 | r = 6378160.0 * (0.998327073 + 0.001676438 * cos(2 * lat / _radeg) - 0.00000351 * cos(4 * lat / _radeg) + 0.000000008 * cos(6 * lat / _radeg)) + obs_alt 59 | 60 | #calculate rotational velocity (perpendicular to the radius vector) in km/s 61 | #23.934469591229 is the siderial day in hours for 1986 62 | v = 2. * pi * (r / 1000.) / (23.934469591229 * 3600.) 63 | 64 | #calculating local mean siderial time (see astronomical almanach) 65 | tu = (jd - 51545.0) / 36525 66 | gmst = 6.697374558 + ut + (236.555367908 * (jd - 51545.0) + 0.093104 * tu ** 2 - 6.2e-6 * tu ** 3) / 3600 67 | lmst = (gmst - obs_long / 15) % 24 68 | 69 | #projection of rotational velocity along the line of sight 70 | vdiurnal = v * cos(lat / _radeg) * cos(dec / _radeg) * sin((ra - lmst * 15) / _radeg) 71 | 72 | #BARICENTRIC and HELIOCENTRIC VELOCITIES 73 | vh,vb=baryvel(xjd, 0) 74 | 75 | #project to line of sight 76 | vbar = vb[0] * cos(dec / _radeg) * cos(ra / _radeg) + vb[1] * cos(dec / _radeg) * sin(ra / _radeg) + vb[2] * sin(dec / _radeg) 77 | vhel = vh[0] * cos(dec / _radeg) * cos(ra / _radeg) + vh[1] * cos(dec / _radeg) * sin(ra / _radeg) + vh[2] * sin(dec / _radeg) 78 | 79 | barycorr = (vdiurnal + vbar) #using baricentric velocity for correction 80 | helicorr = (vdiurnal + vhel) # using heliocentric velocity (Geoffrey Lentner) 81 | if debug: 82 | print ('') 83 | print ('----- HELCORR.PRO - DEBUG INFO - START ----') 84 | print ('(obs_long,obs_lat,obs_alt) Observatory coordinates [deg,m]: ', obs_long, obs_lat, obs_alt) 85 | print ('(ra,dec) Object coordinates (for epoch 2000.0) [deg]: ', ra, dec) 86 | print ('(ut) Universal time (middle of exposure) [hrs]: ', ut) 87 | print ('(jd) Julian date (middle of exposure) (JD-2400000): ', jd) 88 | print ('(hjd) Heliocentric Julian date (middle of exposure) (HJD-2400000): ', hjd) 89 | print ('(gmst) Greenwich mean siderial time [hrs]: ', gmst % 24) 90 | print ('(lmst) Local mean siderial time [hrs]: ', lmst) 91 | print ('(dlat) Latitude correction [deg]: ', dlat) 92 | print ('(lat) Geocentric latitude of observer [deg]: ', lat) 93 | print ('(r) Distance of observer from center of earth [m]: ', r) 94 | print ('(v) Rotational velocity of earth at the position of the observer [km/s]: ', v) 95 | print ('(vdiurnal) Projected earth rotation and earth-moon revolution [km/s]: ', vdiurnal) 96 | print ('(vbar) Baricentric velocity [km/s]: ', vbar) 97 | print ('(vhel) Heliocentric velocity [km/s]: ', vhel) 98 | print ('(corr) Vdiurnal+vbar [km/s]: ', corr) 99 | print ('----- HELCORR.PRO - DEBUG INFO - END -----') 100 | 101 | return (barycorr, helicorr) # modified by GL 102 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/helio_jd.py: -------------------------------------------------------------------------------- 1 | from numpy import array, cos, sin, tan, pi, poly1d, deg2rad 2 | from .xyz import xyz 3 | from .bprecess import bprecess 4 | 5 | def helio_jd(date, ra, dec, b1950=False, time_diff=False): 6 | """ 7 | NAME: 8 | HELIO_JD 9 | PURPOSE: 10 | Convert geocentric (reduced) Julian date to heliocentric Julian date 11 | EXPLANATION: 12 | This procedure correct for the extra light travel time between the Earth 13 | and the Sun. 14 | 15 | An online calculator for this quantity is available at 16 | http://www.physics.sfasu.edu/astro/javascript/hjd.html 17 | CALLING SEQUENCE: 18 | jdhelio = HELIO_JD( date, ra, dec, /B1950, /TIME_DIFF) 19 | 20 | INPUTS 21 | date - reduced Julian date (= JD - 2400000), scalar or vector, MUST 22 | be double precision 23 | ra,dec - scalars giving right ascension and declination in DEGREES 24 | Equinox is J2000 unless the /B1950 keyword is set 25 | 26 | OUTPUTS: 27 | jdhelio - heliocentric reduced Julian date. If /TIME_DIFF is set, then 28 | HELIO_JD() instead returns the time difference in seconds 29 | between the geocentric and heliocentric Julian date. 30 | 31 | OPTIONAL INPUT KEYWORDS 32 | /B1950 - if set, then input coordinates are assumed to be in equinox 33 | B1950 coordinates. 34 | /TIME_DIFF - if set, then HELIO_JD() returns the time difference 35 | (heliocentric JD - geocentric JD ) in seconds 36 | 37 | EXAMPLE: 38 | What is the heliocentric Julian date of an observation of V402 Cygni 39 | (J2000: RA = 20 9 7.8, Dec = 37 09 07) taken June 15, 1973 at 11:40 UT? 40 | 41 | IDL> juldate, [1973,6,15,11,40], jd ;Get geocentric Julian date 42 | IDL> hjd = helio_jd( jd, ten(20,9,7.8)*15., ten(37,9,7) ) 43 | 44 | ==> hjd = 41848.9881 45 | 46 | Wayne Warren (Raytheon ITSS) has compared the results of HELIO_JD with the 47 | FORTRAN subroutines in the STARLINK SLALIB library (see 48 | http://star-www.rl.ac.uk/). 49 | Time Diff (sec) 50 | Date RA(2000) Dec(2000) STARLINK IDL 51 | 52 | 1999-10-29T00:00:00.0 21 08 25. -67 22 00. -59.0 -59.0 53 | 1999-10-29T00:00:00.0 02 56 33.4 +00 26 55. 474.1 474.1 54 | 1940-12-11T06:55:00.0 07 34 41.9 -00 30 42. 366.3 370.2 55 | 1992-02-29T03:15:56.2 12 56 27.4 +42 10 17. 350.8 350.9 56 | 2000-03-01T10:26:31.8 14 28 36.7 -20 42 11. 243.7 243.7 57 | 2100-02-26T09:18:24.2 08 26 51.7 +85 47 28. 104.0 108.8 58 | PROCEDURES CALLED: 59 | bprecess, xyz 60 | 61 | REVISION HISTORY: 62 | Algorithm from the book Astronomical Photometry by Henden, p. 114 63 | Written, W. Landsman STX June, 1989 64 | Make J2000 default equinox, add B1950, /TIME_DIFF keywords, compute 65 | variation of the obliquity W. Landsman November 1999 66 | Converted to python Sergey Koposov July 2010 67 | """ 68 | 69 | #Because XYZ uses default B1950 coordinates, we'll convert everything to B1950 70 | 71 | if not b1950: 72 | ra1, dec1 = bprecess(ra, dec) 73 | else: 74 | ra1 = ra 75 | dec1 = dec 76 | 77 | 78 | delta_t = (array(date).astype(float) - 33282.42345905e0) / 36525.0e0 79 | epsilon_sec = poly1d([44.836e0, -46.8495, -0.00429, 0.00181][::-1])(delta_t) 80 | epsilon = deg2rad(23.433333e0 + epsilon_sec / 3600.0e0) 81 | ra1 = deg2rad(ra1) 82 | dec1 = deg2rad(dec1) 83 | 84 | x, y, z, tmp, tmp, tmp = xyz(date) 85 | 86 | #Find extra distance light must travel in AU, multiply by 1.49598e13 cm/AU, 87 | #and divide by the speed of light, and multiply by 86400 second/year 88 | 89 | time = -499.00522e0 * (cos(dec1) * cos(ra1) * x + (tan(epsilon) * sin(dec1) + cos(dec1) * sin(ra1)) * y) 90 | if time_diff: 91 | return time 92 | else: 93 | return array(date).astype(float) + time / 86400.0e0 94 | 95 | 96 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/lumdist.py: -------------------------------------------------------------------------------- 1 | """ 2 | NAME: 3 | LUMDIST 4 | 5 | PURPOSE: 6 | Calculate luminosity distance (in Mpc) of an object given its redshift 7 | EXPLANATION: 8 | The luminosity distance in the Friedmann-Robertson-Walker model is 9 | taken from Caroll, Press, and Turner (1992, ARAA, 30, 499), p. 511 10 | Uses a closed form (Mattig equation) to compute the distance when the 11 | cosmological constant is zero. Otherwise integrates the function using 12 | QSIMP. 13 | CALLING SEQUENCE: 14 | result = lumdist(z, [H0 = , k = , Omega_M =, Lambda0 = , q0 = ,/SILENT]) 15 | 16 | INPUTS: 17 | z = redshift, positive scalar or vector 18 | 19 | OPTIONAL KEYWORD INPUTS: 20 | /SILENT - If set, the program will not display adopted cosmological 21 | parameters at the terminal. 22 | H0: Hubble parameter in km/s/Mpc, default is 70 23 | 24 | No more than two of the following four parameters should be 25 | specified. None of them need be specified -- the adopted defaults 26 | are given. 27 | k - curvature constant, normalized to the closure density. Default is 28 | 0, indicating a flat universe 29 | Omega_m - Matter density, normalized to the closure density, default 30 | is 0.3. Must be non-negative 31 | Lambda0 - Cosmological constant, normalized to the closure density, 32 | default is 0.7 33 | q0 - Deceleration parameter, numeric scalar = -R*(R'')/(R')^2, default 34 | is -0.55 35 | 36 | OUTPUTS: 37 | The result of the function is the luminosity distance (in Mpc) for each 38 | input value of z. 39 | 40 | EXAMPLE: 41 | (1) Plot the distance of a galaxy in Mpc as a function of redshift out 42 | to z = 5.0, assuming the default cosmology (Omega_m=0.3, Lambda = 0.7, 43 | H0 = 70 km/s/Mpc) 44 | 45 | IDL> z = findgen(50)/10. 46 | IDL> plot,z,lumdist(z),xtit='z',ytit='Distance (Mpc)' 47 | 48 | Now overplot the relation for zero cosmological constant and 49 | Omega_m=0.3 50 | IDL> oplot,z,lumdist(z,lambda=0,omega=0.3),linestyle=1 51 | COMMENTS: 52 | (1) Integrates using the IDL Astronomy Version procedure QSIMP. (The 53 | intrinsic IDL QSIMP function is not called because of its ridiculous 54 | restriction that only scalar arguments can be passed to the integrating 55 | function.) 56 | (2) Can fail to converge at high redshift for closed universes with 57 | non-zero lambda. This can presumably be fixed by replacing QSIMP with 58 | an integrator that can handle a singularity 59 | PROCEDURES CALLED: 60 | COSMO_PARAM, QSIMP 61 | REVISION HISTORY: 62 | Written W. Landsman Raytheon ITSS April 2000 63 | Avoid integer overflow for more than 32767 redshifts July 2001 64 | Use double precision J. Moustakas/W. Landsman April 2008 65 | Convert to python S. Koposov 2010 66 | """ 67 | 68 | from numpy import array, ndarray, sqrt, sin, sinh, maximum 69 | from cosmo_param import cosmo_param 70 | from scipy.integrate import quad 71 | from math import sqrt as msqrt 72 | 73 | def ldist(z, q0=None, lambda0=None): 74 | 75 | term1 = (1. + z) ** 2 76 | term2 = 1. + 2. * (q0 + lambda0) * z 77 | term3 = z * (2. + z) * lambda0 78 | denom = (term1 * term2 - term3) 79 | if denom>0: 80 | out = 1. / msqrt(denom) # since the function is used with scalar arguments 81 | # I use math.sqrt instead of numpy.sqrt for 82 | # performance reasons 83 | else: 84 | out = 0. 85 | return out 86 | 87 | 88 | def lumdist(z, h0=None, k=None, lambda0=None, omega_m=None, q0=None, silent=None): 89 | '''Syntax: result = lumdist(z, H0 = ,k=, Lambda0 = ]) 90 | Returns luminosity distance in Mpc''' 91 | 92 | scal=False 93 | scalret = lambda x : x[0] if scal else x 94 | 95 | if isinstance(z, list): 96 | z = array(z) 97 | elif isinstance(z, ndarray): 98 | pass 99 | else: 100 | scal = True 101 | z = array([z]) 102 | n = len(z) 103 | 104 | omega_m, lambda0, k, q0 = cosmo_param(omega_m, lambda0, k, q0) 105 | 106 | # Check keywords 107 | c = 2.99792458e5 # speed of light in km/s 108 | if h0 is None: 109 | h0 = 70 110 | if not silent: 111 | print 'LUMDIST: H0:', h0, ' Omega_m:', omega_m, ' Lambda0', lambda0, ' q0: ', q0, ' k: ', k#, format='(A,I3,A,f5.2,A,f5.2,A,f5.2,A,F5.2)' 112 | 113 | # For the case of Lambda = 0, we use the closed form from equation 5.238 of 114 | # Astrophysical Formulae (Lang 1998). This avoids terms that almost cancel 115 | # at small q0*z better than the more familiar Mattig formula. 116 | # 117 | if lambda0 == 0: 118 | denom = sqrt(1 + 2 * q0 * z) + 1 + q0 * z 119 | dlum = (c * z / h0) * (1 + z * (1 - q0) / denom) 120 | return scalret(dlum) 121 | 122 | # For non-zero lambda 123 | else: 124 | dlum = z * 0.0 125 | for i in range(n): 126 | if z[i] <= 0.0: 127 | dlum[i] = 0.0 128 | else: 129 | lz = quad(ldist, 0, z[i], args=(q0, lambda0)) 130 | dlum[i] = lz[0] 131 | 132 | if k > 0: 133 | dlum = sinh(sqrt(k) * dlum) / sqrt(k) 134 | else: 135 | if k < 0: 136 | dlum = maximum(sin(sqrt(-k) * dlum) / sqrt(-k), 0) 137 | return scalret(c * (1 + z) * dlum / h0) 138 | 139 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/mwrfits.py: -------------------------------------------------------------------------------- 1 | import numpy, pyfits, types, itertools 2 | 3 | def mwrfits(filename, arraylist, namelist=None, header=None): 4 | """ 5 | Writes the list of numpy.arrays arraylist as a FITS table filename 6 | using namelist as list of names. 7 | Arraylist can be dictionary with arrays as values and names as keys. 8 | Also Arraylist can be numpy-record-array. 9 | Example: 10 | mwrfits('/tmp/xx.fits',[arr,arr1],['X','Y']) 11 | Or : 12 | mwrfits('test.fits',{'X':arr,'Y':arr1}) 13 | Or: 14 | data = numpy.zeros((4,),dtype=[('run','i4'),('rerun','f8'),('zz','b')]) 15 | mwfits('test1.fits',data) 16 | 17 | Keep in mind that when you used a dictionary, the order of columns in the 18 | fits file is not guaranteed 19 | """ 20 | tmplist=[] 21 | if isinstance(arraylist,numpy.ndarray): 22 | if arraylist.dtype.type is numpy.void: 23 | iter=itertools.izip(arraylist.dtype.names, itertools.imap (arraylist.__getitem__ , arraylist.dtype.names)) 24 | else: 25 | if isinstance(arraylist,types.ListType): 26 | iter= zip(namelist, arraylist) 27 | elif isinstance(arraylist,types.DictType): 28 | iter= arraylist.iteritems() 29 | 30 | for name, arr in iter: 31 | if arr.dtype.type==numpy.int8: 32 | format='I' 33 | elif arr.dtype.type==numpy.int16: 34 | format='I' 35 | elif arr.dtype.type==numpy.int32: 36 | format='J' 37 | elif arr.dtype.type==numpy.int64: 38 | format='K' 39 | elif arr.dtype.type==numpy.float32: 40 | format='E' 41 | elif arr.dtype.type==numpy.float64: 42 | format='D' 43 | elif arr.dtype.type==numpy.string_: 44 | format='%dA'%arr.dtype.itemsize 45 | else: 46 | raise Exception("Oops unknown datatype %s"%arr.dtype) 47 | tmplist.append(pyfits.Column(name=name, array=arr, format=format)) 48 | hdu = pyfits.new_table(tmplist) 49 | hdu.writeto(filename,clobber=True) -------------------------------------------------------------------------------- /astrolibpy/astrolib/precess.py: -------------------------------------------------------------------------------- 1 | from numpy import sin, cos, deg2rad, transpose, dot, arcsin, arctan2, zeros,\ 2 | ndarray, array, rad2deg, pi 3 | from .premat import premat 4 | 5 | def precess(ra0, dec0, equinox1, equinox2, doprint=False, fk4=False, radian=False): 6 | """ 7 | NAME: 8 | PRECESS 9 | PURPOSE: 10 | Precess coordinates from EQUINOX1 to EQUINOX2. 11 | EXPLANATION: 12 | For interactive display, one can use the procedure ASTRO which calls 13 | PRECESS or use the /PRINT keyword. The default (RA,DEC) system is 14 | FK5 based on epoch J2000.0 but FK4 based on B1950.0 is available via 15 | the /FK4 keyword. 16 | 17 | Use BPRECESS and JPRECESS to convert between FK4 and FK5 systems 18 | CALLING SEQUENCE: 19 | PRECESS, ra, dec, [ equinox1, equinox2, /PRINT, /FK4, /RADIAN ] 20 | 21 | INPUT - OUTPUT: 22 | RA - Input right ascension (scalar or vector) in DEGREES, unless the 23 | /RADIAN keyword is set 24 | DEC - Input declination in DEGREES (scalar or vector), unless the 25 | /RADIAN keyword is set 26 | 27 | The input RA and DEC are modified by PRECESS to give the 28 | values after precession. 29 | 30 | OPTIONAL INPUTS: 31 | EQUINOX1 - Original equinox of coordinates, numeric scalar. If 32 | omitted, then PRECESS will query for EQUINOX1 and EQUINOX2. 33 | EQUINOX2 - Equinox of precessed coordinates. 34 | 35 | OPTIONAL INPUT KEYWORDS: 36 | /PRINT - If this keyword is set and non-zero, then the precessed 37 | coordinates are displayed at the terminal. Cannot be used 38 | with the /RADIAN keyword 39 | /FK4 - If this keyword is set and non-zero, the FK4 (B1950.0) system 40 | will be used otherwise FK5 (J2000.0) will be used instead. 41 | /RADIAN - If this keyword is set and non-zero, then the input and 42 | output RA and DEC vectors are in radians rather than degrees 43 | 44 | RESTRICTIONS: 45 | Accuracy of precession decreases for declination values near 90 46 | degrees. PRECESS should not be used more than 2.5 centuries from 47 | 2000 on the FK5 system (1950.0 on the FK4 system). 48 | 49 | EXAMPLES: 50 | (1) The Pole Star has J2000.0 coordinates (2h, 31m, 46.3s, 51 | 89d 15' 50.6"); compute its coordinates at J1985.0 52 | 53 | IDL> precess, ten(2,31,46.3)*15, ten(89,15,50.6), 2000, 1985, /PRINT 54 | 55 | ====> 2h 16m 22.73s, 89d 11' 47.3" 56 | 57 | (2) Precess the B1950 coordinates of Eps Ind (RA = 21h 59m,33.053s, 58 | DEC = (-56d, 59', 33.053") to equinox B1975. 59 | 60 | IDL> ra = ten(21, 59, 33.053)*15 61 | IDL> dec = ten(-56, 59, 33.053) 62 | IDL> precess, ra, dec ,1950, 1975, /fk4 63 | 64 | PROCEDURE: 65 | Algorithm from Computational Spherical Astronomy by Taff (1983), 66 | p. 24. (FK4). FK5 constants from "Astronomical Almanac Explanatory 67 | Supplement 1992, page 104 Table 3.211.1. 68 | 69 | PROCEDURE CALLED: 70 | Function PREMAT - computes precession matrix 71 | 72 | REVISION HISTORY 73 | Written, Wayne Landsman, STI Corporation August 1986 74 | Correct negative output RA values February 1989 75 | Added /PRINT keyword W. Landsman November, 1991 76 | Provided FK5 (J2000.0) I. Freedman January 1994 77 | Precession Matrix computation now in PREMAT W. Landsman June 1994 78 | Added /RADIAN keyword W. Landsman June 1997 79 | Converted to IDL V5.0 W. Landsman September 1997 80 | Correct negative output RA values when /RADIAN used March 1999 81 | Work for arrays, not just vectors W. Landsman September 2003 82 | Convert to Python Sergey Koposov July 2010 83 | """ 84 | scal = True 85 | if isinstance(ra0, ndarray): 86 | ra = ra0.copy() 87 | dec = dec0.copy() 88 | scal = False 89 | else: 90 | ra=array([ra0]) 91 | dec=array([dec0]) 92 | npts = ra.size 93 | 94 | if not radian: 95 | ra_rad = deg2rad(ra) #Convert to double precision if not already 96 | dec_rad = deg2rad(dec) 97 | else: 98 | ra_rad = ra 99 | dec_rad = dec 100 | 101 | a = cos(dec_rad) 102 | 103 | x = zeros((npts, 3)) 104 | x[:,0] = a * cos(ra_rad) 105 | x[:,1] = a * sin(ra_rad) 106 | x[:,2] = sin(dec_rad) 107 | 108 | # Use PREMAT function to get precession matrix from Equinox1 to Equinox2 109 | 110 | r = premat(equinox1, equinox2, fk4=fk4) 111 | 112 | x2 = transpose(dot(transpose(r), transpose(x))) #rotate to get output direction cosines 113 | 114 | ra_rad = zeros(npts) + arctan2(x2[:,1], x2[:,0]) 115 | dec_rad = zeros(npts) + arcsin(x2[:,2]) 116 | 117 | if not radian: 118 | ra = rad2deg(ra_rad) 119 | ra = ra + (ra < 0.) * 360.e0 #RA between 0 and 360 degrees 120 | dec = rad2deg(dec_rad) 121 | else: 122 | ra = ra_rad 123 | dec = dec_rad 124 | ra = ra + (ra < 0.) * 2.0e0 * pi 125 | 126 | if doprint: 127 | print ('Equinox (%.2f): %f,%f') % (equinox2, ra, dec) 128 | if scal: 129 | ra, dec = ra[0], dec[0] 130 | return ra, dec 131 | 132 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/precess_xyz.py: -------------------------------------------------------------------------------- 1 | from numpy import sin, cos, arctan2, sqrt, arcsin 2 | from .precess import precess 3 | 4 | def precess_xyz(x, y, z, equinox1, equinox2): 5 | #+ 6 | # NAME: 7 | # PRECESS_XYZ 8 | # 9 | # PURPOSE: 10 | # Precess equatorial geocentric rectangular coordinates. 11 | # 12 | # CALLING SEQUENCE: 13 | # precess_xyz, x, y, z, equinox1, equinox2 14 | # 15 | # INPUT/OUTPUT: 16 | # x,y,z: scalars or vectors giving heliocentric rectangular coordinates 17 | # THESE ARE CHANGED UPON RETURNING. 18 | # INPUT: 19 | # EQUINOX1: equinox of input coordinates, numeric scalar 20 | # EQUINOX2: equinox of output coordinates, numeric scalar 21 | # 22 | # OUTPUT: 23 | # x,y,z are changed upon return 24 | # 25 | # NOTES: 26 | # The equatorial geocentric rectangular coords are converted 27 | # to RA and Dec, precessed in the normal way, then changed 28 | # back to x, y and z using unit vectors. 29 | # 30 | #EXAMPLE: 31 | # Precess 1950 equinox coords x, y and z to 2000. 32 | # IDL> precess_xyz,x,y,z, 1950, 2000 33 | # 34 | #HISTORY: 35 | # Written by P. Plait/ACC March 24 1999 36 | # (unit vectors provided by D. Lindler) 37 | # Use /Radian call to PRECESS W. Landsman November 2000 38 | # Use two parameter call to ATAN W. Landsman June 2001 39 | #- 40 | #check inputs 41 | 42 | #take input coords and convert to ra and dec (in radians) 43 | 44 | ra = arctan2(y, x) 45 | _del = sqrt(x * x + y * y + z * z) #magnitude of distance to Sun 46 | dec = arcsin(z / _del) 47 | 48 | # precess the ra and dec 49 | ra,dec = precess(ra, dec, equinox1, equinox2, radian=True) 50 | 51 | #convert back to x, y, z 52 | xunit = cos(ra) * cos(dec) 53 | yunit = sin(ra) * cos(dec) 54 | zunit = sin(dec) 55 | 56 | x = xunit * _del 57 | y = yunit * _del 58 | z = zunit * _del 59 | 60 | return x,y,z 61 | 62 | 63 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/premat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from numpy import array, sin, cos, zeros, pi, zeros 3 | 4 | def premat(equinox1, equinox2, fk4=False): 5 | """ 6 | NAME: 7 | PREMAT 8 | PURPOSE: 9 | Return the precession matrix needed to go from EQUINOX1 to EQUINOX2. 10 | EXPLANTION: 11 | This matrix is used by the procedures PRECESS and BARYVEL to precess 12 | astronomical coordinates 13 | 14 | CALLING SEQUENCE: 15 | matrix = PREMAT( equinox1, equinox2, [ /FK4 ] ) 16 | 17 | INPUTS: 18 | EQUINOX1 - Original equinox of coordinates, numeric scalar. 19 | EQUINOX2 - Equinox of precessed coordinates. 20 | 21 | OUTPUT: 22 | matrix - double precision 3 x 3 precession matrix, used to precess 23 | equatorial rectangular coordinates 24 | 25 | OPTIONAL INPUT KEYWORDS: 26 | /FK4 - If this keyword is set, the FK4 (B1950.0) system precession 27 | angles are used to compute the precession matrix. The 28 | default is to use FK5 (J2000.0) precession angles 29 | 30 | EXAMPLES: 31 | Return the precession matrix from 1950.0 to 1975.0 in the FK4 system 32 | 33 | IDL> matrix = PREMAT( 1950.0, 1975.0, /FK4) 34 | 35 | PROCEDURE: 36 | FK4 constants from "Computational Spherical Astronomy" by Taff (1983), 37 | p. 24. (FK4). FK5 constants from "Astronomical Almanac Explanatory 38 | Supplement 1992, page 104 Table 3.211.1. 39 | 40 | REVISION HISTORY 41 | Written, Wayne Landsman, HSTX Corporation, June 1994 42 | Converted to IDL V5.0 W. Landsman September 1997 43 | """ 44 | 45 | deg_to_rad = pi / 180.0e0 46 | sec_to_rad = deg_to_rad / 3600.e0 47 | 48 | t = 0.001e0 * (equinox2 - equinox1) 49 | 50 | if not fk4: 51 | st = 0.001e0 * (equinox1 - 2000.e0) 52 | # Compute 3 rotation angles 53 | a = sec_to_rad * t * (23062.181e0 + st * (139.656e0 + 0.0139e0 * st) + t * (30.188e0 - 0.344e0 * st + 17.998e0 * t)) 54 | 55 | b = sec_to_rad * t * t * (79.280e0 + 0.410e0 * st + 0.205e0 * t) + a 56 | 57 | c = sec_to_rad * t * (20043.109e0 - st * (85.33e0 + 0.217e0 * st) + t * (-42.665e0 - 0.217e0 * st - 41.833e0 * t)) 58 | 59 | else: 60 | 61 | st = 0.001e0 * (equinox1 - 1900.e0) 62 | # Compute 3 rotation angles 63 | 64 | a = sec_to_rad * t * (23042.53e0 + st * (139.75e0 + 0.06e0 * st) + t * (30.23e0 - 0.27e0 * st + 18.0e0 * t)) 65 | 66 | b = sec_to_rad * t * t * (79.27e0 + 0.66e0 * st + 0.32e0 * t) + a 67 | 68 | c = sec_to_rad * t * (20046.85e0 - st * (85.33e0 + 0.37e0 * st) + t * (-42.67e0 - 0.37e0 * st - 41.8e0 * t)) 69 | 70 | 71 | sina = sin(a) 72 | sinb = sin(b) 73 | sinc = sin(c) 74 | cosa = cos(a) 75 | cosb = cos(b) 76 | cosc = cos(c) 77 | 78 | r = zeros((3, 3)) 79 | r[0,:] = array([cosa * cosb * cosc - sina * sinb, sina * cosb + cosa * sinb * cosc, cosa * sinc]) 80 | r[1,:] = array([-cosa * sinb - sina * cosb * cosc, cosa * cosb - sina * sinb * cosc, -sina * sinc]) 81 | r[2,:] = array([-cosb * sinc, -sinb * sinc, cosc]) 82 | 83 | return r 84 | 85 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/readcol.py: -------------------------------------------------------------------------------- 1 | import scipy.io 2 | import numpy 3 | 4 | def readcol(filename, delimiter=' ', format=None, skiprows=0, **kw): 5 | """ This routine reads the data from the ascii file 6 | a,b,c=readcol('dat.txt',delimiter='|') 7 | you can skip a certain number of rows in the top of the file by 8 | specifying skiprows=X option. 9 | The format option is needed if you have datatypes different from float in your table 10 | In that case format string should be comma delimted set of I (int) F(float) D (double) 11 | S (string) characters. E.g. 12 | a,b,c=readcol('dat.txt',format='I,S,D') 13 | """ 14 | if format==None: 15 | res=numpy.loadtxt(filename, delimiter=delimiter, skiprows=skiprows, **kw) 16 | nrows = res.shape[0] 17 | if res.ndim==2: 18 | ncols = res.shape[1] 19 | elif res.ndim==1: 20 | ncols=1 21 | res.shape=(nrows,1) 22 | else: 23 | raise "Exception: wrong array dimensions" 24 | 25 | stor=[] 26 | for i in range(ncols): 27 | stor.append(res[:,i]) 28 | return tuple(stor) 29 | else: 30 | types=[] 31 | i=0 32 | formats=format.split(',') 33 | convs={} 34 | 35 | retnull = lambda s: numpy.float(s or 0) 36 | for i, a in enumerate(formats): 37 | if a=='I': 38 | curtype=numpy.int32 39 | convs[i]=retnull 40 | elif a=='L': 41 | curtype=numpy.int64 42 | convs[i]=retnull 43 | elif a=='F': 44 | curtype=numpy.float32 45 | convs[i]=retnull 46 | elif a=='D': 47 | curtype=numpy.float64 48 | convs[i]=retnull 49 | elif a=='S': 50 | curtype="S100"#numpy.str 51 | else: 52 | raise Exception("Sorry, Unknown type in the format string\n The allowed types are S,I,F,D (string, int, float, double)") 53 | types.append(("a%d"%i,curtype)) 54 | 55 | rec=numpy.loadtxt(file(filename),dtype=types, delimiter=delimiter, 56 | skiprows=skiprows,converters=convs) 57 | ncols=len(rec[0]) 58 | nrows=len(rec) 59 | 60 | buf="(" 61 | stor=[] 62 | for a in formats: 63 | if a=='I': 64 | tmp=numpy.zeros(nrows,dtype=numpy.int32) 65 | if a=='L': 66 | tmp=numpy.zeros(nrows,dtype=numpy.int64) 67 | elif a=='F': 68 | tmp=numpy.zeros(nrows,dtype=numpy.float32) 69 | elif a=='D': 70 | tmp=numpy.zeros(nrows,dtype=numpy.float64) 71 | elif a=='S': 72 | tmp=numpy.zeros(nrows,dtype="S100") 73 | stor.append(tmp) 74 | 75 | for i in range(ncols): 76 | for j in range(nrows): 77 | stor[i][j]=rec[j][i] 78 | return tuple(stor) 79 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/sphdist.py: -------------------------------------------------------------------------------- 1 | from numpy import deg2rad,rad2deg,sin,cos,sqrt,arcsin 2 | 3 | def sphdist (ra1, dec1, ra2, dec2): 4 | """measures the spherical distance in degrees 5 | The input has to be in degrees too 6 | """ 7 | dec1_r = deg2rad(dec1) 8 | dec2_r = deg2rad(dec2) 9 | return 2 *\ 10 | rad2deg \ 11 | ( 12 | arcsin 13 | ( 14 | sqrt 15 | ( 16 | ( 17 | sin((dec1_r - dec2_r) / 2) 18 | )**2 19 | + 20 | cos(dec1_r) * cos(dec2_r) * 21 | ( 22 | sin((deg2rad(ra1 - ra2)) / 2) 23 | )**2 24 | ) 25 | ) 26 | ) 27 | 28 | def sphdist_fast(ra1,dec1,ra2,dec2): 29 | import numexpr 30 | return numexpr.evaluate('2*57.295779513082323*(arcsin(sqrt((sin(0.017453292519943295*(dec1-dec2)/2))**2+cos(0.017453292519943295*dec1)*cos(0.017453292519943295*dec2)*(sin(0.017453292519943295*((ra1-ra2))/2))**2)))') 31 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/xyz.py: -------------------------------------------------------------------------------- 1 | from numpy import sqrt, pi, cos, sin 2 | from .precess_xyz import precess_xyz 3 | 4 | def xyz(date, equinox=None): 5 | """ 6 | NAME: 7 | XYZ 8 | PURPOSE: 9 | Calculate geocentric X,Y, and Z and velocity coordinates of the Sun 10 | EXPLANATION: 11 | Calculates geocentric X,Y, and Z vectors and velocity coordinates 12 | (dx, dy and dz) of the Sun. (The positive X axis is directed towards 13 | the equinox, the y-axis, towards the point on the equator at right 14 | ascension 6h, and the z axis toward the north pole of the equator). 15 | Typical position accuracy is <1e-4 AU (15000 km). 16 | 17 | CALLING SEQUENCE: 18 | XYZ, date, x, y, z, [ xvel, yvel, zvel, EQUINOX = ] 19 | 20 | INPUT: 21 | date: reduced julian date (=JD - 2400000), scalar or vector 22 | 23 | OUTPUT: 24 | x,y,z: scalars or vectors giving heliocentric rectangular coordinates 25 | (in A.U) for each date supplied. Note that sqrt(x^2 + y^2 26 | + z^2) gives the Earth-Sun distance for the given date. 27 | xvel, yvel, zvel: velocity vectors corresponding to X, Y and Z. 28 | 29 | OPTIONAL KEYWORD INPUT: 30 | EQUINOX: equinox of output. Default is 1950. 31 | 32 | EXAMPLE: 33 | What were the rectangular coordinates and velocities of the Sun on 34 | Jan 22, 1999 0h UT (= JD 2451200.5) in J2000 coords? NOTE: 35 | Astronomical Almanac (AA) is in TDT, so add 64 seconds to 36 | UT to convert. 37 | 38 | IDL> xyz,51200.5+64.d/86400.d,x,y,z,xv,yv,zv,equinox = 2000 39 | 40 | Compare to Astronomical Almanac (1999 page C20) 41 | X (AU) Y (AU) Z (AU) 42 | XYZ: 0.51456871 -0.76963263 -0.33376880 43 | AA: 0.51453130 -0.7697110 -0.3337152 44 | abs(err): 0.00003739 0.00007839 0.00005360 45 | abs(err) 46 | (km): 5609 11759 8040 47 | 48 | NOTE: Velocities in AA are for Earth/Moon barycenter 49 | (a very minor offset) see AA 1999 page E3 50 | X VEL (AU/DAY) YVEL (AU/DAY) Z VEL (AU/DAY) 51 | XYZ: -0.014947268 -0.0083148382 -0.0036068577 52 | AA: -0.01494574 -0.00831185 -0.00360365 53 | abs(err): 0.000001583 0.0000029886 0.0000032077 54 | abs(err) 55 | (km/sec): 0.00265 0.00519 0.00557 56 | 57 | PROCEDURE CALLS: 58 | PRECESS_XYZ 59 | REVISION HISTORY 60 | Original algorithm from Almanac for Computers, Doggett et al. USNO 1978 61 | Adapted from the book Astronomical Photometry by A. Henden 62 | Written W. Landsman STX June 1989 63 | Correct error in X coefficient W. Landsman HSTX January 1995 64 | Added velocities, more terms to positions and EQUINOX keyword, 65 | some minor adjustments to calculations 66 | P. Plait/ACC March 24, 1999 67 | """ 68 | 69 | picon = pi / 180.0e0 70 | t = (date - 15020.0e0) / 36525.0e0 #Relative Julian century from 1900 71 | 72 | # NOTE: longitude arguments below are given in *equinox* of date. 73 | # Precess these to equinox 1950 to give everything an even footing. 74 | # Compute argument of precession from equinox of date back to 1950 75 | pp = (1.396041e0 + 0.000308e0 * (t + 0.5e0)) * (t - 0.499998e0) 76 | 77 | # Compute mean solar longitude, precessed back to 1950 78 | el = 279.696678e0 + 36000.76892e0 * t + 0.000303e0 * t * t - pp 79 | 80 | # Compute Mean longitude of the Moon 81 | c = 270.434164e0 + 480960.e0 * t + 307.883142e0 * t - 0.001133e0 * t * t - pp 82 | 83 | # Compute longitude of Moon's ascending node 84 | n = 259.183275e0 - 1800.e0 * t - 134.142008e0 * t + 0.002078e0 * t * t - pp 85 | 86 | # Compute mean solar anomaly 87 | g = 358.475833e0 + 35999.04975e0 * t - 0.00015e0 * t * t 88 | 89 | # Compute the mean jupiter anomaly 90 | j = 225.444651e0 + 2880.0e0 * t + 154.906654e0 * t * t 91 | 92 | # Compute mean anomaly of Venus 93 | v = 212.603219e0 + 58320.e0 * t + 197.803875e0 * t + 0.001286e0 * t * t 94 | 95 | # Compute mean anomaly of Mars 96 | m = 319.529425e0 + 19080.e0 * t + 59.8585e0 * t + 0.000181e0 * t * t 97 | 98 | # Convert degrees to radians for trig functions 99 | el = el * picon 100 | g = g * picon 101 | j = j * picon 102 | c = c * picon 103 | v = v * picon 104 | n = n * picon 105 | m = m * picon 106 | 107 | # Calculate X,Y,Z using trigonometric series 108 | x = 0.999860e0 * cos(el) - 0.025127e0 * cos(g - el) + 0.008374e0 * cos(g + el) + 0.000105e0 * cos(g + g + el) + 0.000063e0 * t * cos(g - el) + 0.000035e0 * cos(g + g - el) - 0.000026e0 * sin(g - el - j) - 0.000021e0 * t * cos(g + el) + 0.000018e0 * sin(2.e0 * g + el - 2.e0 * v) + 0.000017e0 * cos(c) - 0.000014e0 * cos(c - 2.e0 * el) + 0.000012e0 * cos(4.e0 * g + el - 8.e0 * m + 3.e0 * j) - 0.000012e0 * cos(4.e0 * g - el - 8.e0 * m + 3.e0 * j) - 0.000012e0 * cos(g + el - v) + 0.000011e0 * cos(2.e0 * g + el - 2.e0 * v) + 0.000011e0 * cos(2.e0 * g - el - 2.e0 * j) 109 | 110 | 111 | y = 0.917308e0 * sin(el) + 0.023053e0 * sin(g - el) + 0.007683e0 * sin(g + el) + 0.000097e0 * sin(g + g + el) - 0.000057e0 * t * sin(g - el) - 0.000032e0 * sin(g + g - el) - 0.000024e0 * cos(g - el - j) - 0.000019e0 * t * sin(g + el) - 0.000017e0 * cos(2.e0 * g + el - 2.e0 * v) + 0.000016e0 * sin(c) + 0.000013e0 * sin(c - 2.e0 * el) + 0.000011e0 * sin(4.e0 * g + el - 8.e0 * m + 3.e0 * j) + 0.000011e0 * sin(4.e0 * g - el - 8.e0 * m + 3.e0 * j) - 0.000011e0 * sin(g + el - v) + 0.000010e0 * sin(2.e0 * g + el - 2.e0 * v) - 0.000010e0 * sin(2.e0 * g - el - 2.e0 * j) 112 | 113 | 114 | z = 0.397825e0 * sin(el) + 0.009998e0 * sin(g - el) + 0.003332e0 * sin(g + el) + 0.000042e0 * sin(g + g + el) - 0.000025e0 * t * sin(g - el) - 0.000014e0 * sin(g + g - el) - 0.000010e0 * cos(g - el - j) 115 | 116 | #Precess_to new equator? 117 | if equinox is not None: 118 | x, y, z = precess_xyz(x, y, z, 1950, equinox) 119 | 120 | xvel = -0.017200e0 * sin(el) - 0.000288e0 * sin(g + el) - 0.000005e0 * sin(2.e0 * g + el) - 0.000004e0 * sin(c) + 0.000003e0 * sin(c - 2.e0 * el) + 0.000001e0 * t * sin(g + el) - 0.000001e0 * sin(2.e0 * g - el) 121 | 122 | yvel = 0.015780 * cos(el) + 0.000264 * cos(g + el) + 0.000005 * cos(2.e0 * g + el) + 0.000004 * cos(c) + 0.000003 * cos(c - 2.e0 * el) - 0.000001 * t * cos(g + el) 123 | 124 | zvel = 0.006843 * cos(el) + 0.000115 * cos(g + el) + 0.000002 * cos(2.e0 * g + el) + 0.000002 * cos(c) + 0.000001 * cos(c - 2.e0 * el) 125 | 126 | #Precess to new equator? 127 | 128 | if equinox is not None: 129 | xvel, yvel, zvel = precess_xyz(xvel, yvel, zvel, 1950, equinox) 130 | 131 | return x, y, z, xvel, yvel, zvel 132 | 133 | -------------------------------------------------------------------------------- /astrolibpy/astrolib/zang.py: -------------------------------------------------------------------------------- 1 | from numpy import pi 2 | from lumdist import lumdist 3 | 4 | def zang(dl, z, h0=None, k=None, lambda0=None, omega_m=None, q0=None, 5 | silent=None): 6 | """ 7 | NAME: 8 | ZANG 9 | PURPOSE: 10 | Determine the angular size of an object as a function of redshift 11 | EXPLANATION: 12 | Requires an input size in kpc and returns an angular size in arc seconds 13 | Default cosmology has a Hubble constant of 70 km/s/Mpc, Omega (matter) 14 | =0.3 and a normalized cosmological constant Lambda = 0.7; however these 15 | values can be changed with appropriate keywords. 16 | 17 | CALLING SEQUENCE: 18 | angsiz = zang( dl, [ z, H0 =, Omega_m =, Lambda0 = , q0 = , k =, 19 | /SILENT] ) 20 | 21 | INPUTS: 22 | dl - linear size of the object *in kpc*, non-negative scalar or vector 23 | z - redshift of object, postive scalar or vector 24 | Either dl and z must have the same number of elements, or at least 25 | one of them must be a vector. 26 | OPTIONAL INPUT KEYWORDS 27 | H0 - Hubble constant in km/s/Mpc, default is 70 28 | 29 | No more than two of the following four parameters should be 30 | specified. None of them need be specified, default values are given 31 | k - curvature constant, normalized to the closure density. Default is 32 | 0, indicating a flat universe 33 | Omega_m - Matter density, normalized to the closure density, default 34 | is 0.3. Must be non-negative 35 | Lambda0 - Cosmological constant, normalized to the closure density, 36 | default is 0.7 37 | q0 - Deceleration parameter, numeric scalar = -R*(R'')/(R')^2, default 38 | is -0.55 39 | 40 | Note that Omega_m + lambda0 + k = 1 and q0 = 0.5*omega_m - lambda0 41 | OUTPUT: 42 | angsiz - Angular size of the object at the given redshift in 43 | arc seconds 44 | EXAMPLE: 45 | (1) What would be the angular size of galaxy of diameter 50 kpc at a redshift 46 | of 1.5 in an open universe with Lambda = 0 and Omega (matter) = 0.3. 47 | Assume the default Hubble constant value of 70 km/s/Mpc. 48 | 49 | IDL> print,zang(50,1.5, Lambda = 0,omega_m = 0.3) 50 | ====> 6.58 arc seconds 51 | 52 | (2) Now plot the angular size of a 50 kpc diameter galaxy as a function of 53 | redshift for the default cosmology (Lambda = 0.7, Omega_m=0.3) up to 54 | z = 0.5 55 | IDL> z = findgen(50)/10. + 0.1 ;Angular size undefined at z = 0 56 | IDL> plot,z,zang(50,z),xtit='z',ytit='Angular Size (")' 57 | NOTES: 58 | This procedure underwent a major revision in April 2000 to allow for a 59 | cosmological constant, ***including a change of the calling sequence*** 60 | 61 | Be sure to supply the input linear size dl in units of kpc. 62 | PROCEDURES CALLED: 63 | LUMDIST() -- Calculates the luminosity distance 64 | REVISION HISTORY: 65 | Written J. Hill STX July, 1988 66 | Converted to IDL V5.0 W. Landsman September 1997 67 | Major rewrite to call LUMDIST function W. Landsman April 2000 68 | Convert to python S. Koposov 2010 69 | """ 70 | _radeg = 180.0 / pi 71 | d = lumdist(z, h0=h0, k=k, lambda0=lambda0, omega_m=omega_m, q0=q0, 72 | silent=silent) 73 | 74 | # Angular distance is equal to the luminosity distance times (1+z)^2 75 | return _radeg * 3600. * dl * (1. + z) ** 2 / (1000. * d) 76 | 77 | -------------------------------------------------------------------------------- /astrolibpy/mpfit/mpfitexpr.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (C) 2009 Sergey Koposov 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | """ 17 | 18 | import mpfit 19 | import re 20 | import numpy 21 | import scipy 22 | 23 | def mpfitexpr(func, x, y, err , start_params, check=True, full_output=False, 24 | imports=None, **kw): 25 | """Fit the used defined expression to the data 26 | Input: 27 | - func: string with the function definition 28 | - x: x vector 29 | - y: y vector 30 | - err: vector with the errors of y 31 | - start_params: the starting parameters for the fit 32 | Output: 33 | - The tuple (params, yfit) with best-fit params and the values of func evaluated at x 34 | Keywords: 35 | - check: boolean parameter. If true(default) the function will be checked for sanity 36 | - full_output: boolean parameter. If True(default is False) then instead of best-fit parameters the mpfit object is returned 37 | - imports: list of strings, of optional modules to be imported, required to evaluate the function 38 | Example: 39 | params,yfit=mpfitexpr('p[0]+p[2]*(x-p[1])',x,y,err,[0,10,1]) 40 | 41 | If you need to use numpy and scipy functions in your function, then 42 | you must to use the full names of these functions, e.g.: 43 | numpy.sin, numpy.cos etc. 44 | 45 | This function is motivated by mpfitexpr() from wonderful MPFIT IDL package 46 | written by Craig Markwardt 47 | 48 | """ 49 | 50 | hash={} 51 | hash['numpy']=numpy 52 | hash['scipy']=scipy 53 | 54 | if imports is not None: 55 | for i in imports: 56 | #exec '%s=__import__("%s")'%(a,b) in globals(),locals() 57 | hash[i]= __import__(i) 58 | def myfunc(p,fjac=None,x=None, y=None, err=None): 59 | return [0, eval('(y-(%s))/err'%func,hash,locals())] 60 | 61 | myre = "(?:[^a-zA-Z_]|^)p\[(\d+)\]" 62 | r = re.compile(myre) 63 | maxp = -1 64 | for m in re.finditer(r,func): 65 | curp = int(m.group(1)) 66 | maxp = curp if curp > maxp else maxp 67 | if check: 68 | if maxp == -1: 69 | raise Exception("wrong function format") 70 | if maxp + 1 != len(start_params): 71 | raise Exception("the length of the start_params != the length of the parameter verctor of the function") 72 | fa={'x' : x, 'y' : y,'err' : err} 73 | res = mpfit.mpfit(myfunc,start_params,functkw=fa,**kw) 74 | yfit = eval(func, hash, {'x':x, 'p': res.params}) 75 | if full_output: 76 | return (res, yfit) 77 | else: 78 | return (res.params, yfit) 79 | -------------------------------------------------------------------------------- /astrolibpy/mpfit/tests/__init__.py: -------------------------------------------------------------------------------- 1 | def test(level=1, verbosity=1): 2 | from numpy.testing import Tester 3 | return Tester().test(level, verbosity) 4 | -------------------------------------------------------------------------------- /astrolibpy/mpfit/tests/test_mpfit.py: -------------------------------------------------------------------------------- 1 | from numpy.testing import * 2 | import numpy as N 3 | import copy 4 | 5 | from mpfit import mpfit 6 | 7 | 8 | def Flin(x,p): 9 | y = p[0] -p[1]*x 10 | return y 11 | 12 | 13 | def myfunctlin(p, fjac=None, x=None, y=None, err=None): 14 | # Parameter values are passed in "p" 15 | # If fjac==None then partial derivatives should not be 16 | # computed. It will always be None if MPFIT is called with default 17 | # flag. 18 | model = Flin(x, p) 19 | # Non-negative status value means MPFIT should continue, negative means 20 | # stop the calculation. 21 | status = 0 22 | return [status, (y-model)/err] 23 | 24 | 25 | 26 | def test_linfit(): 27 | x=N.array([-1.7237128E+00,1.8712276E+00,-9.6608055E-01, 28 | -2.8394297E-01,1.3416969E+00,1.3757038E+00, 29 | -1.3703436E+00,4.2581975E-02,-1.4970151E-01, 30 | 8.2065094E-01]) 31 | y=N.array([1.9000429E-01,6.5807428E+00,1.4582725E+00, 32 | 2.7270851E+00,5.5969253E+00,5.6249280E+00, 33 | 0.787615,3.2599759E+00,2.9771762E+00, 34 | 4.5936475E+00]) 35 | ey=0.07*N.ones(y.shape,dtype='float64') 36 | p0=N.array([1.0,1.0],dtype='float64') #initial conditions 37 | pactual=N.array([3.2,1.78]) #actual values used to make data 38 | parbase={'value':0., 'fixed':0, 'limited':[0,0], 'limits':[0.,0.]} 39 | parinfo=[] 40 | for i in range(len(pactual)): 41 | parinfo.append(copy.deepcopy(parbase)) 42 | for i in range(len(pactual)): 43 | parinfo[i]['value']=p0[i] 44 | fa = {'x':x, 'y':y, 'err':ey} 45 | m = mpfit(myfunctlin, p0, parinfo=parinfo,functkw=fa) 46 | if (m.status <= 0): 47 | print 'error message = ', m.errmsg 48 | assert N.allclose(m.params,N.array([ 3.20996572, -1.7709542 ],dtype='float64')) 49 | assert N.allclose(m.perror,N.array([ 0.02221018, 0.01893756],dtype='float64')) 50 | chisq=(myfunctlin(m.params, x=x, y=y, err=ey)[1]**2).sum() 51 | 52 | assert N.allclose(N.array([chisq],dtype='float64'),N.array([2.756284983],dtype='float64')) 53 | assert m.dof==8 54 | return 55 | 56 | def myfunctrosenbrock(p, fjac=None): 57 | # rosenbrock function 58 | res = N.array([1-p[0],-(1-p[0]),10*(p[1]-p[0]**2),-10*(p[1]-p[0]**2)]) 59 | status = 0 60 | return [status, res] 61 | 62 | 63 | 64 | def test_rosenbrock(): 65 | p0=N.array([-1,1.],dtype='float64') #initial conditions 66 | pactual=N.array([1.,1.]) #actual minimum of the rosenbrock function 67 | m = mpfit(myfunctrosenbrock, p0) 68 | if (m.status <= 0): 69 | print 'error message = ', m.errmsg 70 | assert m.status > 0 71 | assert N.allclose(m.params,pactual) 72 | assert N.allclose(m.fnorm,0) 73 | return 74 | 75 | if __name__ == "__main__": 76 | run_module_suite() 77 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/adabinner.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009-2010 Sergey Koposov 2 | # This file is part of astrolibpy 3 | # 4 | # astrolibpy is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # astrolibpy is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with astrolibpy. If not, see . 16 | 17 | 18 | import numpy.random, numpy,quick_hist, scipy.stats, random 19 | 20 | 21 | __doc__=""" 22 | Adaptive binner module 23 | Functions: 24 | hist: 25 | 1D adaptive histogram 26 | hist2d: 27 | 2D adaptive histogram 28 | 29 | """ 30 | def __doit2d(x, y, hi1=None, hi2=None, thresh=None): 31 | hhs ={} 32 | for curhi in range(hi1,hi2+1): 33 | hh = quick_hist.quick_hist((x, y), range=((0, 1), (0, 1)), 34 | nbins=[2**curhi,2**curhi]) 35 | hhs[curhi]=hh 36 | pixcen = [] 37 | hh0 = hhs[hi2] * 1 # accumulator of the result 38 | area = hh0 * 0 39 | 40 | two = 2 41 | poiss = scipy.stats.poisson(thresh) 42 | 43 | DeepenOrNot = lambda x: random.random() < poiss.cdf(x) 44 | #DeepenOrNot = lambda x: x>thresh 45 | 46 | def doitit(it, i, j): 47 | curhhs = hhs[it] 48 | if it==hi2: 49 | hh[i:i+2, j:j+2] = curhhs[i:i+2, j:j+2] 50 | area[i:i+2, j:j+2] = 2**(-it) 51 | pixcen.append((i+.5,j+.5)) 52 | else: 53 | for ii in range(i, i+2): 54 | for jj in range(j, j+2): 55 | curval = curhhs[ii,jj] 56 | if DeepenOrNot(curval): 57 | doitit(it+1, ii*two, jj*two) 58 | else: 59 | dx=2**(hi2-it) 60 | hh0[ii*dx:(ii+1)*dx, jj*dx:(jj+1)*dx]=curval 61 | area[ii*dx:(ii+1)*dx, jj*dx:(jj+1)*dx] = 2**(-it) 62 | pixcen.append((ii*dx+dx/2.+0.5,jj*dx+dx/2.+0.5)) 63 | n1 = 2**hi1 64 | dn = 2**(hi2-hi1) 65 | 66 | for i in range(n1): 67 | for j in range(n1): 68 | if DeepenOrNot(hhs[hi1][i,j]): 69 | doitit(hi1+1,i*two,j*two) 70 | else: 71 | hh0[i*dn:(i+1)*dn, j*dn:(j+1)*dn] = hhs[hi1][i,j] 72 | area[i*dn:(i+1)*dn, j*dn:(j+1)*dn] = 2**(-hi1) 73 | pixcen.append((i*dn+dn/2.+0.5,j*dn+dn/2.+0.5)) 74 | area = (2**hi1*area)**2 # in smallest pixels squared 75 | return hh0*1./area,pixcen,area 76 | 77 | 78 | def __doit1d(x, hi1=None, hi2=None, thresh=None): 79 | hhs ={} 80 | for curhi in range(hi1,hi2+1): 81 | hh = quick_hist.quick_hist((x,), range=((0, 1),), 82 | nbins=[2**curhi]) 83 | hhs[curhi]=hh 84 | 85 | hh0 = hhs[hi2] * 1 # accumulator of the result 86 | area = hh0 * 0 87 | 88 | two = 2 89 | poiss = scipy.stats.poisson(thresh) 90 | 91 | DeepenOrNot = lambda x: random.random() < poiss.cdf(x) 92 | #DeepenOrNot = lambda x: x>thresh 93 | 94 | def doitit(it, i): 95 | curhhs = hhs[it] 96 | if it==hi2: 97 | hh[i:i+2] = curhhs[i:i+2] 98 | area[i:i+2] = 2**(-it) 99 | else: 100 | for ii in range(i, i+2): 101 | curval = curhhs[ii] 102 | if DeepenOrNot(curval): 103 | doitit(it+1, ii*two) 104 | else: 105 | dx=2**(hi2-it) 106 | hh0[ii*dx:(ii+1)*dx]=curval 107 | area[ii*dx:(ii+1)*dx] = 2**(-it) 108 | n1 = 2**hi1 109 | dn = 2**(hi2-hi1) 110 | 111 | for i in range(n1): 112 | if DeepenOrNot(hhs[hi1][i]): 113 | doitit(hi1+1,i*two) 114 | else: 115 | hh0[i*dn:(i+1)*dn] = hhs[hi1][i] 116 | area[i*dn:(i+1)*dn] = 2**(-hi1) 117 | return hh0*1./(2**hi1*area) 118 | 119 | 120 | def hist2d(x, y, xmin=None, xmax=None, ymin=None, ymax=None, hi=[2,10], 121 | thresh=30, full_output=False): 122 | """ 123 | This function does the 2D histogram with adaptive binning 124 | Example: 125 | >> hist2d(xs,ys, hi=[3,6], thresh=30) 126 | >> hh,xloc,yloc,pixcen,area = hist2d(xra,dec,full_output=True,thresh=100) 127 | Keyword parameters: 128 | ------------------ 129 | hi 130 | the list of two integer values: they describe how coarse the 131 | largest bin and how fine is the smallest bin, e.g. 132 | [2,5] means the largest possible bin has a size 133 | of 1/2**2 of the your dataset and the smallest bin has a 134 | size of 1/2**5 135 | thresh 136 | the minimum number of points within a bin allowed ( 137 | if the number is smaller than the threshold then further 138 | decreasing of the bin size is not allowed by the algorithm) 139 | xmin,xmax,ymin,ymax 140 | x-ranges and y-ranges. If not specified, they are determined 141 | from the x.min(),x.max(),y.min(),y.max() 142 | full_output 143 | Boolean controlling whether to output just just the histogram(full_output=False) 144 | or output the tuple with the histogram, grid-centers in x, grid-centers in y, 145 | pixel-centers, and area 146 | """ 147 | xmin = x.min() if xmin is None else xmin 148 | ymin = y.min() if ymin is None else ymin 149 | xmax = x.max() if xmax is None else xmax 150 | ymax = y.max() if ymax is None else ymax 151 | 152 | xmod = (x-xmin)/(xmax-xmin) 153 | ymod = (y-ymin)/(ymax-ymin) 154 | 155 | ind = (xmod>=0)&(xmod<=1)&(ymod>=0)&(ymod<=1) 156 | hh,pixcen,area = __doit2d(xmod[ind], ymod[ind], hi1=hi[0], hi2=hi[1], thresh=thresh) 157 | xloc = numpy.linspace(xmin,xmax,hh.shape[0],False) 158 | yloc = numpy.linspace(ymin,ymax,hh.shape[0],False) 159 | xloc += 0.5*(xloc[1]-xloc[0]) 160 | yloc += 0.5*(yloc[1]-yloc[0]) 161 | 162 | if full_output: 163 | out = hh,xloc,yloc,pixcen,area 164 | else: 165 | out = hh 166 | return out 167 | 168 | def hist(x, xmin=None, xmax=None, hi=[2,10], thresh=30): 169 | """ 170 | This function does the 1D histogram with adaptive binning 171 | Example: 172 | >> loc, hh = hist(xs, hi=[3,6], thresh=30) 173 | 174 | Keyword parameters: 175 | ------------------ 176 | hi 177 | the list of two integer values: they describe how coarse the 178 | largest bin and how fine is the smallest bin, e.g. 179 | [2,5] means the largest possible bin has a size 180 | of 1/2**2 of the your dataset and the smallest bin has a 181 | size of 1/2**5 182 | thresh 183 | the minimum number of points within a bin allowed ( 184 | if the number is smaller than the threshold then further 185 | decreasing of the bin size is not allowed by the algorithm) 186 | xmin,xmax 187 | x-range. If not specified, they are determined 188 | from the x.min(),x.max() 189 | -------------------------------------------- 190 | Returns: 191 | the histogram and the bin edges vector 192 | """ 193 | xmin = x.min() if xmin is None else xmin 194 | xmax = x.max() if xmax is None else xmax 195 | 196 | xmod = (x-xmin)/(xmax-xmin) 197 | 198 | ind = (xmod>=0)&(xmod<=1) 199 | hh = __doit1d(xmod[ind], hi1=hi[0], hi2=hi[1], thresh=thresh) 200 | loc = numpy.linspace(xmin,xmax,len(hh)+1,True) 201 | return hh, loc 202 | 203 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/cacheFunc.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class cacheFunc: 5 | """ class to cache function calls 6 | E.g. 7 | >> def func(a,b=3): 8 | >> # do something 9 | >> print 'Called', a,b 10 | >> return a+b 11 | 12 | >> funcC = cacheFunc(func) 13 | >> print funcC(2,b=3) 14 | Called 2 3 15 | 5 16 | >> print funcC(3,b=4) 17 | Called 3 4 18 | 7 19 | >> print funcC(2,b=3) 20 | 5 21 | It also can be used as decorator: 22 | @cacheFunc 23 | def myfunc(a,b,c,d=3,e=4): 24 | .... 25 | """ 26 | def __init__(self, func,maxn=10): 27 | self.hash={} 28 | self.l=deque() 29 | self.maxn=maxn 30 | self.func=func 31 | 32 | def __call__(self,*args, **kw): 33 | key = args+tuple(kw.iteritems()) 34 | if self.hash.get(key) is not None: 35 | pass 36 | else: 37 | self.hash[key]=self.func(*args, **kw) 38 | self.l.append(key) 39 | if len(self.l)>self.maxn: 40 | delarg = self.l.popleft() 41 | del self.hash[delarg] 42 | del delarg 43 | return self.hash[key] 44 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/collect_arr.py: -------------------------------------------------------------------------------- 1 | import pypar 2 | 3 | def collect_arr(arr): 4 | """ 5 | A useful collection routine for pypar. 6 | If you are using pypar to parallelize the set of nested loops and fill 7 | the resulting array, you usually need to combine the resulting array 8 | from several mpi threads. In that case you just can execute 9 | res=collect_arr(res) 10 | And it will add the arrays from all the threads and store them in 11 | the thread number 0 12 | """ 13 | if pypar.rank() > 0: 14 | pypar.send(arr, 0) 15 | else: 16 | for i in range(1, pypar.size()): 17 | arr = arr + pypar.receive(i) 18 | return arr 19 | 20 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/correct_pm.py: -------------------------------------------------------------------------------- 1 | import gal_uvw, cv_coord, euler 2 | import numpy as np 3 | 4 | def get_uvw_sun(vlsr=220): 5 | 6 | usun, vsun, wsun = -8.5, 13.38 + vlsr, 6.49 # the signs are in the coord system of gal_uvw 7 | # e.g. U points toward anticenter 8 | return usun,vsun,wsun 9 | 10 | def correct_pm(ra, dec, pmra, pmdec, dist, vlsr=220): 11 | """Corrects the proper motion for the speed of the Sun 12 | Arguments: 13 | ra - RA in deg 14 | dec -- Declination in deg 15 | pmra -- pm in RA in mas/yr 16 | pmdec -- pm in declination in mas/yr 17 | dist -- distance in kpc 18 | Returns: 19 | (pmra,pmdec) the tuple with the proper motions corrected for the Sun's motion 20 | """ 21 | 22 | one = ra * 0 + 1 23 | zero = ra * 0 24 | usun,vsun,wsun = get_uvw_sun(vlsr=vlsr) 25 | dist_pc = dist * 1000. 26 | ur, vr, wr = gal_uvw.gal_uvw(distance=dist_pc, ra=ra, dec=dec, 27 | pmra=one, pmdec=zero, vrad=zero) 28 | ud, vd, wd = gal_uvw.gal_uvw(distance=dist_pc, ra=ra, dec=dec, 29 | pmra=zero, pmdec=one, vrad=zero) 30 | 31 | 32 | 33 | d_pmra = -(ur * usun + vr * vsun + wr * wsun) / (ur**2 + vr**2 + wr**2) 34 | d_pmdec = -(ud * usun + vd * vsun + wd * wsun) / (ud**2 + vd**2 + wd**2) 35 | # d_pmra d_pmdec -- these should be the pm's of the non-moving object as seen from the moving Sun 36 | return (pmra-d_pmra,pmdec-d_pmdec) 37 | 38 | 39 | def correct_vel(ra, dec, vel, vlsr=220): 40 | """Corrects the proper motion for the speed of the Sun 41 | Arguments: 42 | ra - RA in deg 43 | dec -- Declination in deg 44 | pmra -- pm in RA in mas/yr 45 | pmdec -- pm in declination in mas/yr 46 | dist -- distance in kpc 47 | Returns: 48 | (pmra,pmdec) the tuple with the proper motions corrected for the Sun's motion 49 | """ 50 | 51 | 52 | l,b = euler.euler(ra, dec) 53 | l = np.deg2rad(l) 54 | b = np.deg2rad(b) 55 | usun,vsun,wsun = get_uvw_sun(vlsr=vlsr) 56 | 57 | delta = -usun*np.cos(l)*np.cos(b) + vsun * np.sin(l) * np.cos(b) + wsun * np.sin(b) 58 | # projection of the sun's velocity to the lign of sight vector 59 | # notice the first minus -- it is because the usun is in the coord system where 60 | # X points towards anticenter 61 | 62 | #return the corrected velocity 63 | return vel + delta 64 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/from_hex.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009-2010 Sergey Koposov 2 | # This file is part of astrolibpy 3 | # 4 | # astrolibpy is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # astrolibpy is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with astrolibpy. If not, see . 16 | 17 | 18 | import numpy, re 19 | def from_hex(arr, delim=':'): 20 | r=re.compile('\s*(\-?)(.+)%s(.+)%s(.+)'%(delim,delim)) 21 | ret=[] 22 | for a in arr: 23 | m = r.search(a) 24 | 25 | sign = m.group(1)=='-' 26 | if sign: 27 | sign=-1 28 | else: 29 | sign=1 30 | i1 = int(m.group(2)) 31 | i2 = int(m.group(3)) 32 | i3 = float(m.group(4)) 33 | val = sign*(int(i1)+int(i2)/60.+(float(i3))/3600.) 34 | ret.append(val) 35 | return numpy.array(ret) 36 | 37 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/get_dss.py: -------------------------------------------------------------------------------- 1 | import urllib2,StringIO,aplpy,pyfits 2 | import matplotlib.pyplot as plt 3 | def get_dss(ra, dec, survey='all', radius = 15, debug=False, noerase=False, 4 | subplot=(1,1,1)): 5 | """ 6 | radius is in arcmins 7 | survey: 'all', 'poss2ukstu', 'dss1'.. 8 | """ 9 | #survey='poss2ukstu' 10 | url='http://archive.stsci.edu/cgi-bin/dss_search?v=%s&r=%fd&d=%fd&e=J2000&h=%f&w=%f&f=fits&c=none&fov=NONE&v3='%(survey,ra,dec,radius,radius) 11 | 12 | response = urllib2.urlopen(url) 13 | if debug: 14 | print url 15 | html = response.read() 16 | f=StringIO.StringIO(html) 17 | 18 | dat=pyfits.open(f) 19 | if not noerase: 20 | plt.clf() 21 | gc=aplpy.FITSFigure(dat,figure=plt.gcf(),subplot=subplot) 22 | gc.set_tick_labels_format('ddd.dddd','ddd.dddd') 23 | gc.show_grayscale() 24 | return gc 25 | 26 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/idlplotInd.py: -------------------------------------------------------------------------------- 1 | import idlplot 2 | import numpy as np 3 | 4 | """ 5 | This module is a set wrappers around idlplot designed to make 6 | the plots of subsets of the data: e.g. 7 | plot(x,y,ind=ind) 8 | instead of plot(x[ind],y[ind]) 9 | 10 | """ 11 | def tvhist2d(a,b,*args,**kw): 12 | ind = kw.get('ind') 13 | 14 | if ind is None: 15 | return idlplot.tvhist2d(a,b,*args,**kw) 16 | else: 17 | weights = kw.get('weights') 18 | if weights is not None: 19 | kw['weights']=kw['weights'][ind] 20 | del kw['ind'] 21 | return idlplot.tvhist2d(a[ind],b[ind],*args,**kw) 22 | 23 | def plothist(a,*args,**kw): 24 | ind = kw.get('ind') 25 | 26 | if ind is None: 27 | ret=idlplot.plothist(a,*args,**kw) 28 | else: 29 | weights = kw.get('weights') 30 | if weights is not None: 31 | if not np.isscalar(kw['weights']): 32 | kw['weights']=kw['weights'][ind] 33 | del kw['ind'] 34 | ret=idlplot.plothist(a[ind],*args,**kw) 35 | return ret 36 | 37 | def plot(a, b=None, **kw): 38 | ind = kw.get('ind') 39 | 40 | if ind is None: 41 | idlplot.plot(a,b,**kw) 42 | else: 43 | del kw['ind'] 44 | if b is not None: 45 | idlplot.plot(a[ind], b[ind], **kw) 46 | else: 47 | idlplot.plot(a[ind], None, **kw) 48 | 49 | 50 | def oplot(a, b=None, **kw): 51 | ind = kw.get('ind') 52 | 53 | if ind is None: 54 | idlplot.oplot(a, b, **kw) 55 | else: 56 | del kw['ind'] 57 | if b is not None: 58 | idlplot.oplot(a[ind], b[ind], **kw) 59 | else: 60 | idlplot.oplot(a[ind], **kw) 61 | 62 | 63 | def ploterror(a,b,c,*args,**kw): 64 | ind = kw.get('ind') 65 | 66 | if ind is None: 67 | idlplot.ploterror(a,b,c,*args,**kw) 68 | else: 69 | del kw['ind'] 70 | l = len(args) 71 | args1=[None]*l 72 | for i in range(l): 73 | args1[i]=args[i][ind] 74 | idlplot.ploterror(a[ind],b[ind],c[ind],*args1,**kw) 75 | 76 | tvhist2d.__doc__ = idlplot.tvhist2d.__doc__ 77 | plot.__doc__ = idlplot.plot.__doc__ 78 | oplot.__doc__ = idlplot.oplot.__doc__ 79 | ploterror.__doc__ = idlplot.ploterror.__doc__ 80 | plothist.__doc__ = idlplot.plothist.__doc__ 81 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/match_lists.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009-2010 Sergey Koposov 2 | # This file is part of astrolibpy 3 | # 4 | # astrolibpy is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # astrolibpy is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with astrolibpy. If not, see . 16 | 17 | 18 | import scipy.spatial.kdtree, numpy 19 | from scipy import version 20 | from numpy import sin,cos,deg2rad,rad2deg,arcsin 21 | 22 | scipy_version = ('.'.join(version.version.split('.')[0:2])).split('.')[0:2] 23 | 24 | def match_lists(ra1, dec1, ra2, dec2, dist, numNei=1): 25 | """crossmatches the list of objects (ra1,dec1) with 26 | another list of objects (ra2,dec2) with the matching radius "dist" 27 | The routines searches for up to numNei closest neighbors 28 | the routine returns the distance to the neighbor and the list 29 | of indices of the neighbor. Everything is in degrees. 30 | if no match is found the distance is NaN. 31 | Example: 32 | > dist, ind = match_lists(ra1,dec1,ra2,dec2, 1./3600) 33 | > goodmatch_ind = numpy.isfinite(dist) 34 | > plot(ra1[goodmatch_ind],ra2[ind[goodmatch_ind]]) 35 | Another example: 36 | > print match_lists( [1,1], [2,3], [1.1,1.2,6,], [2.1,2.2,10], 0.3,numNei=2) 37 | (array([[ 0.1413761 , 0.28274768], 38 | [ inf, inf]]), 39 | array([[0, 1], 40 | [3, 3]])) 41 | """ 42 | cosd = lambda x : cos(deg2rad(x)) 43 | sind = lambda x : sin(deg2rad(x)) 44 | mindist = 2 * sind(dist/2.) 45 | getxyz = lambda r, d: [cosd(r)*cosd(d), sind(r)*cosd(d), sind(d)] 46 | xyz1 = numpy.array(getxyz(ra1, dec1)) 47 | xyz2 = numpy.array(getxyz(ra2, dec2)) 48 | 49 | if (int(scipy_version[0])==0) and (int(scipy_version[1])<8): 50 | # If old scipy version is detected then we use KDTree instead of 51 | # cKDTtree because there is a bug in the cKDTree 52 | # http://projects.scipy.org/scipy/ticket/1178 53 | tree2 = scipy.spatial.KDTree(xyz2.T) 54 | else: 55 | tree2 = scipy.spatial.cKDTree(xyz2.T) 56 | del xyz2 57 | ret = tree2.query(xyz1.T, numNei, 0, 2, mindist) 58 | del xyz1 59 | dist, ind = ret 60 | finite = numpy.isfinite(dist) 61 | dist[finite] = rad2deg(2*arcsin(dist[finite]/2)) 62 | return dist, ind 63 | 64 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/my_healpy.py: -------------------------------------------------------------------------------- 1 | import healpy,numpy as np 2 | 3 | def radec2pix(nside,ra,dec,nest=True): 4 | _,__=np.pi/2-np.deg2rad(dec),np.deg2rad(ra) 5 | return healpy.ang2pix(nside,_,__,nest=nest) 6 | 7 | def pix2radec(nside,pix,nest=True): 8 | __,_ = healpy.pix2ang(nside,pix,nest=nest) 9 | ra,dec=np.rad2deg(_),np.rad2deg(np.pi/2-__) 10 | return ra,dec 11 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/pg2hdf5.py: -------------------------------------------------------------------------------- 1 | import glob, tables, numpy, Queue,psycopg2,threading,sys 2 | 3 | def getConnection( db=None, driver=None, user=None, 4 | password=None, host=None): 5 | conn_str = "dbname=%s host=%s"%(db,host) 6 | if user is not None: 7 | conn_str = conn_str+ ' user=%s'%user 8 | if password is not None: 9 | conn_str = conn_str+ ' password=%s'%password 10 | conn = psycopg2.connect(conn_str) 11 | return conn 12 | 13 | def getCursor(conn, driver=None, preamb=None, notNamed=False): 14 | cur = conn.cursor() 15 | if preamb is not None: 16 | cur.execute(preamb) 17 | else: 18 | cur.execute('set cursor_tuple_fraction TO 1') 19 | # this is required because otherwise PG may decide to execute a different plan 20 | if notNamed: 21 | return cur 22 | cur = conn.cursor(name='sqlutilcursor') 23 | cur.arraysize=100000 24 | return cur 25 | 26 | def __inserter(desc, filename, qIn, endEvent): 27 | h5 = tables.openFile(filename, 'w') 28 | i=0; 29 | while(not endEvent.is_set()): 30 | try: 31 | tups = qIn.get(True,0.1) 32 | i+=1; 33 | #names='ra,dec,rmag,flag,psfr,aa,kk,airm,sky,camcol,typ'.split(',') 34 | #qOut.put(numpy.core.records.array(tups)) 35 | names = [ x.name for x in desc] 36 | if i==1: 37 | desc= {} 38 | desc1=[] 39 | for j,x, in enumerate(tups[0]): 40 | #desc[names[j]]=j.dtype 41 | desc1.append((names[j],type(x))) 42 | desc1=numpy.dtype(desc1) 43 | tab=h5.createTable('/','Table',desc1) 44 | tab.append(tups) 45 | except Queue.Empty: 46 | continue 47 | 48 | tab.close() 49 | h5.close() 50 | 51 | def get(query, filename, params=None, db="wsdb", driver="psycopg2", user=None, 52 | password=None, host='localhost', preamb=None, 53 | getConn=False, conn=None, maskNull=False): 54 | '''This program executes the sql query and saves the results 55 | in the HDF5 file (without storing all the results in memory) 56 | pg2hdf5.get('select ra,dec,d25 from rc3','rc3.h5') 57 | ''' 58 | connSupplied = (conn is not None) 59 | if not connSupplied: 60 | conn = getConnection(db=db,driver=driver,user=user,password=password, 61 | host=host) 62 | try: 63 | cur = getCursor(conn, driver=driver, preamb=preamb) 64 | 65 | if params is None: 66 | res = cur.execute(query) 67 | else: 68 | res = cur.execute(query, params) 69 | 70 | qIn = Queue.Queue(1) 71 | endEvent = threading.Event() 72 | nrec = 0 73 | reslist = [] 74 | try: 75 | while(True): 76 | tups = cur.fetchmany() 77 | if nrec==0: 78 | desc = cur.description 79 | proc = threading.Thread(target=__inserter, args = (desc, filename, qIn, endEvent)) 80 | proc.start() 81 | 82 | if tups == []: 83 | break 84 | 85 | qIn.put(tups) 86 | nrec+=1 87 | endEvent.set() 88 | except BaseException: 89 | endEvent.set() 90 | proc.join(0.2) # notice that here the timeout is larger than the timeout 91 | # in the converter process 92 | if proc.is_alive(): 93 | proc.terminate() 94 | raise 95 | proc.join() 96 | if reslist == []: 97 | nCols = len(cur.description) 98 | res = numpy.array([], 99 | dtype=numpy.dtype([('a%d'%i,'f') for i in range(nCols)]) 100 | ) 101 | else: 102 | res = numpy.concatenate(reslist) 103 | res=[res[tmp] for tmp in res.dtype.names] 104 | if maskNull: 105 | for i in range(len(res)): 106 | if res[i].dtype==numpy.object: 107 | res[i]=res[i].astype(numpy.float) 108 | 109 | except BaseException: 110 | try: 111 | conn.rollback() 112 | except Exception: 113 | pass 114 | if not connSupplied: 115 | try: 116 | conn.close() # do not close if we were given the connection 117 | except: 118 | pass 119 | raise 120 | 121 | cur.close() 122 | conn.rollback() 123 | 124 | if not getConn: 125 | if not connSupplied: 126 | conn.close() # do not close if we were given the connection 127 | return res 128 | else: 129 | return conn,res 130 | 131 | 132 | if __name__=='__main__': 133 | get(sys.argv[1],sys.argv[2],host='cappc118') -------------------------------------------------------------------------------- /astrolibpy/my_utils/quick_hist.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009-2010 Sergey Koposov 2 | # This file is part of astrolibpy 3 | # 4 | # astrolibpy is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # astrolibpy is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with astrolibpy. If not, see . 16 | 17 | 18 | import numpy as np 19 | import scipy.weave, scipy.weave.converters 20 | 21 | def quick_hist(arrs, range=None, nbins=None, weights=None, getPos=False): 22 | """ 23 | N-dimensional histogram routine. 24 | Example: 25 | > xs=np.random.uniform(size=100); ys= np.random.uniform(size=100) 26 | > hh = quick_hist((xs,ys), range=[(0,1),(0,1)], nbins=[20,10]) 27 | Arguments: 28 | arr -- tuple of N-arrays 29 | range -- list of tuples of ranges 30 | nbins -- list of numbers of bins 31 | Keywords: 32 | weights -- weighting for the histogram 33 | getPos -- return the 1D vector of the positions within the histogram 34 | (-1 if the point is outside the range) 35 | """ 36 | from __builtin__ import range as xrange 37 | nd = len(arrs) 38 | if range is None: 39 | range=[] 40 | for i in xrange(nd): 41 | range.append((arrs[0].min(),arrs[0].max())) 42 | if nbins is None: 43 | nbins = [10]*nd 44 | 45 | if len(nbins)!=nd: 46 | raise ValueError('The array of nbins MUST have the same length as the number of input data vectors') 47 | if len(range)!=nd: 48 | raise ValueError('The array of ranges MUST have the same length as the number of input data vectors') 49 | 50 | nx = len(arrs[0]) 51 | for curarr in arrs: 52 | if len(curarr)!=nx: 53 | raise ValueError('All the input arrays MUST have the same length!') 54 | if weights is not None: 55 | if len(weights)!=nx: 56 | raise ValueError('The weights array MUST have the same length as the input arrays') 57 | # convert all the bins into integers 58 | nbins = [ int(_tmp) for _tmp in nbins] 59 | 60 | poss = np.zeros((nx,), dtype=np.int64) 61 | 62 | ind = np.ones_like(arrs[0]).astype(bool) 63 | nbins_rev = nbins + [] 64 | nbins_rev.reverse() 65 | mults = (reduce(lambda x, y: x + [y * x[-1]], nbins_rev, [1]))[:-1] 66 | mults.reverse() 67 | for i in xrange(nd): 68 | cur_arr = np.ascontiguousarray(arrs[i],dtype=np.float64) 69 | cur_range0 = float(range[i][0]) 70 | cur_range1 = float(range[i][1]) 71 | cur_nbins = nbins[i] 72 | cur_mult = mults[i] 73 | code1 = """ 74 | int i, cur_pos; 75 | double curfac = cur_nbins * 1./ (cur_range1-cur_range0); 76 | for (i=0; i=0 ) && (cur_pos= 0) & ( cur_pos < cur_nbins)) 98 | poss += cur_pos * cur_mult 99 | 100 | poss = poss[ind] 101 | newlen = len(poss) 102 | if weights is None: 103 | weights_str = '1' 104 | else: 105 | weightsind = weights[ind] 106 | weights_str = 'weightsind(i)' 107 | if not getPos: 108 | del ind 109 | res = np.zeros(np.array(nbins, dtype=np.int64).prod()) 110 | 111 | code = """ 112 | int i; 113 | for (i=0; i> resolve.resolve('M31') 10 | (10.684708329999999, 41.268749999999997) 11 | 12 | Requires the following modules: 13 | suds, lxml 14 | """ 15 | url = 'http://cdsws.u-strasbg.fr/axis/services/Sesame?wsdl' 16 | client = Client(url) 17 | xml=client.service.SesameXML(objectName) 18 | tree = etree.fromstring(xml.encode('utf-8')) 19 | # take the first resolver 20 | pathRa = tree.xpath('/Sesame/Target/Resolver[1]/jradeg') 21 | pathDec = tree.xpath('/Sesame/Target/Resolver[1]/jdedeg') 22 | if len(pathRa)==0: 23 | return [] 24 | ra=float(pathRa[0].text) 25 | dec=float(pathDec[0].text) 26 | return ra,dec 27 | 28 | 29 | if __name__=='__main__': 30 | res = resolve(sys.argv[1]) 31 | if len(res)>0: 32 | print res[0],res[1] 33 | else: 34 | print 'Not found' 35 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/sphere_rotate.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | from cv_coord import cv_coord 3 | 4 | 5 | def sphere_rotate(ra, dec, rapol, decpol, ra0, revert=False): 6 | """ rotate ra,dec to a new spherical coordinate system where the pole is 7 | at rapol,decpol and the zeropoint is at ra=ra0 8 | revert flag allows to reverse the transformation 9 | """ 10 | x,y,z=cv_coord(ra,dec,dec*0+1,fr='sph',to='rect',degr=True) 11 | 12 | tmppol=cv_coord(rapol,decpol,1,degr=True,fr='sph',to='rect') #pole axis 13 | tmpvec1=cv_coord(ra0,0,1,degr=True,fr='sph',to='rect') #x axis 14 | tmpvec1=numpy.array(tmpvec1) 15 | 16 | tmpvec1[2]=(-tmppol[0]*tmpvec1[0]-tmppol[1]*tmpvec1[1])/tmppol[2] 17 | tmpvec1/=numpy.sqrt((tmpvec1**2).sum()) 18 | tmpvec2=numpy.cross(tmppol,tmpvec1) # y axis 19 | 20 | if not revert: 21 | xnew=x*tmpvec1[0]+y*tmpvec1[1]+z*tmpvec1[2] 22 | ynew=x*tmpvec2[0]+y*tmpvec2[1]+z*tmpvec2[2] 23 | znew=x*tmppol[0] +y*tmppol[1] +z*tmppol[2] 24 | else: 25 | xnew=x*tmpvec1[0]+y*tmpvec2[0]+z*tmppol[0] 26 | ynew=x*tmpvec1[1]+y*tmpvec2[1]+z*tmppol[1] 27 | znew=x*tmpvec1[2]+y*tmpvec2[2]+z*tmppol[2] 28 | del x,y,z 29 | tmp=cv_coord(xnew,ynew,znew,to='sph',fr='rect',degr=True) 30 | return (tmp[0],tmp[1]) 31 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/staralt.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2014 Sergey Koposov koposov@ast.cam.ac.uk 3 | # 4 | # This file is part of astrolibpy 5 | # 6 | # astrolibpy is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # astrolibpy is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with astrolibpy. If not, see . 17 | 18 | import ephem,numpy as np 19 | 20 | import sphdist 21 | 22 | def getalt(ra, dec, yr, mon, day, hr, minu, sec=0, lon='-111:35:59', lat='31:57:12', 23 | elev=2000, retSun=False, retDistMoon=False): 24 | """computes the altitude in degrees of a given object at the given utc time 25 | ra dec in degress 26 | yr mon day hr minu in utc 27 | lon lat are in degrees and lon is positive to the East 28 | retSun means that you'll get height of the sun above the horizon 29 | retDistMoon means that you'll also get the distance to the moon 30 | """ 31 | 32 | obs = ephem.Observer(); 33 | obs.lon = lon # longitude 34 | obs.lat = lat #latitude 35 | obs.elevation=elev; 36 | fb = ephem.FixedBody(); 37 | fb._ra=np.deg2rad(ra); 38 | fb._dec=np.deg2rad(dec) 39 | obs.date=ephem.Date('%d/%02d/%d %d:%d:0'%(yr,mon,day,hr,minu)) 40 | fb.compute(obs); 41 | alt = np.rad2deg(1*fb.alt) 42 | az = np.rad2deg(1*fb.az); 43 | if retSun: 44 | fbSun = ephem.Sun(); 45 | fbSun.compute(obs); 46 | altSun = np.rad2deg(1*(fbSun.alt)) 47 | if retDistMoon: 48 | fbMoon = ephem.Moon(); 49 | fbMoon.compute(obs); 50 | altMoon = np.rad2deg(1*fbMoon.alt) 51 | azMoon = np.rad2deg(1*fbMoon.az); 52 | distMoon = sphdist.sphdist(az,alt,azMoon,altMoon) 53 | if retSun or retDistMoon: 54 | ret = [alt] 55 | else: 56 | ret = alt 57 | if retSun: 58 | ret.append(altSun) 59 | if retDistMoon: 60 | ret.append(distMoon) 61 | return ret 62 | 63 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/wav2RGB.py: -------------------------------------------------------------------------------- 1 | """ 2 | code taken from here: 3 | http://codingmess.blogspot.com/2009/05/conversion-of-wavelength-in-nanometers.html 4 | """ 5 | 6 | def wav2RGB(wavelength): 7 | """ 8 | convert wavelength in angstrems to matplotlib color string 9 | """ 10 | w = int(wavelength/10.) # convert to integer nanometers 11 | 12 | # colour 13 | if w >= 380 and w < 440: 14 | R = -(w - 440.) / (440. - 350.) 15 | G = 0.0 16 | B = 1.0 17 | elif w >= 440 and w < 490: 18 | R = 0.0 19 | G = (w - 440.) / (490. - 440.) 20 | B = 1.0 21 | elif w >= 490 and w < 510: 22 | R = 0.0 23 | G = 1.0 24 | B = -(w - 510.) / (510. - 490.) 25 | elif w >= 510 and w < 580: 26 | R = (w - 510.) / (580. - 510.) 27 | G = 1.0 28 | B = 0.0 29 | elif w >= 580 and w < 645: 30 | R = 1.0 31 | G = -(w - 645.) / (645. - 580.) 32 | B = 0.0 33 | elif w >= 645 and w <= 780: 34 | R = 1.0 35 | G = 0.0 36 | B = 0.0 37 | else: 38 | R = 0.0 39 | G = 0.0 40 | B = 0.0 41 | 42 | # intensity correction 43 | if w >= 380 and w < 420: 44 | SSS = 0.3 + 0.7*(w - 350) / (420 - 350) 45 | elif w >= 420 and w <= 700: 46 | SSS = 1.0 47 | elif w > 700 and w <= 780: 48 | SSS = 0.3 + 0.7*(780 - w) / (780 - 700) 49 | else: 50 | SSS = 0.0 51 | SSS *= 255 52 | 53 | val = (int(SSS*R), int(SSS*G), int(SSS*B)) 54 | strval = "#%02x%02x%02x"%(val) 55 | return strval 56 | 57 | -------------------------------------------------------------------------------- /astrolibpy/my_utils/window_func.py: -------------------------------------------------------------------------------- 1 | import scipy,numpy,numpy as np 2 | 3 | def window_func(x, y, func, xmin=None, xmax=None, nbin=100, empty=False, 4 | xlog=False): 5 | """This function does compute a user-supplied function 6 | on the subsets of y grouped by values of x 7 | E.g. imagine that you have x from 0 to 100 and you want 8 | to know the mean values of y for 00 34 | if len(cury)>0 or empty: 35 | retv[i]=func(cury) 36 | retx = xmin+(xmax-xmin)*1./nbin*(0.5+numpy.arange(nbin)) 37 | if xlog: 38 | retx = 10**retx 39 | if empty: 40 | mask |= True 41 | return retx[mask], retv[mask],hh[mask] 42 | 43 | 44 | def window_func2d(x,y,z,func,xmin=None,xmax=None,ymin=None,ymax=None,nbins=[10,10]): 45 | """This function does compute a user-supplied function 46 | on the subsets of y grouped by values of x 47 | E.g. imagine that you have x from 0 to 100 and you want 48 | to know the mean values of y for 0= 0) & (yinds >= 0) & (xinds < nbins[0]) & (yinds < nbins[1]) 74 | xinds, yinds, z1 = [_[subind] for _ in [xinds,yinds,z]] 75 | valind = yinds * (nbins[0]) + xinds 76 | sortind = np.argsort(valind) 77 | valind = valind[sortind] 78 | poss = np.where(np.diff(valind) > 0)[0] 79 | z1 = z1[sortind] 80 | for i in range(len(poss) + 1): 81 | if i == 0: 82 | left = 0 83 | right = poss[0] + 1 84 | elif i == len(poss): 85 | left = poss[-1] + 1 86 | right = len(valind) 87 | else: 88 | left = poss[i-1] + 1 89 | right = poss[i] + 1 90 | curval = valind[left] 91 | retv[curval % (nbins[0]), curval / (nbins[0])] = func(z1[left:right]) 92 | return retv, locx[0], locx[-1], locy[0], locy[-1], hh -------------------------------------------------------------------------------- /astrolibpy/plotting/healmap.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import numpy,healpy,matplotlib.collections,matplotlib.patches 3 | import sqlutil,quick_hist 4 | import matplotlib.pyplot as plt 5 | from idlplot import plot,oplot 6 | 7 | import healpy 8 | import scipy as sc 9 | 10 | # Joe Bovy's code of his extensions of the healpy module 11 | # Copyright Joe Bovy 12 | class bovy_healpy: 13 | @staticmethod 14 | def pix2vert(nside,ipix,nest=False): 15 | """ 16 | NAME: 17 | pix2vert 18 | PURPOSE: 19 | calculate the locations of the vertices (theta,phi) 20 | of a given HEALPix pixel 21 | INPUT: 22 | nside - HEALPix resolution parameter 23 | ipix - pixel number 24 | nest - if True, use NESTED scheme (default: RING) 25 | OUTPUT: 26 | numpy.array([4,2]) theta,phi [rad] NWSE 27 | HISTORY: 28 | 2010-01-21 - Written - Bovy (NYU) 29 | """ 30 | (centerTheta,centerPhi)= healpy.pix2ang(nside,ipix,nest=nest) 31 | #Are we in the polar regime or in the equatorial regime? 32 | z= sc.cos(centerTheta) 33 | if z > -2./3. and z < 2./3.: 34 | return bovy_healpy._ang2vert_eq(nside,centerTheta,centerPhi,z) 35 | else: 36 | return bovy_healpy._ang2vert(nside,centerTheta,centerPhi,z) 37 | @staticmethod 38 | def _ang2vert_eq(nside,theta,phi,z): 39 | a= 4./3./nside 40 | b= 8./3./sc.pi 41 | deltaZ= a 42 | deltaPhi= a/b 43 | out= [] 44 | out.append([sc.arccos(z+deltaZ/2),phi]) 45 | out.append([theta,phi-deltaPhi/2.]) 46 | out.append([sc.arccos(z-deltaZ/2),phi]) 47 | out.append([theta,phi+deltaPhi/2.]) 48 | return sc.array(out) 49 | @staticmethod 50 | def _ang2vert(nside,theta,phi,z): 51 | (xsCenter,ysCenter)= bovy_healpy._ang2xsys(z,phi) 52 | delta= sc.pi/4./nside 53 | out= [] 54 | out.append(bovy_healpy._xsys2ang(xsCenter,ysCenter+delta)) 55 | out.append(bovy_healpy._xsys2ang(xsCenter-delta,ysCenter)) 56 | out.append(bovy_healpy._xsys2ang(xsCenter,ysCenter-delta)) 57 | out.append(bovy_healpy._xsys2ang(xsCenter+delta,ysCenter)) 58 | return sc.array(out) 59 | @staticmethod 60 | def _xsys2ang(xs,ys): 61 | if sc.fabs(ys) <= sc.pi/4.: 62 | return [sc.arccos(8./3./sc.pi*ys),xs] 63 | else: 64 | xt= (xs % (sc.pi/2.)) 65 | fabsys= sc.fabs(ys) 66 | theta= sc.arccos((1.-1./3.*(2.-4.*fabsys/sc.pi)**2.)*ys/fabsys) 67 | if fabsys == sc.pi/2.: 68 | phi= xs-fabsys+sc.pi/4. 69 | else: 70 | phi= xs-(fabsys-sc.pi/4.)/(fabsys-sc.pi/2.)*(xt-sc.pi/4.) 71 | return [theta % (sc.pi+0.0000000001),phi % (2.*sc.pi)] #Hack 72 | @staticmethod 73 | def _ang2xsys(z,phi): 74 | if sc.fabs(z) <= 2./3.: 75 | return [phi,3.*sc.pi/8.*z] 76 | else: 77 | phit= (phi % (sc.pi/2.)) 78 | sigz= bovy_healpy._sigma(z) 79 | return [phi-(sc.fabs(sigz)-1.)*(phit-sc.pi/4.),sc.pi/4.*sigz] 80 | 81 | @staticmethod 82 | def _sigma(z): 83 | if z < 0.: 84 | return -(2.-sc.sqrt((3.*(1+z)))) 85 | 86 | else: 87 | return 2.-sc.sqrt((3.*(1-z))) 88 | 89 | def healmap(ras, decs, ramin=0, ramax=360, decmin=-90, decmax=90, nside=64, 90 | xflip=False, vmax=None, vmin=None, cmap=plt.cm.gray_r, 91 | linewidth=1, weights=None, wrap_angle=None, skip_empty=True, 92 | weight_norm=False, rasterized=True): 93 | """ 94 | Make the 2D histogram of the datapoints on the sky using the Healpix 95 | pixels 96 | """ 97 | 98 | plt.ioff() 99 | 100 | nel = 12 * nside**2 101 | ipix = healpy.ang2pix(nside, numpy.deg2rad(decs)+numpy.pi*0.5, numpy.deg2rad(ras)) 102 | hh = quick_hist.quick_hist((ipix,), nbins=[nel], 103 | range=[[0, nel]],weights=weights) 104 | hhsum = quick_hist.quick_hist((ipix,), nbins=[nel], 105 | range=[[0, nel]]) 106 | 107 | pixarea = (4*numpy.pi*(180/numpy.pi)**2)/nel # in sq. deg 108 | hh = hh / pixarea 109 | hhsum = hhsum / pixarea 110 | 111 | xloc = numpy.arange(nel) 112 | verts = [bovy_healpy.pix2vert(nside, xx) for xx in xloc] 113 | 114 | collist = [] 115 | fac = 180/numpy.pi 116 | if wrap_angle is None: 117 | wrap_angle = 2 * numpy.pi 118 | else: 119 | wrap_angle = numpy.deg2rad(wrap_angle) 120 | for ii, v in enumerate(verts): 121 | if skip_empty and hhsum[ii] == 0: 122 | continue 123 | 124 | b,a = v.T 125 | if (a.max()-a.min())>(numpy.pi): 126 | a = ((a + (numpy.pi - a[0])) % (2 * numpy.pi)) - (numpy.pi-a[0]) 127 | if a.mean() < 0: 128 | a = (a + 2 * numpy.pi) 129 | if a.mean() > wrap_angle: 130 | a -= 2 * numpy.pi 131 | xys = numpy.array([a * fac, b * fac - 90]).T 132 | 133 | curpoly = matplotlib.patches.Polygon(xys, closed=True) 134 | collist.append(curpoly) 135 | 136 | raranges = [ramin,ramax] 137 | if xflip: 138 | raranges = raranges[::-1] 139 | plot([-1], xr=raranges, yr=[decmin,decmax]) 140 | ax = plt.gca() 141 | tmppol = matplotlib.patches.Polygon(numpy.array(([0, 360, 360, 0], 142 | [-90, -90, 90, 90])).T,closed=True) 143 | tmpcoll = matplotlib.collections.PatchCollection([tmppol], linewidths=0.01, 144 | edgecolors='black', facecolors='black') 145 | tmpcoll.set_array(numpy.array([0])) 146 | 147 | 148 | coll = matplotlib.collections.PatchCollection(collist, 149 | linewidths=linewidth) 150 | 151 | if vmin is None: 152 | vmin = 0 153 | if vmax is None: 154 | vmax = hh.max() 155 | 156 | if weight_norm: 157 | hh = hh * 1. / (hhsum + 1 * (hhsum == 0)) 158 | 159 | if skip_empty: 160 | hh1 = hh[hhsum > 0] 161 | else: 162 | hh1 = hh 163 | 164 | coll.set_array(hh1) 165 | coll.set_cmap(cmap) 166 | coll.set_clim(vmin, vmax) 167 | coll.set_edgecolors(coll.cmap(coll.norm(hh1))) 168 | coll.set_rasterized(rasterized) 169 | tmpcoll.set_clim(vmin, vmax) 170 | 171 | ax.add_collection(coll) 172 | return coll 173 | 174 | 175 | if __name__=='__main__': 176 | ras,decs=sqlutil.get('select radeg,dedeg from sdss_phot_qso.main', 177 | host='cappc118') 178 | doit(ras,decs, ramin=0,ramax=360,decmin=-90, decmax=90, nside=64) 179 | 180 | plt.savefig('xx.png',dpi=200) 181 | -------------------------------------------------------------------------------- /astrolibpy/plotting/lasso_plot.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2010 Sergey Koposov 2 | # This file is part of astrolibpy 3 | # 4 | # astrolibpy is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # astrolibpy is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with astrolibpy. If not, see . 16 | 17 | from matplotlib.widgets import Lasso 18 | import matplotlib.path as mplpa 19 | from matplotlib.pyplot import gca 20 | from numpy import nonzero,array 21 | 22 | class lasso_plot: 23 | """ The class is designed to select the datapoints by drawing the region 24 | around it. 25 | Example: 26 | plot(xs, ys, ps=3 ) # first plot the data 27 | las = lasso_plot.lasso_plot(xs,ys) 28 | Now click on the plot and do not release the mouse button till 29 | you draw your region 30 | After that the variable las.ind will contain the indices of those 31 | points inside the region and las.verts will contain the vertices of the 32 | polygon you've just drawn """ 33 | def __init__(self, xs, ys): 34 | self.axes = gca() 35 | self.canvas = self.axes.figure.canvas 36 | self.xys = array([xs,ys]).T#[d for d in zip(xs,ys)] 37 | fig = self.axes.figure 38 | self.cid = self.canvas.mpl_connect('button_press_event', self.onpress) 39 | self.ind = None 40 | self.mask = None 41 | self.verts = None 42 | 43 | def callback(self, verts): 44 | self.path = mplpa.Path(verts) 45 | mask = self.path.contains_points(self.xys) 46 | ind = nonzero(mask)[0] 47 | self.canvas.draw_idle() 48 | self.canvas.widgetlock.release(self.lasso) 49 | self.canvas.mpl_disconnect(self.cid) 50 | del self.lasso 51 | del self.xys 52 | self.verts = verts 53 | self.ind = ind 54 | self.mask = mask 55 | 56 | def inside(self, xs,ys): 57 | tmpxys = zip(xs,ys) 58 | return self.path.contains_points(tmpxys) 59 | 60 | def onpress(self, event): 61 | if self.canvas.widgetlock.locked(): return 62 | if event.inaxes is None: return 63 | self.lasso = Lasso(event.inaxes, (event.xdata, event.ydata), self.callback) 64 | # acquire a lock on the widget drawing 65 | self.canvas.widgetlock(self.lasso) 66 | 67 | -------------------------------------------------------------------------------- /astrolibpy/utils/derivator.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Nov 12 14:00:40 2013 4 | 5 | @author: koposov 6 | """ 7 | import numpy as np 8 | import workerpool 9 | 10 | class derivator: 11 | # class which compute finite difference derivatives using multiple cpus 12 | def __init__(self, func, eps1=1e-4, eps2=1e-8, nthreads=1 ,kw={}): 13 | self.func=func 14 | if nthreads > 1: 15 | self.pool = workerpool.pool(func, nthreads=nthreads, kw=kw) 16 | self.eps1 = eps1 17 | self.eps2 = eps2 18 | 19 | def __call__(self,p0): 20 | p = np.asarray(p0) 21 | eps = np.maximum(np.abs(p * self.eps1), self.eps2) 22 | if self.pool is not None: 23 | self.pool.apply_async(0, p) 24 | for i in range(len(p)): 25 | curp = p.copy() 26 | curp[i] += eps[i] 27 | self.pool.apply_async(i + 1, curp) 28 | res0 = self.pool.get(0) 29 | res = np.zeros(len(p)) 30 | for i in range(len(p)): 31 | res[i] = self.pool.get(i+1) 32 | else: 33 | res0 = self.func(p) 34 | for i in range(len(p)): 35 | curp = p.copy() 36 | curp[i] += eps[i] 37 | res[i] = self.func(curp) 38 | ret = (res - res0) / eps 39 | return ret 40 | 41 | def __del__(self): 42 | if self.pool is not None: 43 | del self.pool 44 | del self.func 45 | 46 | -------------------------------------------------------------------------------- /astrolibpy/utils/idlsave.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2009-2010 Sergey Koposov 3 | # This file is part of astrolibpy 4 | # 5 | # astrolibpy is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # astrolibpy is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with astrolibpy. If not, see . 17 | 18 | 19 | """ 20 | To work with idlsave you need first to do 21 | from idlsave import idlsave 22 | 23 | Then you can save and restore the variables 24 | 25 | idlsave.save('xx.sav','x,y',2,3) 26 | exec (idlsave.restore('xx.sav')) 27 | 28 | """ 29 | 30 | try: 31 | import cPickle as pickle 32 | except ImportError: 33 | import pickle 34 | import types 35 | import re,struct 36 | 37 | class idlsave: 38 | """ The class devoted for saving and restoring the python variable in 39 | files 40 | """ 41 | dhash={} 42 | def __init(self): 43 | pass 44 | 45 | @staticmethod 46 | def versionId(version): 47 | return 'IDLSAVE-v%04d'%version 48 | 49 | @staticmethod 50 | def parseVersion(s): 51 | m=re.match('IDLSAVE-v(\d\d\d\d)',s) 52 | if m is None: 53 | return 1 54 | else: 55 | return int(m.group(1)) 56 | 57 | 58 | @staticmethod 59 | def __cureString(s): 60 | return s.replace('\n','').replace(' ','').replace('\t','') 61 | 62 | @staticmethod 63 | def __splitString(s): 64 | return idlsave.__cureString(s).split(',') 65 | 66 | @staticmethod 67 | def save(filename=None, names=None, *args, **kw): 68 | """ 69 | Saves your variable in a file, in such a way that you can easily 70 | retrieve them later. 71 | Example: 72 | > x=2 73 | > y=[3,4,5] 74 | > idlsave.save("mydat.psav",'x,y',x,y) 75 | or 76 | > exec(idlsave.save("myday.psav",'x,y')) 77 | Now you can leave python. You can later restore the x,y, variables 78 | using idlsave.restore (see there for the doc) 79 | """ 80 | if len(args)==0: 81 | return "idlsave.save(\"%s\",\"%s\",%s)"%(filename,idlsave.__cureString(names),names) 82 | 83 | if type(names)==types.StringType: 84 | names = idlsave.__splitString(names) 85 | if len(names)!=len(args): 86 | raise Exception("The number of variable names should \ 87 | be equal to the number of variables)") 88 | f=open(filename,"w") 89 | version = kw.get('version',2) 90 | curhash={} 91 | for a in range(len(names)): 92 | curhash[names[a]]=args[a] 93 | 94 | if version==1: 95 | pickle.dump(curhash, f, 2) 96 | elif version==2: 97 | f.write(idlsave.versionId(version)) 98 | headlen1 = f.tell() 99 | f.write(struct.pack('!q',long(0))) 100 | offsets = dict([(name,long(0)) for name in names]) 101 | for name in names: 102 | offsets[name] = long(f.tell()) 103 | pickle.dump(curhash[name], f, 2) 104 | offOffs = f.tell() 105 | pickle.dump(offsets, f, 2) 106 | f.seek(headlen1) 107 | f.write(struct.pack('!q',long(offOffs))) 108 | f.close() 109 | del curhash 110 | return None 111 | 112 | @staticmethod 113 | def restore(filename=None, names=None, asdict=False, version=None, printVars=False): 114 | """Restores the variables stored in a file by idlsave.save routine 115 | Example: 116 | > exec(idlsave.restore("mydat.psav")) 117 | > ra,dec = idlsave.restore("mysav.psav","ra,dec") 118 | Note that you MUST use this exact form exec(idlsave.restore(...)) 119 | """ 120 | f=open(filename,"r") 121 | vid = idlsave.versionId(1) 122 | prefix=f.read(len(vid)) 123 | if version is None: 124 | version=idlsave.parseVersion(prefix) 125 | if version==1: 126 | f.seek(0) # there is no header in the version1 files 127 | 128 | if version==1: 129 | xx=pickle.load(f) 130 | idlsave.dhash=xx 131 | f.close() 132 | if names is None: 133 | if asdict: 134 | ret=idlsave.dhash.copy() 135 | del idlsave.dhash 136 | return ret 137 | buf=",".join(idlsave.dhash.iterkeys()) 138 | if len(idlsave.dhash)==1: 139 | buf=buf+',' 140 | buf=buf+"=idlsave.getallvars(printVars=%s)"%(str(printVars)) 141 | return buf 142 | else: 143 | names = idlsave.__splitString(names) 144 | res=[idlsave.dhash[a] for a in names] 145 | del idlsave.dhash 146 | return res 147 | elif version==2: 148 | offOff = struct.unpack('!q',f.read(8))[0] 149 | f.seek(offOff) 150 | offsets = pickle.load(f) 151 | if names is None: 152 | names1 = offsets.keys() 153 | else: 154 | names1 = idlsave.__splitString(names) 155 | hash = {} 156 | for name in names1: 157 | off = offsets[name] 158 | f.seek(off) 159 | hash[name] = pickle.load(f) 160 | if asdict: 161 | return hash 162 | idlsave.dhash=hash 163 | f.close() 164 | if names is None: 165 | buf=",".join(idlsave.dhash.iterkeys()) 166 | if len(idlsave.dhash)==1: 167 | buf=buf+',' 168 | buf=buf+"=idlsave.getallvars(%s)"%str(printVars) 169 | return buf # return the string for exec 170 | else: 171 | res=[idlsave.dhash[a] for a in names1] 172 | del idlsave.dhash 173 | return res 174 | 175 | @staticmethod 176 | def getallvars(printVars=False): 177 | tup=tuple(a for a in idlsave.dhash.itervalues()) 178 | if printVars: 179 | print (','.join([k for k in idlsave.dhash.keys()])) 180 | del idlsave.dhash 181 | return tup 182 | -------------------------------------------------------------------------------- /astrolibpy/utils/sqlutil.py: -------------------------------------------------------------------------------- 1 | 2 | # This file is part of astrolibpy 3 | # 4 | # astrolibpy is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # astrolibpy is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with astrolibpy. If not, see . 16 | 17 | 18 | import types 19 | import numpy, sys, numpy as np 20 | import time,psycopg2 21 | import threading 22 | try: 23 | import queue 24 | except: 25 | import Queue as queue 26 | import collections, warnings 27 | try: 28 | dictclass = collections.OrderedDict 29 | except AttributeError: 30 | try: 31 | import ordereddict 32 | dictclass = ordereddict.OrderedDict 33 | except ImportError: 34 | warnings.warn('No ordered dict library found') 35 | dictclass = dict 36 | 37 | def getConnection( db=None, driver=None, user=None, 38 | password=None, host=None,port=5432, timeout=None): 39 | if driver=='psycopg2': 40 | import psycopg2 41 | conn_str = "dbname=%s host=%s port=%d"%(db,host,port) 42 | if user is not None: 43 | conn_str = conn_str+ ' user=%s'%user 44 | if password is not None: 45 | conn_str = conn_str+ ' password=%s'%password 46 | conn = psycopg2.connect(conn_str) 47 | elif driver=='sqlite3': 48 | import sqlite3 49 | if timeout is None: 50 | timeout = 5 51 | conn = sqlite3.connect(db, timeout=timeout) 52 | else: 53 | raise Exception("Unknown driver") 54 | return conn 55 | 56 | def getCursor(conn, driver=None, preamb=None, notNamed=False): 57 | if driver=='psycopg2': 58 | cur = conn.cursor() 59 | if preamb is not None: 60 | cur.execute(preamb) 61 | else: 62 | cur.execute('set cursor_tuple_fraction TO 1') 63 | # this is required because otherwise PG may decide to execute a different plan 64 | if notNamed: 65 | return cur 66 | cur = conn.cursor(name='sqlutilcursor') 67 | cur.arraysize=100000 68 | elif driver=='sqlite3': 69 | cur = conn.cursor() 70 | return cur 71 | 72 | def __converter(qIn, qOut, endEvent, dtype): 73 | while(not endEvent.is_set()): 74 | try: 75 | tups = qIn.get(True,0.1) 76 | except queue.Empty: 77 | continue 78 | try: 79 | res=numpy.core.records.array(tups,dtype=dtype) 80 | except: 81 | print ('Failed to convert input data into array') 82 | endEvent.set() 83 | raise 84 | qOut.put(res) 85 | 86 | 87 | def get(query, params=None, db="wsdb", driver="psycopg2", user=None, 88 | password=None, host='localhost', preamb=None, 89 | getConn=False, conn=None, maskNull=False, port=5432, 90 | strLength=10, timeout=None, notNamed=False, asDict=False): 91 | '''This program executes the sql query and returns 92 | the tuple of the numpy arrays. 93 | Example: 94 | a,b, c = sqlutil.get('select ra,dec,d25 from rc3') 95 | You can also use the parameters in your query: 96 | Example: 97 | a,b = squlil.get('select ra,dec from rc3 where name=?',"NGC 3166") 98 | ''' 99 | __pgTypeHash = {16:bool,18:str,20:'i8',21:'i2',23:'i4',25:'|S%d'%strLength,700:'f4',701:'f8', 100 | 1042:'|S%d'%strLength,#character() 101 | 1043:'|S%d'%strLength,#varchar 102 | 1700:'f8' #numeric 103 | } 104 | 105 | connSupplied = (conn is not None) 106 | if not connSupplied: 107 | conn = getConnection(db=db, driver=driver, user=user, password=password, 108 | host=host, port=port, timeout=timeout) 109 | try: 110 | cur = getCursor(conn, driver=driver, preamb=preamb,notNamed=notNamed) 111 | 112 | if params is None: 113 | res = cur.execute(query) 114 | else: 115 | res = cur.execute(query, params) 116 | 117 | qIn = queue.Queue(1) 118 | qOut = queue.Queue() 119 | endEvent = threading.Event() 120 | nrec = 0 ## keeps the number of arrays sent to the other thread 121 | ## minus number received 122 | reslist=[] 123 | proc = None 124 | colNames = [] 125 | if driver=='psycopg2': 126 | try: 127 | while(True): 128 | tups = cur.fetchmany() 129 | if nrec==0: 130 | desc = cur.description 131 | typeCodes = [_tmp.type_code for _tmp in desc] 132 | colNames = [_tmp.name for _tmp in cur.description] 133 | dtype=numpy.dtype([('a%d'%_i,__pgTypeHash[_t]) for _i,_t in enumerate(typeCodes)]) 134 | proc = threading.Thread(target=__converter, args = (qIn, qOut, endEvent,dtype)) 135 | proc.start() 136 | 137 | if tups == []: 138 | break 139 | qIn.put(tups) 140 | nrec += 1 141 | try: 142 | reslist.append(qOut.get(False)) 143 | nrec -= 1 144 | except queue.Empty: 145 | pass 146 | try: 147 | while(nrec!=0): 148 | try: 149 | reslist.append(qOut.get(True, 0.1)) 150 | nrec-=1 151 | except: 152 | if endEvent.is_set(): 153 | raise Exception('Child thread failed') 154 | except queue.Empty: 155 | pass 156 | endEvent.set() 157 | except BaseException: 158 | endEvent.set() 159 | if proc is not None: 160 | proc.join(0.2) # notice that here the timeout is larger than the timeout 161 | # in the converter process 162 | if proc.is_alive(): 163 | proc.terminate() 164 | raise 165 | proc.join() 166 | if reslist == []: 167 | nCols = len(desc) 168 | res = numpy.array([], 169 | dtype=numpy.dtype([('a%d'%i,'f') for i in range(nCols)]) 170 | ) 171 | else: 172 | res = numpy.concatenate(reslist) 173 | 174 | elif driver=='sqlite3': 175 | tups=cur.fetchall() 176 | if len(tups)>0: 177 | _cast = {types.BooleanType: numpy.bool, 178 | types.IntType: numpy.int32, 179 | types.LongType: numpy.int64, 180 | types.FloatType: numpy.float64, 181 | types.StringType: numpy.str, 182 | types.UnicodeType: numpy.str} 183 | try: 184 | typelist=[_cast[type(tmp)] for tmp in tups[0]] 185 | except KeyError: 186 | raise Exception("Unknown datatype") 187 | res = numpy.core.records.array(tups) 188 | else: 189 | return None 190 | 191 | res=[res[tmp] for tmp in res.dtype.names] 192 | if maskNull: 193 | for i in range(len(res)): 194 | if res[i].dtype==numpy.object: 195 | res[i]=res[i].astype(numpy.float) 196 | 197 | except BaseException: 198 | try: 199 | conn.rollback() 200 | except: 201 | pass 202 | if not connSupplied: 203 | try: 204 | conn.close() # do not close if we were given the connection 205 | except: 206 | pass 207 | raise 208 | 209 | cur.close() 210 | #conn.commit() 211 | if asDict: 212 | resDict = dictclass() 213 | for _n, _v in zip(colNames,res): 214 | resDict[_n]=_v 215 | res=resDict 216 | if not getConn: 217 | if not connSupplied: 218 | 219 | conn.close() # do not close if we were given the connection 220 | return res 221 | else: 222 | return conn,res 223 | 224 | def execute(query, params=None, db="wsdb", driver="psycopg2", user=None, 225 | password=None, host='locahost', 226 | conn=None, preamb=None, timeout=None, 227 | noCommit=False): 228 | "Execute a given SQL command without returning the results" 229 | connSupplied = (conn is not None) 230 | if not connSupplied: 231 | conn = getConnection(db=db,driver=driver,user=user,password=password, 232 | host=host, timeout=timeout) 233 | try: 234 | cur = getCursor(conn, driver=driver, preamb=preamb, notNamed=True) 235 | 236 | cur.execute(query, params) 237 | except BaseException: 238 | try: 239 | conn.rollback() 240 | except Exception: 241 | pass 242 | if not connSupplied: 243 | try: 244 | conn.close() # do not close if we were given the connection 245 | except: 246 | pass 247 | raise 248 | cur.close() 249 | if not noCommit: 250 | conn.commit() 251 | if not connSupplied: 252 | conn.close() # do not close if we were given the connection 253 | 254 | 255 | def create_schema(tableName, arrays, names, temp=False): 256 | hash = dict([ 257 | (np.int32,'integer'), 258 | (np.int64,'bigint'), 259 | (np.float32,'real'), 260 | (np.float64,'double precision'), 261 | (np.string_,'varchar'), 262 | ]) 263 | if temp: 264 | temp='temporary' 265 | else: 266 | temp='' 267 | outp = 'create %s table %s '%(temp,tableName) 268 | outp1 = [] 269 | for arr,name in zip(arrays,names): 270 | outp1.append(name+' '+hash[arr.dtype.type]) 271 | return outp + '(' + ','.join(outp1)+')' 272 | 273 | def print_arrays(arrays, f): 274 | hash = dict([ 275 | (np.int32,'%d'), 276 | (np.int64,'%d'), 277 | (np.float32,'%.18e'), 278 | (np.float64,'%.18e'), 279 | (np.string_,'%s') 280 | ]) 281 | fmt = [ hash[x.dtype.type] for x in arrays] 282 | recarr = np.rec.fromarrays(arrays) 283 | np.savetxt(f, recarr, fmt=fmt) 284 | 285 | def upload(tableName, arrays, names, db="wsdb", driver="psycopg2", user=None, 286 | password=None, host='locahost', 287 | conn=None, preamb=None, timeout=None, 288 | noCommit=False, temp=False, 289 | analyze=False, createTable=True): 290 | """ Upload the data stored in the tuple of arrays in the DB 291 | x=np.arange(10) 292 | y=x**.5 293 | sqlutil.upload('mytable',(x,y),('xcol','ycol')) 294 | """ 295 | 296 | connSupplied = (conn is not None) 297 | if not connSupplied: 298 | conn = getConnection(db=db,driver=driver,user=user,password=password, 299 | host=host, timeout=timeout) 300 | try: 301 | cur = getCursor(conn, driver=driver, preamb=preamb, notNamed=True) 302 | if createTable: 303 | query1 = create_schema(tableName, arrays, names, temp=temp) 304 | cur.execute(query1) 305 | import cStringIO 306 | f = cStringIO.StringIO() 307 | print_arrays(arrays, f) 308 | f.seek(0) 309 | cur.copy_from(f,tableName,sep=' ',columns=names) 310 | except BaseException: 311 | try: 312 | conn.rollback() 313 | except Exception: 314 | pass 315 | if not connSupplied: 316 | try: 317 | conn.close() # do not close if we were given the connection 318 | except: 319 | pass 320 | raise 321 | if analyze: 322 | cur.execute('analyze %s'%tableName) 323 | cur.close() 324 | if not noCommit: 325 | conn.commit() 326 | if not connSupplied: 327 | conn.close() # do not close if we were given the connection 328 | 329 | 330 | 331 | def local_join(query, tableName, arrays, names, db="wsdb", driver="psycopg2", user=None, 332 | password=None, host='locahost', 333 | port=5432, 334 | conn=None, preamb=None, timeout=None, strLength=20): 335 | """ This function allows joining the data from python with the data in the DB,it 336 | first uploads the data in the DB and then run a user specified query: 337 | x=np.arange(10) 338 | y=x**.5 339 | sqlutil.local_join('select * from mytable as m, sometable as s where s.id=m.xcol', 340 | 'mytable',(x,y),('xcol','ycol')) 341 | """ 342 | 343 | connSupplied = (conn is not None) 344 | if not connSupplied: 345 | conn = getConnection(db=db,driver=driver,user=user,password=password, 346 | host=host, timeout=timeout,port=port) 347 | 348 | upload(tableName, arrays, names, conn=conn, noCommit=True, temp=True, 349 | analyze=True) 350 | res = get(query, conn=conn, preamb=preamb, strLength=strLength) 351 | conn.rollback() 352 | 353 | if not connSupplied: 354 | conn.close() 355 | return res 356 | 357 | -------------------------------------------------------------------------------- /astrolibpy/utils/workerpool.py: -------------------------------------------------------------------------------- 1 | import multiprocessing as mp, numpy as np 2 | import signal 3 | 4 | def doit(func, queuein, queout, kw): 5 | signal.signal(signal.SIGINT, signal.SIG_IGN) 6 | while True: 7 | inp = queuein.get() 8 | if inp is None: 9 | break 10 | i, p = inp 11 | res = func(p, **kw) 12 | queout.put((i, res)) 13 | 14 | class pool: 15 | def __init__(self, func, kw={}, nthreads=1): 16 | self.nthreads = nthreads 17 | self.queuein = mp.Queue() 18 | self.queueout = mp.Queue() 19 | self.procs = [mp.Process(target=doit, name='xpool_%d'%i, 20 | args=(func, self.queuein, self.queueout, kw)) 21 | for i in range(nthreads) ] 22 | [_.start() for _ in self.procs] 23 | self.stage = {} 24 | 25 | def apply_async(self, i, p): 26 | self.queuein.put((i, p)) 27 | 28 | def get(self, i): 29 | if i in self.stage: 30 | ret = self.stage[i] 31 | del self.stage[i] 32 | return ret 33 | else: 34 | while True: 35 | iret, ret = self.queueout.get() 36 | if iret != i: 37 | self.stage[iret] = ret 38 | else: 39 | return ret 40 | 41 | def get_any(self): 42 | if len(self.stage) != 0: 43 | i, ret = self.stage.popitem() 44 | return i, ret 45 | else: 46 | iret, ret = self.queueout.get() 47 | return iret, ret 48 | 49 | def map(self, params): 50 | for i, p in enumerate(params): 51 | self.queuein.put((i, p)) 52 | ret = [None] * len(params) 53 | for i in range(len(params)): 54 | resi, res = self.queueout.get() 55 | ret[resi] = res 56 | return ret 57 | 58 | def __del__(self): 59 | self.join() 60 | 61 | def join(self): 62 | for p in self.procs: 63 | self.queuein.put(None) 64 | for p in self.procs: 65 | p.join() 66 | del self.queuein 67 | del self.queueout 68 | --------------------------------------------------------------------------------