├── .gitignore ├── mpfit ├── tests │ ├── __init__.py │ └── test_mpfit.py └── mpfitexpr.py ├── my_utils ├── astropy2pg.py ├── get_dss.py ├── resolve.py ├── from_hex.py ├── wav2RGB.py ├── table_joiner.py ├── sphere_rotate.py ├── rotate_pm.py ├── staralt.py ├── match_lists.py ├── clicker.py ├── pg2hdf5.py ├── crossmatcher.py ├── quick_hist.py ├── adabinner.py ├── sky_plotter.py └── correct_pm.py ├── astrolib ├── sphdist.py ├── cv_coord.py ├── precess_xyz.py ├── readcol.py ├── daycnv.py ├── premat.py ├── zang.py ├── convolve.py ├── cosmo_param.py ├── helio_jd.py ├── helcorr.py ├── galage.py ├── euler.py ├── lumdist.py ├── precess.py ├── gal_uvw.py ├── xyz.py ├── bprecess.py └── baryvel.py ├── README.md ├── utils ├── workerpool.py └── idlsave.py ├── plotting ├── idlplotInd.py └── lasso_plot.py └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | __pycache__ -------------------------------------------------------------------------------- /mpfit/tests/__init__.py: -------------------------------------------------------------------------------- 1 | def test(level=1, verbosity=1): 2 | from numpy.testing import Tester 3 | return Tester().test(level, verbosity) 4 | -------------------------------------------------------------------------------- /my_utils/astropy2pg.py: -------------------------------------------------------------------------------- 1 | import astropy.table as atpy2 2 | import pandas 3 | import sqlalchemy 4 | import sqlutil 5 | 6 | def printSchema(fname, host, db, user='koposov', tabname='newtab'): 7 | # print the sql schema for a given table 8 | tab = atpy2.Table().read(fname) 9 | df = tab.filled().to_pandas() 10 | conn = sqlutil.getConnection(driver='psycopg2', host=host, db=db, user=user) 11 | AL = sqlalchemy.create_engine('postgresql://', creator=(lambda : conn)) 12 | print (pandas.io.sql.get_schema(df,tabname, con=AL)) 13 | 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /my_utils/get_dss.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import aplpy 3 | try: 4 | from urllib2 import urlopen 5 | except: 6 | from urllib.request import urlopen 7 | try: 8 | from StringIO import StringIO 9 | except: 10 | from io import BytesIO as StringIO 11 | 12 | import astropy.io.fits as pyfits 13 | import matplotlib.pyplot as plt 14 | def get_dss(ra, dec, survey='all', radius = 15, debug=False, noerase=False, 15 | subplot=(1,1,1)): 16 | """ 17 | radius is in arcmins 18 | survey: 'all', 'poss2ukstu', 'dss1'.. 19 | """ 20 | #survey='poss2ukstu' 21 | 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) 22 | 23 | response = urlopen(url) 24 | if debug: 25 | print( url) 26 | html = response.read() 27 | f=StringIO(html) 28 | 29 | dat=pyfits.open(f) 30 | if not noerase: 31 | plt.clf() 32 | gc=aplpy.FITSFigure(dat,figure=plt.gcf(),subplot=subplot) 33 | gc.set_tick_labels_format('ddd.dddd','ddd.dddd') 34 | gc.show_grayscale() 35 | return gc 36 | 37 | -------------------------------------------------------------------------------- /my_utils/resolve.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from urllib.parse import quote_plus 3 | from lxml import etree 4 | from urllib.request import urlopen 5 | 6 | 7 | def resolve(objectName): 8 | """ R 9 | esolve the object by name using CDS 10 | Example: 11 | >> resolve.resolve('M31') 12 | (10.684708329999999, 41.268749999999997) 13 | 14 | Requires the following modules: 15 | suds, lxml 16 | """ 17 | url = 'http://vizier.u-strasbg.fr/cgi-bin/Sesame/-ox/?%s' % quote_plus( 18 | objectName) 19 | f = urlopen(url) 20 | result = f.read() 21 | tree = etree.fromstring(result) 22 | # take the first resolver 23 | success = True 24 | for i in range(4): 25 | pathRa = tree.xpath('/Sesame/Target/Resolver[%d]/jradeg' % i) 26 | pathDec = tree.xpath('/Sesame/Target/Resolver[%d]/jdedeg' % i) 27 | if len(pathRa) != 0: 28 | success = True 29 | break 30 | if not success: 31 | return [] 32 | ra = float(pathRa[0].text) 33 | dec = float(pathDec[0].text) 34 | return ra, dec 35 | 36 | 37 | if __name__ == '__main__': 38 | res = resolve(sys.argv[1]) 39 | if len(res) > 0: 40 | print(res[0], res[1]) 41 | else: 42 | print('Not found') 43 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # astrolibpy 2 | ### This library contains a few astronomy routines that I wrote or I converted from IDL. 3 | 4 | Author (2008-2024) Sergey Koposov 5 | 6 | Email: skoposov __AT__ ed __DOT__ ac __DOT__ uk 7 | 8 | The tools that may be still interesting to other users are 9 | * idlsave.py -- module for saving/restoring sets of variables in a file 10 | * lasso_plot -- module for selecting points on the plot by drawing a contour 11 | * clicker -- module for extracting one or many clicks coordinates from matplotlib plot 12 | * crossmatcher -- module for x-matching a table by position with a table inside a Postgresql database 13 | * match_lists -- x-match lists by position on the sky using kd-tree 14 | * mpfit -- Levenberg-Marquardt fitter (converted from IDL) 15 | * idlplot/idlplotInd -- set of wrappers for plotting in IDL style where your command has all 16 | the options you want. Also allows efficient histogramming, subsetting etc 17 | 18 | 19 | 20 | The astrolib part of this repo is probably obsolete given the existance of astropy 21 | 22 | ### Installation 23 | This package does not need to be installed in the sense of 'pip install', so if you 24 | want to you use it, you can either copy a script that you want and add it to the current 25 | directory or clone the repo and add the path to PYTHONPATH 26 | 27 | ### License 28 | The licensing for the programs I wrote myself is GPL3. For all other 29 | programs (mainly converted from IDL) I guess the license is either BSD or 30 | they are in public domain. 31 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /utils/workerpool.py: -------------------------------------------------------------------------------- 1 | import multiprocessing as mp, numpy as np 2 | import signal 3 | 4 | def doit(func, queuein, queout, args, 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, *args, **kw) 12 | queout.put((i, res)) 13 | 14 | class pool: 15 | def __init__(self, func, args=None, kw={}, nthreads=1, mpmap=False): 16 | # create a processing pool that has the function that needs to 17 | # be called stored 18 | # that reduces the overhead of constantly pickling the function 19 | # mpmap=True flag enables the multiprocessing.Pool.map syntax of map 20 | # e.g. map(func, list) 21 | # the first argument is then ignored 22 | self.nthreads = nthreads 23 | self.queuein = mp.Queue() 24 | self.queueout = mp.Queue() 25 | self.mpmap = mpmap 26 | self.procs = [mp.Process(target=doit, name='xpool_%d'%i, 27 | args=(func, self.queuein, self.queueout, args, kw)) 28 | for i in range(nthreads) ] 29 | [_.start() for _ in self.procs] 30 | self.stage = {} 31 | 32 | def apply_async(self, i, p): 33 | self.queuein.put((i, p)) 34 | 35 | def get(self, i): 36 | if i in self.stage: 37 | ret = self.stage[i] 38 | del self.stage[i] 39 | return ret 40 | else: 41 | while True: 42 | iret, ret = self.queueout.get() 43 | if iret != i: 44 | self.stage[iret] = ret 45 | else: 46 | return ret 47 | 48 | def get_any(self): 49 | if len(self.stage) != 0: 50 | i, ret = self.stage.popitem() 51 | return i, ret 52 | else: 53 | iret, ret = self.queueout.get() 54 | return iret, ret 55 | 56 | def map(self, *args): 57 | if self.mpmap: 58 | params=args[1] 59 | else: 60 | params=args[0] 61 | for i, p in enumerate(params): 62 | self.queuein.put((i, p)) 63 | ret = [None] * len(params) 64 | for i in range(len(params)): 65 | resi, res = self.queueout.get() 66 | ret[resi] = res 67 | return ret 68 | 69 | def __del__(self): 70 | self.join() 71 | 72 | def join(self): 73 | for p in self.procs: 74 | self.queuein.put(None) 75 | for p in self.procs: 76 | p.join() 77 | del self.queuein 78 | del self.queueout 79 | -------------------------------------------------------------------------------- /my_utils/table_joiner.py: -------------------------------------------------------------------------------- 1 | import duckdb 2 | import numpy as np 3 | import pandas as pd 4 | import astropy.table as atpy 5 | 6 | 7 | def order_convert(x): 8 | if x.dtype.byteorder not in ('=', '|'): 9 | x = x.byteswap() 10 | return x.view(x.data.dtype.newbyteorder()) 11 | return x 12 | 13 | 14 | def check_type(arr): 15 | # Check if the array is of string type 16 | if np.issubdtype(arr.dtype, np.str_): 17 | return 'string' 18 | if np.issubdtype(arr.dtype, np.bytes_): 19 | return 'string' 20 | elif np.issubdtype(arr.dtype, np.integer): 21 | return 'int' 22 | if np.issubdtype(arr.dtype, np.floating): 23 | return 'float' 24 | raise RuntimeError('unsupported type') 25 | 26 | 27 | def join_to_left(T1, 28 | T2, 29 | key=None, 30 | fill_value=None, 31 | key_left=None, 32 | key_right=None, 33 | fill_values={ 34 | 'string': '', 35 | 'int': -9999, 36 | 'float': np.nan 37 | }): 38 | """ 39 | Return Table 2 matched to T1 using key. 40 | 41 | In the case of multiple matches a random one is returned 42 | 43 | """ 44 | rowid1 = np.arange(len(T1)) 45 | assert ((key is not None) 46 | or (key_left is not None and key_right is not None)) 47 | if key is not None: 48 | key_left, key_right = key, key 49 | 50 | df1 = pd.DataFrame({'rowid': rowid1, 'key': order_convert(T1[key_left])}) 51 | rowid2 = np.arange(len(T2)) 52 | df2 = pd.DataFrame({'rowid': rowid2, 'key': order_convert(T2[key_right])}) 53 | duckdb.register("tab1", df1) 54 | duckdb.register("tab2", df2) 55 | R = duckdb.sql(''' 56 | select distinct on (tab1.rowid) tab1.rowid as r1, tab2.rowid as r2 57 | from tab1, tab2 where 58 | tab1.key = tab2.key order by tab1.rowid''').fetchnumpy() 59 | xrowid1, xrowid2 = R['r1'], R['r2'] 60 | ret = {} 61 | n1 = len(T1) 62 | for k in T2.columns: 63 | cura = T2[k][xrowid2] 64 | cur_fill = fill_values[check_type(cura)] 65 | cur_ret = np.zeros(n1, dtype=cura.dtype) 66 | cur_ret[:] = cur_fill 67 | cur_ret[xrowid1] = cura 68 | ret[k] = cur_ret 69 | return atpy.Table(ret) 70 | -------------------------------------------------------------------------------- /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(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 | -------------------------------------------------------------------------------- /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) -------------------------------------------------------------------------------- /my_utils/sphere_rotate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import numexpr 3 | 4 | 5 | def torect(ra, dec): 6 | x = numexpr.evaluate( 7 | 'cos(ra/57.295779513082323)*cos(dec/57.295779513082323)') 8 | y = numexpr.evaluate( 9 | 'sin(ra/57.295779513082323)*cos(dec/57.295779513082323)') 10 | z = numexpr.evaluate('sin(dec/57.295779513082323)') 11 | return x, y, z 12 | 13 | 14 | def fromrect(x, y, z): 15 | ra = numexpr.evaluate('arctan2(y,x)*57.295779513082323') 16 | dec = numexpr.evaluate('57.295779513082323*arctan2(z,sqrt(x**2+y**2))') 17 | return ra, dec 18 | 19 | 20 | def rotation_matrix(rapol, decpol, ra0): 21 | """ 22 | Return the rotation matrix corresponding to the pole of rapol, decpol 23 | and with zero of new latitude corresponding to ra=ra0 24 | This matrix need to be np.dot'ed with the input vector to get 25 | forward transform 26 | """ 27 | tmppol = np.array(torect(rapol, decpol)) # pole axis 28 | tmpvec1 = np.array(torect(ra0, 0)) # x axis 29 | tmpvec1 = np.array(tmpvec1) 30 | 31 | tmpvec1[2] = (-tmppol[0] * tmpvec1[0] - tmppol[1] * tmpvec1[1]) / tmppol[2] 32 | tmpvec1 /= np.sqrt((tmpvec1**2).sum()) 33 | tmpvec2 = np.cross(tmppol, tmpvec1) # y axis 34 | M = np.array([tmpvec1, tmpvec2, tmppol]) 35 | return M 36 | 37 | 38 | def sphere_rotate(ra, 39 | dec, 40 | rapol=None, 41 | decpol=None, 42 | ra0=None, 43 | revert=False, 44 | mat=None): 45 | """ rotate ra,dec to a new spherical coordinate system where the pole is 46 | at rapol,decpol and the zeropoint is at ra=ra0 47 | revert flag allows to reverse the transformation 48 | """ 49 | 50 | x, y, z = torect(ra, dec) 51 | if rapol is not None and decpol is not None and ra0 is not None: 52 | M = rotation_matrix(rapol, decpol, ra0) 53 | else: 54 | if mat is None: 55 | raise Exception('matrix or rapol,decpol, ra0' 56 | ' need to be provided') 57 | else: 58 | M = mat 59 | 60 | if not revert: 61 | Axx, Axy, Axz = M[0] 62 | Ayx, Ayy, Ayz = M[1] 63 | Azx, Azy, Azz = M[2] 64 | else: 65 | Axx, Ayx, Azx = M[0] 66 | Axy, Ayy, Azy = M[1] 67 | Axz, Ayz, Azz = M[2] 68 | xnew = numexpr.evaluate('x*Axx+y*Axy+z*Axz') 69 | ynew = numexpr.evaluate('x*Ayx+y*Ayy+z*Ayz') 70 | znew = numexpr.evaluate('x*Azx+y*Azy+z*Azz') 71 | del x, y, z 72 | tmp = fromrect(xnew, ynew, znew) 73 | return (tmp[0], tmp[1]) 74 | -------------------------------------------------------------------------------- /my_utils/rotate_pm.py: -------------------------------------------------------------------------------- 1 | import sphere_rotate 2 | import numpy as np 3 | 4 | 5 | def cosd(x): 6 | return np.cos(np.deg2rad(x)) 7 | 8 | 9 | def sind(x): 10 | return np.sin(np.deg2rad(x)) 11 | 12 | 13 | def rotate_pm( 14 | ra, 15 | dec, 16 | pmra, 17 | pmdec, 18 | rapol=None, 19 | decpol=None, 20 | ra0=None, 21 | revert=False, 22 | mat=None, 23 | ): 24 | """ 25 | Rotate the proper motion to the sphere_rotate coord system 26 | that is specified by the pole direction and right ascencion of the (0,0) pt 27 | I assume all angles are in degrees and proper motions are in mas/yr 28 | Arguments: 29 | ra: 30 | dec: 31 | pmra: 32 | pmdec: 33 | rapol: float 34 | RA of the pole 35 | decpol float 36 | Dec of the pole 37 | ra0: float 38 | RA of the (0,0) point of the new coordinate system 39 | revert: bool 40 | if true do the inverse transformation. I.e. convert 41 | phi1,phi2,pmphii1,pmphi2 42 | to pmra,pmdec 43 | Returns: 44 | pmphi1, pmphi2 in the new coordinate system 45 | """ 46 | 47 | ra, dec, pmra, pmdec = [ 48 | np.asarray(np.atleast_1d(_)) for _ in [ra, dec, pmra, pmdec] 49 | ] 50 | if rapol is not None and decpol is not None and ra0 is not None: 51 | M = sphere_rotate.rotation_matrix(rapol, decpol, ra0) 52 | else: 53 | if mat is None: 54 | raise Exception( 55 | 'either rapol,decpol,ra0 or mat needs to be provided') 56 | M = mat 57 | if revert: 58 | M = M.T 59 | # unit vectors 60 | e_mura = np.array([-sind(ra), cosd(ra), ra * 0]) 61 | e_mudec = np.array( 62 | [-sind(dec) * cosd(ra), -sind(dec) * sind(ra), 63 | cosd(dec)]) 64 | # velocity vector in arbitrary units 65 | V = pmra * e_mura + pmdec * e_mudec 66 | del e_mura, e_mudec 67 | # apply rotation to velocity 68 | V1 = M @ V 69 | del V 70 | X = np.array([cosd(ra) * cosd(dec), sind(ra) * cosd(dec), sind(dec)]) 71 | # apply rotation to position 72 | X1 = M @ X 73 | del X 74 | # rotated coordinates in radians 75 | lon = np.arctan2(X1[1, :], X1[0, :]) 76 | lat = np.arctan2(X1[2, :], np.sqrt(X1[0, :]**2 + X1[1, :]**2)) 77 | del X1 78 | # unit vectors in rotated coordinates 79 | e_mura = np.array([-np.sin(lon), np.cos(lon), lon * 0]) 80 | e_mudec = np.array( 81 | [-np.sin(lat) * np.cos(lon), -np.sin(lat) * np.sin(lon), 82 | np.cos(lat)]) 83 | del lon, lat 84 | return np.sum(e_mura * V1, axis=0), np.sum(e_mudec * V1, axis=0) 85 | -------------------------------------------------------------------------------- /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 19 | import numpy as np 20 | import sphdist 21 | 22 | 23 | def getalt(ra, 24 | dec, 25 | yr, 26 | mon, 27 | day, 28 | hr, 29 | minu, 30 | sec=0, 31 | lon='-111:35:59', 32 | lat='31:57:12', 33 | elev=2000, 34 | retSun=False, 35 | retDistMoon=False): 36 | """ 37 | computes the altitude in degrees of a given object at the given utc time 38 | ra dec in degrees 39 | yr mon day hr minu in utc 40 | lon lat are in degrees and lon is positive to the East 41 | retSun means that you'll get height of the sun above the horizon 42 | retDistMoon means that you'll also get the distance to the moon 43 | """ 44 | 45 | obs = ephem.Observer() 46 | obs.lon = lon # longitude 47 | obs.lat = lat # latitude 48 | obs.elevation = elev 49 | fb = ephem.FixedBody() 50 | fb._ra = np.deg2rad(ra) 51 | fb._dec = np.deg2rad(dec) 52 | obs.date = ephem.Date('%d/%02d/%d %d:%d:0' % (yr, mon, day, hr, minu)) 53 | fb.compute(obs) 54 | alt = np.rad2deg(1 * fb.alt) 55 | az = np.rad2deg(1 * fb.az) 56 | if retSun: 57 | fbSun = ephem.Sun() 58 | fbSun.compute(obs) 59 | altSun = np.rad2deg(1 * (fbSun.alt)) 60 | if retDistMoon: 61 | fbMoon = ephem.Moon() 62 | fbMoon.compute(obs) 63 | altMoon = np.rad2deg(1 * fbMoon.alt) 64 | azMoon = np.rad2deg(1 * fbMoon.az) 65 | distMoon = sphdist.sphdist(az, alt, azMoon, altMoon) 66 | if retSun or retDistMoon: 67 | ret = [alt] 68 | else: 69 | ret = alt 70 | if retSun: 71 | ret.append(altSun) 72 | if retDistMoon: 73 | ret.append(distMoon) 74 | return ret 75 | -------------------------------------------------------------------------------- /mpfit/tests/test_mpfit.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from numpy.testing import * 3 | import numpy as N 4 | import copy 5 | 6 | from mpfit import mpfit 7 | 8 | 9 | def Flin(x,p): 10 | y = p[0] -p[1]*x 11 | return y 12 | 13 | 14 | def myfunctlin(p, fjac=None, x=None, y=None, err=None): 15 | # Parameter values are passed in "p" 16 | # If fjac==None then partial derivatives should not be 17 | # computed. It will always be None if MPFIT is called with default 18 | # flag. 19 | model = Flin(x, p) 20 | # Non-negative status value means MPFIT should continue, negative means 21 | # stop the calculation. 22 | status = 0 23 | return [status, (y-model)/err] 24 | 25 | 26 | 27 | def test_linfit(): 28 | x=N.array([-1.7237128E+00,1.8712276E+00,-9.6608055E-01, 29 | -2.8394297E-01,1.3416969E+00,1.3757038E+00, 30 | -1.3703436E+00,4.2581975E-02,-1.4970151E-01, 31 | 8.2065094E-01]) 32 | y=N.array([1.9000429E-01,6.5807428E+00,1.4582725E+00, 33 | 2.7270851E+00,5.5969253E+00,5.6249280E+00, 34 | 0.787615,3.2599759E+00,2.9771762E+00, 35 | 4.5936475E+00]) 36 | ey=0.07*N.ones(y.shape,dtype='float64') 37 | p0=N.array([1.0,1.0],dtype='float64') #initial conditions 38 | pactual=N.array([3.2,1.78]) #actual values used to make data 39 | parbase={'value':0., 'fixed':0, 'limited':[0,0], 'limits':[0.,0.]} 40 | parinfo=[] 41 | for i in range(len(pactual)): 42 | parinfo.append(copy.deepcopy(parbase)) 43 | for i in range(len(pactual)): 44 | parinfo[i]['value']=p0[i] 45 | fa = {'x':x, 'y':y, 'err':ey} 46 | m = mpfit(myfunctlin, p0, parinfo=parinfo,functkw=fa) 47 | if (m.status <= 0): 48 | print( 'error message = ', m.errmsg) 49 | assert N.allclose(m.params,N.array([ 3.20996572, -1.7709542 ],dtype='float64')) 50 | assert N.allclose(m.perror,N.array([ 0.02221018, 0.01893756],dtype='float64')) 51 | chisq=(myfunctlin(m.params, x=x, y=y, err=ey)[1]**2).sum() 52 | 53 | assert N.allclose(N.array([chisq],dtype='float64'),N.array([2.756284983],dtype='float64')) 54 | assert m.dof==8 55 | return 56 | 57 | def myfunctrosenbrock(p, fjac=None): 58 | # rosenbrock function 59 | res = N.array([1-p[0],-(1-p[0]),10*(p[1]-p[0]**2),-10*(p[1]-p[0]**2)]) 60 | status = 0 61 | return [status, res] 62 | 63 | 64 | 65 | def test_rosenbrock(): 66 | p0=N.array([-1,1.],dtype='float64') #initial conditions 67 | pactual=N.array([1.,1.]) #actual minimum of the rosenbrock function 68 | m = mpfit(myfunctrosenbrock, p0) 69 | if (m.status <= 0): 70 | print( 'error message = ', m.errmsg) 71 | assert m.status > 0 72 | assert N.allclose(m.params,pactual) 73 | assert N.allclose(m.fnorm,0) 74 | return 75 | 76 | if __name__ == "__main__": 77 | run_module_suite() 78 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | import scipy.spatial.kdtree 18 | import numpy as np 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 | 25 | def cosd(x): 26 | return cos(deg2rad(x)) 27 | 28 | 29 | def sind(x): 30 | return sin(deg2rad(x)) 31 | 32 | 33 | def getxyz(r, d): 34 | return [cosd(r) * cosd(d), sind(r) * cosd(d), sind(d)] 35 | 36 | 37 | def get_indices(D, ind): 38 | """ Return the indices of points that match 39 | The input is the distances and index array returned by match_lists 40 | I.e. 41 | > D,ind=match_lists.match_lists([1,1], [2,3], [1.1,1.2,6,], 42 | [2.1,2.2,10], 0.3) 43 | > print (match_lists.get_indices(D,ind)) 44 | """ 45 | x1 = np.isfinite(D) 46 | x2 = ind[x1] 47 | return np.nonzero(x1)[0], x2 48 | 49 | 50 | def match_lists(ra1, dec1, ra2, dec2, dist, numNei=1): 51 | """crossmatches the list of objects (ra1,dec1) with 52 | another list of objects (ra2,dec2) with the matching radius "dist" 53 | The routines searches for up to numNei closest neighbors 54 | the routine returns the distance to the neighbor and the list 55 | of indices of the neighbor. Everything is in degrees. 56 | if no match is found the distance is NaN. 57 | Example: 58 | > dist, ind = match_lists(ra1,dec1,ra2,dec2, 1./3600) 59 | > goodmatch_ind = numpy.isfinite(dist) 60 | > plot(ra1[goodmatch_ind],ra2[ind[goodmatch_ind]]) 61 | Another example: 62 | > print match_lists( [1,1], [2,3], [1.1,1.2,6,], 63 | [2.1,2.2,10], 0.3, numNei=2) 64 | (array([[ 0.1413761 , 0.28274768], 65 | [ inf, inf]]), 66 | array([[0, 1], 67 | [3, 3]])) 68 | """ 69 | mindist = 2 * sind(dist / 2.) 70 | xyz1 = np.array(getxyz(ra1, dec1)) 71 | xyz2 = np.array(getxyz(ra2, dec2)) 72 | 73 | if (int(scipy_version[0]) == 0) and (int(scipy_version[1]) < 8): 74 | # If old scipy version is detected then we use KDTree instead of 75 | # cKDTtree because there is a bug in the cKDTree 76 | # http://projects.scipy.org/scipy/ticket/1178 77 | tree2 = scipy.spatial.KDTree(xyz2.T) 78 | else: 79 | tree2 = scipy.spatial.cKDTree(xyz2.T) 80 | del xyz2 81 | ret = tree2.query(xyz1.T, numNei, 0, 2, mindist) 82 | del xyz1 83 | dist, ind = ret 84 | finite = np.isfinite(dist) 85 | dist[finite] = rad2deg(2 * arcsin(dist[finite] / 2)) 86 | return dist, ind 87 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plotting/idlplotInd.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterable 2 | import idlplot 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | """ 6 | This module is a set wrappers around idlplot designed to make 7 | the plots of subsets of the data: e.g. 8 | plot(x,y,ind=ind) 9 | instead of plot(x[ind],y[ind]) 10 | 11 | """ 12 | 13 | 14 | def tvhist2d(a, b, *args, **kw): 15 | ind = kw.get('ind') 16 | 17 | if ind is None: 18 | return idlplot.tvhist2d(a, b, *args, **kw) 19 | else: 20 | weights = kw.get('weights') 21 | if weights is not None: 22 | kw['weights'] = kw['weights'][ind] 23 | del kw['ind'] 24 | return idlplot.tvhist2d(a[ind], b[ind], *args, **kw) 25 | 26 | 27 | def plothist(a, *args, **kw): 28 | ind = kw.get('ind') 29 | 30 | if ind is None: 31 | ret = idlplot.plothist(a, *args, **kw) 32 | else: 33 | weights = kw.get('weights') 34 | if weights is not None: 35 | if not np.isscalar(kw['weights']): 36 | kw['weights'] = kw['weights'][ind] 37 | del kw['ind'] 38 | ret = idlplot.plothist(a[ind], *args, **kw) 39 | return ret 40 | 41 | 42 | def plot(a, b=None, **kw): 43 | ind = kw.get('ind') 44 | 45 | if ind is None: 46 | idlplot.plot(a, b, **kw) 47 | else: 48 | del kw['ind'] 49 | if b is not None: 50 | idlplot.plot(a[ind], b[ind], **kw) 51 | else: 52 | idlplot.plot(a[ind], None, **kw) 53 | 54 | 55 | def plot_scatter(a, b, s=None, c=None, **kw): 56 | ind = kw.get('ind') 57 | 58 | if ind is None: 59 | idlplot.plot_scatter(a, b, s=s, c=c, **kw) 60 | else: 61 | del kw['ind'] 62 | if c is not None and isinstance(c, Iterable): 63 | c = c[ind] 64 | if s is not None and isinstance(s, Iterable): 65 | s = s[ind] 66 | idlplot.plot_scatter(a[ind], b[ind], s=s, c=c, **kw) 67 | 68 | 69 | def oplot(a, b=None, **kw): 70 | ind = kw.get('ind') 71 | 72 | if ind is None: 73 | idlplot.oplot(a, b, **kw) 74 | else: 75 | del kw['ind'] 76 | if b is not None: 77 | idlplot.oplot(a[ind], b[ind], **kw) 78 | else: 79 | idlplot.oplot(a[ind], **kw) 80 | 81 | 82 | def errorfixer(var, ind): 83 | var = np.asarray(var) 84 | if var.ndim == 2 and var.shape[0] == 2: 85 | var1 = [var[0][ind], var[1][ind]] 86 | else: 87 | var1 = var[ind] 88 | return var1 89 | 90 | 91 | def ploterror(a, b, c, *args, **kw): 92 | ind = kw.get('ind') 93 | 94 | if ind is None: 95 | idlplot.ploterror(a, b, c, *args, **kw) 96 | else: 97 | del kw['ind'] 98 | ll = len(args) 99 | args1 = [None] * ll 100 | c1 = errorfixer(c, ind) 101 | 102 | for i in range(ll): 103 | args1[i] = errorfixer(args[i], ind) 104 | idlplot.ploterror(a[ind], b[ind], c1, *args1, **kw) 105 | 106 | 107 | def scatter(a, b, c=None, s=None, *args, **kw): 108 | ind = kw.get('ind') 109 | if ind is None: 110 | plt.scatter(a, b, c=c, s=s, *args, **kw) 111 | else: 112 | del kw['ind'] 113 | if c is not None: 114 | c = c[ind] 115 | if s is not None: 116 | s = s[ind] 117 | plt.scatter(a[ind], b[ind], c=c, s=s, *args, **kw) 118 | 119 | 120 | tvhist2d.__doc__ = idlplot.tvhist2d.__doc__ 121 | plot.__doc__ = idlplot.plot.__doc__ 122 | plot_scatter.__doc__ = idlplot.plot_scatter.__doc__ 123 | oplot.__doc__ = idlplot.oplot.__doc__ 124 | ploterror.__doc__ = idlplot.ploterror.__doc__ 125 | plothist.__doc__ = idlplot.plothist.__doc__ 126 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plotting/lasso_plot.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2010-2019 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 __future__ import print_function 18 | from matplotlib.widgets import Lasso 19 | import matplotlib.path as mplpa 20 | from matplotlib.pyplot import gca 21 | import numpy as np 22 | import quick_hist 23 | 24 | class lasso_plot: 25 | """ The class is designed to select the datapoints by drawing the region 26 | around them. 27 | Example: 28 | plt.plot(xs, ys) # first plot the data 29 | las = lasso_plot.lasso_plot(xs,ys) 30 | Now click on the plot and do not release the mouse button till 31 | you draw your region 32 | After that the variable las.mask will contain the boolean mask of the 33 | points inside the region and las.verts will contain the vertices of the 34 | polygon you've just drawn 35 | The option bins is helpful when the dataset is very large. Then 36 | the data is binned first before checking whether it is inside the 37 | contour 38 | plt.plot(xs, ys) 39 | las = lasso_plot.lasso_plot(xs,ys,bins=200) 40 | """ 41 | 42 | def __init__(self, xs, ys, bins=None): 43 | self.axes = gca() 44 | self.canvas = self.axes.figure.canvas 45 | self.xys = (np.asarray(xs), np.asarray(ys)) 46 | self.cid = self.canvas.mpl_connect('button_press_event', self.onpress) 47 | self.mask = None 48 | self.verts = None 49 | self.bins = bins 50 | 51 | def __getstate__(self): 52 | """ Custom pickle method to get rid of canvas/axes/lasso objects""" 53 | state = self.__dict__.copy() 54 | del state['canvas'] 55 | del state['axes'] 56 | del state['lasso'] 57 | return state 58 | 59 | def callback(self, verts): 60 | self.verts = np.array(verts) 61 | mask = self.inside(self.xys[0], self.xys[1], self.bins) 62 | self.mask = mask 63 | self.canvas.draw_idle() 64 | self.canvas.widgetlock.release(self.lasso) 65 | self.canvas.mpl_disconnect(self.cid) 66 | del self.xys 67 | 68 | def inside(self, xs, ys, bins=None): 69 | """ Check if points xs,ys are inside the hand-selected mask """ 70 | return inside(xs, ys, self.verts, bins) 71 | 72 | def onpress(self, event): 73 | if self.canvas.widgetlock.locked(): 74 | return 75 | if event.inaxes is None: 76 | return 77 | self.lasso = Lasso(event.inaxes, (event.xdata, event.ydata), 78 | self.callback) 79 | # acquire a lock on the widget drawing 80 | self.canvas.widgetlock(self.lasso) 81 | 82 | 83 | def inside(xs, ys, verts, bins=None): 84 | """ Check if points xs,ys are inside a mask specified by an array 85 | of vertices Nx2""" 86 | xys = np.array([xs, ys]).T 87 | 88 | path = mplpa.Path(verts) 89 | if bins is None: 90 | mask = path.contains_points(xys) 91 | ind = np.nonzero(mask)[0] 92 | else: 93 | if hasattr(bins, '__iter__'): 94 | if len(bins) == 2: 95 | nbins = bins 96 | else: 97 | raise Exception( 98 | 'The bins parameter must be a scalar or a tuple with 2 elements' 99 | ) 100 | else: 101 | nbins = [bins, bins] 102 | minx, maxx = verts[:, 0].min(), verts[:, 0].max() 103 | miny, maxy = verts[:, 1].min(), verts[:, 1].max() 104 | hh, pos = quick_hist.quick_hist(xys.T, 105 | nbins=nbins, 106 | range=[[minx, maxx], [miny, maxy]], 107 | getPos=True) 108 | xbincens = np.linspace(minx, maxx, bins + 1, True) 109 | ybincens = np.linspace(miny, maxy, bins + 1, True) 110 | 111 | xbincens = .5 * (xbincens[1:] + xbincens[:-1]) 112 | ybincens = .5 * (ybincens[1:] + ybincens[:-1]) 113 | xbincens = xbincens[:, None] + ybincens[None, :] * 0 114 | ybincens = ybincens[None, :] + xbincens[:, None] * 0 115 | xybincens = np.array([xbincens.flatten(), ybincens.flatten()]) 116 | 117 | mask = path.contains_points(xybincens.T) 118 | mask = mask[pos] & (pos >= 0) 119 | return mask 120 | -------------------------------------------------------------------------------- /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 | corr = (vdiurnal + vbar) #using baricentric velocity for correction 80 | 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#, format='(A,F20.12)' 87 | print '(jd) Julian date (middle of exposure) (JD-2400000): ', jd#, format='(A,F20.12)' 88 | print '(hjd) Heliocentric Julian date (middle of exposure) (HJD-2400000): ', hjd#, format='(A,F20.12)' 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#, format='(A,F12.9)' 99 | print '----- HELCORR.PRO - DEBUG INFO - END -----' 100 | print '' 101 | 102 | 103 | return (corr, hjd) 104 | 105 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /astrolib/euler.py: -------------------------------------------------------------------------------- 1 | from numpy import (array, sin, cos, pi, deg2rad, rad2deg, arctan2, arcsin, 2 | minimum) 3 | 4 | 5 | def euler(ai, bi, select=1, fk4=False): 6 | """ 7 | NAME: 8 | EULER 9 | PURPOSE: 10 | Transform between Galactic, celestial, and ecliptic coordinates. 11 | EXPLANATION: 12 | Use the procedure ASTRO to use this routine interactively 13 | 14 | CALLING SEQUENCE: 15 | AO, BO = EULER(AI, BI, [SELECT=1, FK4=False]) 16 | 17 | INPUTS: 18 | AI - Input Longitude in DEGREES, scalar or vector. If only two 19 | parameters are supplied, then AI and BI will be modified to 20 | contain the output longitude and latitude. 21 | BI - Input Latitude in DEGREES 22 | 23 | OPTIONAL INPUT: 24 | SELECT - Integer (1-6) specifying type of coordinate transformation. 25 | 26 | SELECT From To | SELECT From To 27 | 1 RA-Dec (2000) Galactic | 4 Ecliptic RA-Dec 28 | 2 Galactic RA-DEC | 5 Ecliptic Galactic 29 | 3 RA-Dec Ecliptic | 6 Galactic Ecliptic 30 | 31 | If not supplied as a parameter or keyword, then EULER will prompt for 32 | the value of SELECT 33 | Celestial coordinates (RA, Dec) should be given in equinox J2000 34 | unless the /FK4 keyword is set. 35 | OUTPUTS: 36 | AO - Output Longitude in DEGREES 37 | BO - Output Latitude in DEGREES 38 | 39 | INPUT KEYWORD: 40 | /FK4 - If this keyword is set and non-zero, then input and output 41 | celestial and ecliptic coordinates should be given in equinox 42 | B1950. 43 | /SELECT - The coordinate conversion integer (1-6) may 44 | alternatively be 45 | specified as a keyword 46 | NOTES: 47 | EULER was changed in December 1998 to use J2000 coordinates as the 48 | default, ** and may be incompatible with earlier versions***. 49 | REVISION HISTORY: 50 | Written W. Landsman, February 1987 51 | Adapted from Fortran by Daryl Yentis NRL 52 | Converted to IDL V5.0 W. Landsman September 1997 53 | Made J2000 the default, added /FK4 keyword W. Landsman December 1998 54 | Add option to specify SELECT as a keyword W. Landsman March 2003 55 | """ 56 | 57 | # J2000 coordinate conversions are based on the following constants 58 | # (see the Hipparcos explanatory supplement). 59 | # eps = 23.4392911111d Obliquity of the ecliptic 60 | # alphaG = 192.85948d Right Ascension of Galactic North Pole 61 | # deltaG = 27.12825d Declination of Galactic North Pole 62 | # lomega = 32.93192d Galactic longitude of celestial equator 63 | # alphaE = 180.02322d Ecliptic longitude of Galactic North Pole 64 | # deltaE = 29.811438523d Ecliptic latitude of Galactic North Pole 65 | # Eomega = 6.3839743d Galactic longitude of ecliptic equator 66 | 67 | if fk4: 68 | # equinox = '(B1950)' 69 | psi = array([ 70 | 0.57595865315e0, 4.9261918136e0, 0.00000000000e0, 0.0000000000e0, 71 | 0.11129056012e0, 4.7005372834e0 72 | ]) 73 | stheta = array([ 74 | 0.88781538514e0, -0.88781538514e0, 0.39788119938e0, 75 | -0.39788119938e0, 0.86766174755e0, -0.86766174755e0 76 | ]) 77 | ctheta = array([ 78 | 0.46019978478e0, 0.46019978478e0, 0.91743694670e0, 0.91743694670e0, 79 | 0.49715499774e0, 0.49715499774e0 80 | ]) 81 | phi = array([ 82 | 4.9261918136e0, 0.57595865315e0, 0.0000000000e0, 0.00000000000e0, 83 | 4.7005372834e0, 0.11129056012e0 84 | ]) 85 | else: 86 | # equinox = '(J2000)' 87 | psi = array([ 88 | 0.57477043300e0, 4.9368292465e0, 0.00000000000e0, 0.0000000000e0, 89 | 0.11142137093e0, 4.71279419371e0 90 | ]) 91 | stheta = array([ 92 | 0.88998808748e0, -0.88998808748e0, 0.39777715593e0, 93 | -0.39777715593e0, 0.86766622025e0, -0.86766622025e0 94 | ]) 95 | ctheta = array([ 96 | 0.45598377618e0, 0.45598377618e0, 0.91748206207e0, 0.91748206207e0, 97 | 0.49714719172e0, 0.49714719172e0 98 | ]) 99 | phi = array([ 100 | 4.9368292465e0, 0.57477043300e0, 0.0000000000e0, 0.00000000000e0, 101 | 4.71279419371e0, 0.11142137093e0 102 | ]) 103 | if select not in [1, 2, 3, 4, 5, 6]: 104 | raise ValueError( 105 | 'Select parameter should be an integer between 1 and 6') 106 | i = select - 1 107 | b = deg2rad(bi) 108 | cb = cos(b) 109 | sb = sin(b) 110 | del b 111 | a = deg2rad(ai) - phi[i] 112 | cbsa = cb * sin(a) 113 | b = -stheta[i] * cbsa + ctheta[i] * sb 114 | bo = rad2deg(arcsin(minimum(b, 1.0))) 115 | del b 116 | a = arctan2(ctheta[i] * cbsa + stheta[i] * sb, cb * cos(a)) 117 | del cb, cbsa, sb 118 | ao = rad2deg(((a + psi[i] + 4 * pi) % (2 * pi))) 119 | 120 | return (ao, bo) 121 | -------------------------------------------------------------------------------- /my_utils/clicker.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import scipy.spatial 3 | import numpy as np 4 | 5 | 6 | class MultiClicker: 7 | """ Class to collect the clicked points 8 | You initialize it as 9 | >>> cl = MultiClicker(plt.gcf()) 10 | Then all the clicks will be recorded in cl.points 11 | If you click the right-hand button, the recording will stop. 12 | You can also stop recording by calling 13 | >>> cl.stop() 14 | """ 15 | 16 | def __init__(self, fig): 17 | self.cid = None 18 | self.points = [] 19 | 20 | def onclick(event): 21 | try: 22 | print( 23 | 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % 24 | (event.button, event.x, event.y, event.xdata, event.ydata)) 25 | if event.button == 3: 26 | self.stop() 27 | else: 28 | self.points.append((event.xdata, event.ydata)) 29 | except: # noqa 30 | raise 31 | print('printing failed') 32 | 33 | self.canvas = fig.canvas 34 | self.cid = self.canvas.mpl_connect('button_press_event', onclick) 35 | 36 | def stop(self): 37 | self.canvas.mpl_disconnect(self.cid) 38 | 39 | 40 | class NearbyClicker: 41 | """ 42 | Class to call function on clicked points 43 | If you have plotted the xs,ys points 44 | and defined function 45 | def callback(i) 46 | Then when you create a clicker 47 | >>> NearbyClicker(plt.gcf(), xs, ys, callback) 48 | At each click a point that is closest to the clicked location is 49 | recorded and the callback function is called with the integer 50 | number of this point 51 | """ 52 | 53 | def __init__(self, fig, xs, ys, callback): 54 | self.cid = None 55 | self.xs = xs 56 | self.ys = ys 57 | self.tree = scipy.spatial.cKDTree(np.array([xs, ys]).T) 58 | 59 | def onclick(event): 60 | try: 61 | if event.button == 3: 62 | self.stop() 63 | else: 64 | d, pos = self.tree.query(np.r_[event.xdata, event.ydata]) 65 | print('Clicked %f %f ; Selected %d: %f %f' % 66 | (event.xdata, event.ydata, pos, self.xs[pos], 67 | self.ys[pos])) 68 | callback(pos) 69 | except: # noqa 70 | print('callback failed') 71 | 72 | self.canvas = fig.canvas 73 | self.cid = self.canvas.mpl_connect('button_press_event', onclick) 74 | 75 | def stop(self): 76 | self.canvas.mpl_disconnect(self.cid) 77 | 78 | 79 | class CallbackClicker: 80 | """ 81 | Class to call function on clicked location 82 | If you have plotted the xs,ys points 83 | and defined function 84 | def callback(x,y) 85 | Then when you create a clicker 86 | >>> NearbyClicker(plt.gcf(), callback) 87 | At each click a the callback function is called with the location 88 | """ 89 | 90 | def __init__(self, fig, callback): 91 | self.cid = None 92 | 93 | def onclick(event): 94 | try: 95 | if event.button == 3: 96 | self.stop() 97 | else: 98 | print('Clicked %f %f ' % (event.xdata, event.ydata)) 99 | callback(event.xdata, event.ydata) 100 | except: # noqa 101 | print('callback failed') 102 | 103 | self.canvas = fig.canvas 104 | self.cid = self.canvas.mpl_connect('button_press_event', onclick) 105 | 106 | def stop(self): 107 | self.canvas.mpl_disconnect(self.cid) 108 | 109 | 110 | def clicker(fig, xobj=None): 111 | """ 112 | This function records the coordinates of a single click on the figure 113 | """ 114 | cid = None 115 | 116 | def onclick(event): 117 | try: 118 | print('cid=%s button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % 119 | (cid, event.button, event.x, event.y, event.xdata, 120 | event.ydata)) 121 | if xobj is not None: 122 | if isinstance(xobj, dict): 123 | xobj['x'] = event.xdata 124 | xobj['y'] = event.ydata 125 | except: # noqa 126 | print('printing failed') 127 | event.canvas.mpl_disconnect(cid) 128 | 129 | cid = fig.canvas.mpl_connect('button_press_event', onclick) 130 | 131 | 132 | def clicker_multi(fig): 133 | """ 134 | This function records the coordinates of a multiple clicks 135 | on the figure 136 | """ 137 | cid = None 138 | res = [] 139 | 140 | def onclick(event): 141 | try: 142 | print('cid=%s button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % 143 | (cid, event.button, event.x, event.y, event.xdata, 144 | event.ydata)) 145 | if event.button == 3: 146 | event.canvas.mpl_disconnect(cid) 147 | else: 148 | res.append((event.xdata, event.ydata)) 149 | except: # noqa 150 | print('printing failed') 151 | 152 | cid = fig.canvas.mpl_connect('button_press_event', onclick) 153 | return res 154 | -------------------------------------------------------------------------------- /astrolib/lumdist.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | """ 3 | NAME: 4 | LUMDIST 5 | 6 | PURPOSE: 7 | Calculate luminosity distance (in Mpc) of an object given its redshift 8 | EXPLANATION: 9 | The luminosity distance in the Friedmann-Robertson-Walker model is 10 | taken from Caroll, Press, and Turner (1992, ARAA, 30, 499), p. 511 11 | Uses a closed form (Mattig equation) to compute the distance when the 12 | cosmological constant is zero. Otherwise integrates the function using 13 | QSIMP. 14 | CALLING SEQUENCE: 15 | result = lumdist(z, [H0 = , k = , Omega_M =, Lambda0 = , q0 = ,/SILENT]) 16 | 17 | INPUTS: 18 | z = redshift, positive scalar or vector 19 | 20 | OPTIONAL KEYWORD INPUTS: 21 | /SILENT - If set, the program will not display adopted cosmological 22 | parameters at the terminal. 23 | H0: Hubble parameter in km/s/Mpc, default is 70 24 | 25 | No more than two of the following four parameters should be 26 | specified. None of them need be specified -- the adopted defaults 27 | are given. 28 | k - curvature constant, normalized to the closure density. Default is 29 | 0, indicating a flat universe 30 | Omega_m - Matter density, normalized to the closure density, default 31 | is 0.3. Must be non-negative 32 | Lambda0 - Cosmological constant, normalized to the closure density, 33 | default is 0.7 34 | q0 - Deceleration parameter, numeric scalar = -R*(R'')/(R')^2, default 35 | is -0.55 36 | 37 | OUTPUTS: 38 | The result of the function is the luminosity distance (in Mpc) for each 39 | input value of z. 40 | 41 | EXAMPLE: 42 | (1) Plot the distance of a galaxy in Mpc as a function of redshift out 43 | to z = 5.0, assuming the default cosmology (Omega_m=0.3, Lambda = 0.7, 44 | H0 = 70 km/s/Mpc) 45 | 46 | IDL> z = findgen(50)/10. 47 | IDL> plot,z,lumdist(z),xtit='z',ytit='Distance (Mpc)' 48 | 49 | Now overplot the relation for zero cosmological constant and 50 | Omega_m=0.3 51 | IDL> oplot,z,lumdist(z,lambda=0,omega=0.3),linestyle=1 52 | COMMENTS: 53 | (1) Integrates using the IDL Astronomy Version procedure QSIMP. (The 54 | intrinsic IDL QSIMP function is not called because of its ridiculous 55 | restriction that only scalar arguments can be passed to the integrating 56 | function.) 57 | (2) Can fail to converge at high redshift for closed universes with 58 | non-zero lambda. This can presumably be fixed by replacing QSIMP with 59 | an integrator that can handle a singularity 60 | PROCEDURES CALLED: 61 | COSMO_PARAM, QSIMP 62 | REVISION HISTORY: 63 | Written W. Landsman Raytheon ITSS April 2000 64 | Avoid integer overflow for more than 32767 redshifts July 2001 65 | Use double precision J. Moustakas/W. Landsman April 2008 66 | Convert to python S. Koposov 2010 67 | """ 68 | 69 | from numpy import array, ndarray, sqrt, sin, sinh, maximum 70 | from cosmo_param import cosmo_param 71 | from scipy.integrate import quad 72 | from math import sqrt as msqrt 73 | 74 | def ldist(z, q0=None, lambda0=None): 75 | 76 | term1 = (1. + z) ** 2 77 | term2 = 1. + 2. * (q0 + lambda0) * z 78 | term3 = z * (2. + z) * lambda0 79 | denom = (term1 * term2 - term3) 80 | if denom>0: 81 | out = 1. / msqrt(denom) # since the function is used with scalar arguments 82 | # I use math.sqrt instead of numpy.sqrt for 83 | # performance reasons 84 | else: 85 | out = 0. 86 | return out 87 | 88 | 89 | def lumdist(z, h0=None, k=None, lambda0=None, omega_m=None, q0=None, silent=None): 90 | '''Syntax: result = lumdist(z, H0 = ,k=, Lambda0 = ]) 91 | Returns luminosity distance in Mpc''' 92 | 93 | scal=False 94 | scalret = lambda x : x[0] if scal else x 95 | 96 | if isinstance(z, list): 97 | z = array(z) 98 | elif isinstance(z, ndarray): 99 | pass 100 | else: 101 | scal = True 102 | z = array([z]) 103 | n = len(z) 104 | 105 | omega_m, lambda0, k, q0 = cosmo_param(omega_m, lambda0, k, q0) 106 | 107 | # Check keywords 108 | c = 2.99792458e5 # speed of light in km/s 109 | if h0 is None: 110 | h0 = 70 111 | if not silent: 112 | print( 'LUMDIST: H0:', h0, ' Omega_m:', omega_m, ' Lambda0', lambda0, ' q0: ', q0, ' k: ', k) 113 | 114 | # For the case of Lambda = 0, we use the closed form from equation 5.238 of 115 | # Astrophysical Formulae (Lang 1998). This avoids terms that almost cancel 116 | # at small q0*z better than the more familiar Mattig formula. 117 | # 118 | if lambda0 == 0: 119 | denom = sqrt(1 + 2 * q0 * z) + 1 + q0 * z 120 | dlum = (c * z / h0) * (1 + z * (1 - q0) / denom) 121 | return scalret(dlum) 122 | 123 | # For non-zero lambda 124 | else: 125 | dlum = z * 0.0 126 | for i in range(n): 127 | if z[i] <= 0.0: 128 | dlum[i] = 0.0 129 | else: 130 | lz = quad(ldist, 0, z[i], args=(q0, lambda0)) 131 | dlum[i] = lz[0] 132 | 133 | if k > 0: 134 | dlum = sinh(sqrt(k) * dlum) / sqrt(k) 135 | else: 136 | if k < 0: 137 | dlum = maximum(sin(sqrt(-k) * dlum) / sqrt(-k), 0) 138 | return scalret(c * (1 + z) * dlum / h0) 139 | 140 | -------------------------------------------------------------------------------- /astrolib/precess.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from numpy import sin, cos, deg2rad, transpose, dot, arcsin, arctan2, zeros,\ 3 | ndarray, array, rad2deg, pi 4 | from premat import premat 5 | 6 | def precess(ra0, dec0, equinox1, equinox2, doprint=False, fk4=False, radian=False): 7 | """ 8 | NAME: 9 | PRECESS 10 | PURPOSE: 11 | Precess coordinates from EQUINOX1 to EQUINOX2. 12 | EXPLANATION: 13 | For interactive display, one can use the procedure ASTRO which calls 14 | PRECESS or use the /PRINT keyword. The default (RA,DEC) system is 15 | FK5 based on epoch J2000.0 but FK4 based on B1950.0 is available via 16 | the /FK4 keyword. 17 | 18 | Use BPRECESS and JPRECESS to convert between FK4 and FK5 systems 19 | CALLING SEQUENCE: 20 | PRECESS, ra, dec, [ equinox1, equinox2, /PRINT, /FK4, /RADIAN ] 21 | 22 | INPUT - OUTPUT: 23 | RA - Input right ascension (scalar or vector) in DEGREES, unless the 24 | /RADIAN keyword is set 25 | DEC - Input declination in DEGREES (scalar or vector), unless the 26 | /RADIAN keyword is set 27 | 28 | The input RA and DEC are modified by PRECESS to give the 29 | values after precession. 30 | 31 | OPTIONAL INPUTS: 32 | EQUINOX1 - Original equinox of coordinates, numeric scalar. If 33 | omitted, then PRECESS will query for EQUINOX1 and EQUINOX2. 34 | EQUINOX2 - Equinox of precessed coordinates. 35 | 36 | OPTIONAL INPUT KEYWORDS: 37 | /PRINT - If this keyword is set and non-zero, then the precessed 38 | coordinates are displayed at the terminal. Cannot be used 39 | with the /RADIAN keyword 40 | /FK4 - If this keyword is set and non-zero, the FK4 (B1950.0) system 41 | will be used otherwise FK5 (J2000.0) will be used instead. 42 | /RADIAN - If this keyword is set and non-zero, then the input and 43 | output RA and DEC vectors are in radians rather than degrees 44 | 45 | RESTRICTIONS: 46 | Accuracy of precession decreases for declination values near 90 47 | degrees. PRECESS should not be used more than 2.5 centuries from 48 | 2000 on the FK5 system (1950.0 on the FK4 system). 49 | 50 | EXAMPLES: 51 | (1) The Pole Star has J2000.0 coordinates (2h, 31m, 46.3s, 52 | 89d 15' 50.6"); compute its coordinates at J1985.0 53 | 54 | IDL> precess, ten(2,31,46.3)*15, ten(89,15,50.6), 2000, 1985, /PRINT 55 | 56 | ====> 2h 16m 22.73s, 89d 11' 47.3" 57 | 58 | (2) Precess the B1950 coordinates of Eps Ind (RA = 21h 59m,33.053s, 59 | DEC = (-56d, 59', 33.053") to equinox B1975. 60 | 61 | IDL> ra = ten(21, 59, 33.053)*15 62 | IDL> dec = ten(-56, 59, 33.053) 63 | IDL> precess, ra, dec ,1950, 1975, /fk4 64 | 65 | PROCEDURE: 66 | Algorithm from Computational Spherical Astronomy by Taff (1983), 67 | p. 24. (FK4). FK5 constants from "Astronomical Almanac Explanatory 68 | Supplement 1992, page 104 Table 3.211.1. 69 | 70 | PROCEDURE CALLED: 71 | Function PREMAT - computes precession matrix 72 | 73 | REVISION HISTORY 74 | Written, Wayne Landsman, STI Corporation August 1986 75 | Correct negative output RA values February 1989 76 | Added /PRINT keyword W. Landsman November, 1991 77 | Provided FK5 (J2000.0) I. Freedman January 1994 78 | Precession Matrix computation now in PREMAT W. Landsman June 1994 79 | Added /RADIAN keyword W. Landsman June 1997 80 | Converted to IDL V5.0 W. Landsman September 1997 81 | Correct negative output RA values when /RADIAN used March 1999 82 | Work for arrays, not just vectors W. Landsman September 2003 83 | Convert to Python Sergey Koposov July 2010 84 | """ 85 | scal = True 86 | if isinstance(ra0, ndarray): 87 | ra = ra0.copy() 88 | dec = dec0.copy() 89 | scal = False 90 | else: 91 | ra=array([ra0]) 92 | dec=array([dec0]) 93 | npts = ra.size 94 | 95 | if not radian: 96 | ra_rad = deg2rad(ra) #Convert to double precision if not already 97 | dec_rad = deg2rad(dec) 98 | else: 99 | ra_rad = ra 100 | dec_rad = dec 101 | 102 | a = cos(dec_rad) 103 | 104 | x = zeros((npts, 3)) 105 | x[:,0] = a * cos(ra_rad) 106 | x[:,1] = a * sin(ra_rad) 107 | x[:,2] = sin(dec_rad) 108 | 109 | # Use PREMAT function to get precession matrix from Equinox1 to Equinox2 110 | 111 | r = premat(equinox1, equinox2, fk4=fk4) 112 | 113 | x2 = transpose(dot(transpose(r), transpose(x))) #rotate to get output direction cosines 114 | 115 | ra_rad = zeros(npts) + arctan2(x2[:,1], x2[:,0]) 116 | dec_rad = zeros(npts) + arcsin(x2[:,2]) 117 | 118 | if not radian: 119 | ra = rad2deg(ra_rad) 120 | ra = ra + (ra < 0.) * 360.e0 #RA between 0 and 360 degrees 121 | dec = rad2deg(dec_rad) 122 | else: 123 | ra = ra_rad 124 | dec = dec_rad 125 | ra = ra + (ra < 0.) * 2.0e0 * pi 126 | 127 | if doprint: 128 | print( 'Equinox (%.2f): %f,%f' % (equinox2, ra, dec)) 129 | if scal: 130 | ra, dec = ra[0], dec[0] 131 | return ra, dec 132 | 133 | -------------------------------------------------------------------------------- /my_utils/pg2hdf5.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import psycopg2 3 | import threading 4 | import h5py 5 | import argparse 6 | 7 | from queue import Queue, Empty 8 | 9 | strLength = 20 10 | __pgTypeHash = { 11 | 16: bool, 12 | 18: str, 13 | 20: 'i8', 14 | 21: 'i2', 15 | 23: 'i4', 16 | 1007: 'i4', 17 | 25: '|U%d', 18 | 700: 'f4', 19 | 701: 'f8', 20 | 1005: 'i2', 21 | 1007: 'i4', 22 | 1016: 'i8', 23 | 1021: 'f4', 24 | 1022: 'f8', 25 | 1042: '|U%d', # character() 26 | 1043: '|U%d', # varchar 27 | 1700: 'f8', # numeric 28 | 1114: ' 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /utils/idlsave.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2009-2022 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 | To work with idlsave you need first to do 19 | from idlsave import idlsave 20 | 21 | Then you can save and restore the variables: 22 | > x=2 23 | > y=3 24 | > idlsave.save('xx.sav','x,y', x, y) 25 | OR 26 | > exec(idlsave.save('xx.sav','x,y')) 27 | 28 | To restore you need to do: 29 | > exec (idlsave.restore('xx.sav')) 30 | 31 | """ 32 | from __future__ import print_function 33 | try: 34 | import cPickle as pickle 35 | except ImportError: 36 | import pickle 37 | import re 38 | import struct 39 | 40 | 41 | class idlsave: 42 | """ The class designed for saving and restoring python variables to 43 | files 44 | """ 45 | dhash = {} 46 | 47 | def __init(self): 48 | pass 49 | 50 | @staticmethod 51 | def versionId(version): 52 | return 'IDLSAVE-v%04d' % version 53 | 54 | @staticmethod 55 | def parseVersion(s): 56 | m = re.match(r'IDLSAVE-v(\d\d\d\d)', s) 57 | if m is None: 58 | return 1 59 | else: 60 | return int(m.group(1)) 61 | 62 | @staticmethod 63 | def __cureString(s): 64 | return s.replace('\n', '').replace(' ', '').replace('\t', '') 65 | 66 | @staticmethod 67 | def __splitString(s): 68 | return idlsave.__cureString(s).split(',') 69 | 70 | @staticmethod 71 | def save(filename=None, names=None, *args, **kw): 72 | """ 73 | Saves your variable in a file, in such a way that you can easily 74 | retrieve them later. 75 | Example: 76 | > x = 2 77 | > y = [3,4,5] 78 | > idlsave.save("mydat.psav", 'x,y', x, y) 79 | or 80 | > exec(idlsave.save("myday.psav",'x,y')) 81 | Now you can leave python. You can later restore the x,y, variables 82 | using idlsave.restore 83 | 84 | Storage format for version 2 files is the following: 85 | The first 8 bytes store the offset of the pickled dictionary of 86 | offsets. The dictionary of offsets stores the 87 | offset of each pickled variable in the file. 88 | The keys in the dictionary are variable names. 89 | """ 90 | 91 | if len(args) == 0: 92 | return "idlsave.save(\"%s\",\"%s\",%s)" % ( 93 | filename, idlsave.__cureString(names), names) 94 | 95 | if isinstance(names, str): 96 | names = idlsave.__splitString(names) 97 | if len(names) != len(args): 98 | raise Exception("The number of variable names should \ 99 | be equal to the number of variables)") 100 | f = open(filename, "wb") 101 | version = kw.get('version', 2) 102 | curhash = {} 103 | for a in range(len(names)): 104 | curhash[names[a]] = args[a] 105 | if 'protocol' not in kw: 106 | protocol = pickle.HIGHEST_PROTOCOL 107 | else: 108 | protocol = kw['protocol'] 109 | if version == 1: 110 | pickle.dump(curhash, f, protocol) 111 | elif version == 2: 112 | f.write(idlsave.versionId(version).encode('ascii')) 113 | headlen1 = f.tell() 114 | f.write(struct.pack('!q', 0)) 115 | offsets = dict([(name, 0) for name in names]) 116 | for name in names: 117 | offsets[name] = f.tell() 118 | pickle.dump(curhash[name], f, protocol) 119 | offOffs = f.tell() 120 | pickle.dump(offsets, f, protocol) 121 | f.seek(headlen1) 122 | f.write(struct.pack('!q', offOffs)) 123 | f.close() 124 | del curhash 125 | return None 126 | 127 | @staticmethod 128 | def restore(filename=None, 129 | names=None, 130 | asdict=False, 131 | version=None, 132 | printVars=False): 133 | """Restores the variables stored in a file by idlsave.save routine 134 | Example: 135 | > exec(idlsave.restore("mydat.psav")) 136 | Note that you MUST use this exact form exec(idlsave.restore(...)) 137 | Or you can restore only the variables that you want: 138 | > ra, dec = idlsave.restore("mysav.psav", "ra,dec") 139 | """ 140 | f = open(filename, "rb") 141 | vid = idlsave.versionId(1) 142 | try: 143 | prefix = f.read(len(vid)).decode('ascii') 144 | except: # noqa 145 | version = 1 146 | if version is None: 147 | version = idlsave.parseVersion(prefix) 148 | if version == 1: 149 | f.seek(0) # there is no header in the version1 files 150 | 151 | if version == 1: 152 | vard = pickle.load(f) 153 | idlsave.dhash = vard 154 | f.close() 155 | if names is None: 156 | if asdict: 157 | ret = idlsave.dhash.copy() 158 | del idlsave.dhash 159 | return ret 160 | buf = ",".join(idlsave.dhash.keys()) 161 | if len(idlsave.dhash) == 1: 162 | buf = buf + ',' 163 | buf = buf + \ 164 | "=idlsave.getallvars(printVars=%s)" % (str(printVars)) 165 | return buf 166 | else: 167 | names = idlsave.__splitString(names) 168 | res = [idlsave.dhash[a] for a in names] 169 | del idlsave.dhash 170 | return res 171 | elif version == 2: 172 | offOff = struct.unpack('!q', f.read(8))[0] 173 | f.seek(offOff) 174 | offsets = pickle.load(f) 175 | if names is None: 176 | names1 = offsets.keys() 177 | else: 178 | names1 = idlsave.__splitString(names) 179 | retd = {} 180 | for name in names1: 181 | off = offsets[name] 182 | try: 183 | f.seek(off) 184 | retd[name] = pickle.load(f) 185 | except UnicodeDecodeError: 186 | f.seek(off) 187 | retd[name] = pickle.load(f, encoding='latin1') 188 | f.close() 189 | if asdict: 190 | return retd 191 | if names is None: 192 | idlsave.dhash = retd 193 | buf = ",".join(idlsave.dhash.keys()) 194 | if len(idlsave.dhash) == 1: 195 | buf = buf + ',' 196 | buf = buf + "=idlsave.getallvars(%s)" % str(printVars) 197 | return buf # return the string for exec 198 | else: 199 | res = [retd[a] for a in names1] 200 | return res 201 | 202 | @staticmethod 203 | def getallvars(printVars=False): 204 | tup = tuple(a for a in idlsave.dhash.values()) 205 | if printVars: 206 | print(','.join([k for k in idlsave.dhash.keys()])) 207 | del idlsave.dhash 208 | return tup 209 | -------------------------------------------------------------------------------- /my_utils/crossmatcher.py: -------------------------------------------------------------------------------- 1 | import sqlutilpy 2 | import numpy as np 3 | 4 | 5 | def doit(tabname, 6 | ra, 7 | dec, 8 | colstring, 9 | radeccols=('ra', 'dec'), 10 | pmradeccols=('pmra', 'pmdec'), 11 | epoch=None, 12 | rad=1., 13 | extra=None, 14 | epochcol=None, 15 | max_pm_offset=30, 16 | yourradeccols=('ra', 'dec'), 17 | yourepochcol='epoch', 18 | tab_alias='tt', 19 | host=None, 20 | port=None, 21 | db=None, 22 | user=None, 23 | password=None, 24 | asDict=False, 25 | conn=None, 26 | preamb=None): 27 | """ 28 | Performs the nearest neighbor crossmatch within specified radius 29 | with the remote table the input is given by ra,dec columns 30 | 31 | Parameters: 32 | ----------- 33 | 34 | tabname: string 35 | The name of the table to crossmatch against 36 | ra: numpy 37 | The numpy array with right ascension 38 | dec : numpy 39 | The numpy array with declinations 40 | colstring: string 41 | The comma sepated list of columns that you want to retrieve 42 | epoch: numpy (optional) 43 | If specified we try to use proper motions for the xmatch 44 | radeccols: tuple (optional) 45 | The tuple of two strings with the name of ra,dec columns in 46 | the remote table. Default ('ra','dec') 47 | pmradeccols: tuple (optional) 48 | The tuple of proper motion columns in the table 49 | epochcol: str 50 | The name of the column with epoch 51 | rad: float (optional) 52 | The cross-match radius in arcseconds (default 1) 53 | max_pm_offset: float (optional) 54 | The maximum offset in arcsec allowed. If proper motions 55 | are used 56 | extra: string allows you to specify and additional SQL Where condition 57 | tab_alias: string 58 | The alias for the table that you are crossmatching against, so you can 59 | refer to its columns 60 | host,port,db,user,password: string 61 | The connection parameters to the DB if needed 62 | asDict: bool 63 | if True instead of returning a tuple a dictionary is returned 64 | preamb: string (optional) 65 | additional commands to be executed before the query 66 | conn: connection object(optional) 67 | An explicit connection to the DB can be provided 68 | 69 | Example: 70 | -------- 71 | > ra = np.arange(10) 72 | > dec = np.arange(10)+5 73 | > gmag,rmag= crossmatcher.doit('sdssdr9.phototag', ra,dec, 74 | 'psfmag_g,psfmag_r', rad=2.) 75 | 76 | """ 77 | racol, deccol = radeccols 78 | if extra is None: 79 | extra = 'true' 80 | your_ra, your_dec = yourradeccols 81 | preamb = '' or preamb 82 | kw = dict( 83 | preamb=('set enable_seqscan to off;' + 'set enable_mergejoin to off;' + 84 | 'set enable_hashjoin to off;' + (preamb or '')), 85 | host=host, 86 | db=db, 87 | user=user, 88 | port=(port or 5432), 89 | password=password, 90 | asDict=asDict, 91 | conn=conn) 92 | 93 | if epoch is None: 94 | RES = sqlutilpy.local_join( 95 | str.format( 96 | """ 97 | select {colstring} from 98 | ( select * from mytable order by 99 | q3c_ang2ipix({your_ra},{your_dec}) 100 | ) as m 101 | left join lateral (select * from {tabname} as s where 102 | q3c_join(m.{your_ra}, m.{your_dec}, 103 | s.{racol}, s.{deccol}, {rad}/3600.) and {extra} 104 | order by q3c_dist(m.{your_ra}, m.{your_dec}, 105 | s.{racol},s.{deccol}) asc limit 1) as {tab_alias} 106 | on true order by xid """, **locals()), 'mytable', 107 | (ra, dec, np.arange(len(ra))), (your_ra, your_dec, 'xid'), **kw) 108 | else: 109 | try: 110 | nep = len(epoch) 111 | except TypeError: 112 | epoch = np.zeros(len(ra)) + epoch 113 | else: 114 | if nep != len(ra): 115 | raise Exception( 116 | 'length of the epoch must be equal to length of positions') 117 | maxap = max_pm_offset + rad 118 | pmracol, pmdeccol = pmradeccols 119 | dist_str = f'''q3c_dist_pm( 120 | s.{racol},s.{deccol}, s.{pmracol}, s.{pmdeccol}, 1, 121 | s.{epochcol}, m.{your_ra}, m.{your_dec}, 122 | m.{yourepochcol})''' 123 | RES = sqlutilpy.local_join( 124 | str.format( 125 | """ 126 | select {colstring} from 127 | ( select * from mytable order by 128 | q3c_ang2ipix({your_ra},{your_dec}) 129 | ) as m 130 | left join lateral (select * from {tabname} as s where 131 | q3c_join(m.{your_ra}, m.{your_dec}, 132 | s.{racol}, s.{deccol}, {maxap}/3600.) and {extra} 133 | and {dist_str}<{rad}/3600. 134 | order by {dist_str} asc limit 1) as {tab_alias} 135 | on true order by xid """, 136 | **locals()), 'mytable', (ra, dec, np.arange(len(ra)), epoch), 137 | (your_ra, your_dec, 'xid', yourepochcol), **kw) 138 | 139 | return RES 140 | 141 | 142 | def doit_by_key(tabname, 143 | keys, 144 | colstring, 145 | key_col=None, 146 | extra=None, 147 | yourkeycol='id', 148 | tab_alias='tt', 149 | host=None, 150 | port=None, 151 | db=None, 152 | user=None, 153 | password=None, 154 | asDict=False, 155 | conn=None, 156 | preamb=None): 157 | """ 158 | Performs the crossmatch by id 159 | with the remote table 160 | 161 | Parameters: 162 | ----------- 163 | 164 | tabname: string 165 | The name of the table to crossmatch against 166 | keys: numpy 167 | The numpy array with ids that will be used for matching 168 | colstring: string 169 | The comma sepated list of columns that you want to retrieve 170 | key_col: string 171 | The name of the column in the table used to xmatch (i.e. 'source_id') 172 | extra: string allows you to specify and additional SQL Where condition 173 | tab_alias: string 174 | The alias for the table that you are crossmatching against, so you can 175 | refer to its columns 176 | host,port,db,user,password: string 177 | The connection parameters to the DB if needed 178 | asDict: bool 179 | if True instead of returning a tuple a dictionary is returned 180 | preamb: string (optional) 181 | additional commands to be executed before the query 182 | conn: connection object(optional) 183 | An explicit connection to the DB can be provided 184 | 185 | Example: 186 | -------- 187 | > ids = np.arange(10) 188 | > my_source_id = np.array([44,42323232]) 189 | > rv,= crossmatcher.doit_by_key( "gaia_dr3.gaia_source", 190 | > my_source_id, 'radial_velocity' key_col='source_id') 191 | """ 192 | if extra is None: 193 | extra = 'true' 194 | preamb = '' or preamb 195 | RES = sqlutilpy.local_join(str.format( 196 | """ 197 | select {colstring} 198 | from mytable as m 199 | left join lateral 200 | (select * from {tabname} as s where m.id=s.{key_col} 201 | and {extra} limit 1) as {tab_alias} 202 | on true order by m.xid """, **locals()), 203 | 'mytable', (keys, np.arange(len(keys))), 204 | ('id', 'xid'), 205 | preamb=((preamb or '')), 206 | host=host, 207 | db=db, 208 | user=user, 209 | port=(port or 5432), 210 | password=password, 211 | asDict=asDict, 212 | conn=conn) 213 | return RES 214 | -------------------------------------------------------------------------------- /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 | from __future__ import print_function 18 | from functools import reduce 19 | import warnings 20 | import numpy as np 21 | import hashlib 22 | import importlib 23 | import os 24 | 25 | 26 | def _getcode(): 27 | cdef = """ 28 | void hister( 29 | double cur_range0, 30 | double cur_range1, 31 | int cur_nbins, 32 | int cur_mult, 33 | double *cur_arr, 34 | int64_t *poss, 35 | int64_t *ind, 36 | int nx); 37 | 38 | void adder_now( 39 | int64_t* res, 40 | int64_t* poss, 41 | int newlen); 42 | 43 | void adder_wei( 44 | double* res, 45 | int64_t* poss, 46 | double* weights, 47 | int newlen); 48 | 49 | """ 50 | 51 | src = """ 52 | static void hister( 53 | double cur_range0, 54 | double cur_range1, 55 | int cur_nbins, 56 | int cur_mult, 57 | double *cur_arr, 58 | int64_t *poss, 59 | int64_t *ind, 60 | int nx) 61 | { 62 | int64_t i, cur_pos; 63 | double curfac = cur_nbins * 1./ (cur_range1- cur_range0); 64 | for(i=0;i=0 ) && (cur_pos xs=np.random.uniform(size=100); ys= np.random.uniform(size=100) 134 | > hh = quick_hist((xs,ys), range=[(0,1),(0,1)], nbins=[20,10]) 135 | Arguments: 136 | arr -- tuple of N-arrays 137 | range -- list of tuples of ranges 138 | nbins -- list of numbers of bins 139 | Keywords: 140 | weights -- weighting for the histogram 141 | getPos -- return the 1D vector of the positions within the histogram 142 | (-1 if the point is outside the range) 143 | """ 144 | nd = len(arrs) 145 | if range is None: 146 | range = [] 147 | for i in np.arange(nd): 148 | range.append((arrs[0].min(), arrs[0].max())) 149 | if nbins is None: 150 | nbins = [10] * nd 151 | 152 | if len(nbins) != nd: 153 | raise ValueError('The array of nbins MUST have the same length as the' 154 | 'number of input data vectors') 155 | if len(range) != nd: 156 | raise ValueError('The array of ranges MUST have the same length' 157 | 'as the number of input data vectors') 158 | 159 | nx = arrs[0].size 160 | for curarr in arrs: 161 | if (curarr.size) != nx: 162 | raise ValueError('All the input arrays MUST have the same length!') 163 | if weights is not None: 164 | if (weights.size) != nx: 165 | raise ValueError('The weights array MUST have the same length as' 166 | ' the input arrays') 167 | # convert all the bins into integers 168 | nbins = [int(_tmp) for _tmp in nbins] 169 | 170 | poss = np.zeros((nx, ), dtype=np.int64) 171 | ind = np.ones((nx, ), dtype=np.int64) 172 | nbins_rev = nbins + [] 173 | nbins_rev.reverse() 174 | mults = (reduce(lambda x, y: x + [y * x[-1]], nbins_rev, [1]))[:-1] 175 | mults.reverse() 176 | assert (poss.flags['C_CONTIGUOUS']) 177 | assert (ind.flags['C_CONTIGUOUS']) 178 | slow = False 179 | modname = _getlibname() 180 | 181 | for i in np.arange(nd): 182 | cur_arr = np.ascontiguousarray(arrs[i], dtype=np.float64) 183 | cur_range0 = float(range[i][0]) 184 | cur_range1 = float(range[i][1]) 185 | cur_nbins = int(nbins[i]) 186 | cur_mult = int(mults[i]) 187 | try: 188 | try: 189 | M = importlib.import_module(modname) 190 | # from _quick_hist import ffi, lib 191 | except ModuleNotFoundError: 192 | _buildlib() 193 | M = importlib.import_module(modname) 194 | poss_ffi = M.ffi.cast('int64_t *', M.ffi.from_buffer(poss)) 195 | ind_ffi = M.ffi.cast('int64_t *', M.ffi.from_buffer(ind)) 196 | cur_arr_ffi = M.ffi.cast('double *', M.ffi.from_buffer(cur_arr)) 197 | 198 | M.lib.hister(cur_range0, cur_range1, cur_nbins, cur_mult, 199 | cur_arr_ffi, poss_ffi, ind_ffi, nx) 200 | except: # noqa 201 | warnings.warn("Sorry the compiled version didn't work :(," 202 | "executing a slower Python-only version.") 203 | slow = True 204 | cur_pos = (cur_arr - cur_range0) * (cur_nbins * 1. / 205 | (cur_range1 - cur_range0)) 206 | cur_pos = np.floor(cur_pos).astype(np.int64) 207 | ind = ind * ((cur_pos >= 0) & (cur_pos < cur_nbins)) 208 | poss += cur_pos * cur_mult 209 | 210 | ind = ind.astype(bool) 211 | 212 | poss = poss[ind] 213 | newlen = len(poss) 214 | 215 | nret = np.array(nbins, dtype=np.int64).prod() 216 | 217 | if weights is not None: 218 | res = np.zeros(nret, dtype=np.float64) 219 | weightsind = np.ascontiguousarray(weights[ind], dtype=np.float64) 220 | else: 221 | res = np.zeros(nret, dtype=np.int64) 222 | 223 | if not getPos: 224 | del ind 225 | 226 | assert (res.flags['C_CONTIGUOUS']) 227 | assert (poss.flags['C_CONTIGUOUS']) 228 | 229 | if not slow: 230 | poss_ffi = M.ffi.cast('int64_t *', M.ffi.from_buffer(poss)) 231 | 232 | if weights is not None: 233 | weightsind_ffi = M.ffi.cast('double *', 234 | M.ffi.from_buffer(weightsind)) 235 | res_ffi = M.ffi.cast('double *', M.ffi.from_buffer(res)) 236 | M.lib.adder_wei(res_ffi, poss_ffi, weightsind_ffi, newlen) 237 | else: 238 | res_ffi = M.ffi.cast('int64_t *', M.ffi.from_buffer(res)) 239 | M.lib.adder_now(res_ffi, poss_ffi, newlen) 240 | else: 241 | if weights is None: 242 | for i in np.arange(len(poss)): 243 | res[poss[i]] += 1 244 | else: 245 | for i in np.arange(len(poss)): 246 | res[poss[i]] += weightsind[i] 247 | if not getPos: 248 | return res.reshape(nbins) 249 | else: 250 | H = np.zeros(len(ind), dtype=np.int64) - 1 251 | H[ind] = poss 252 | return res.reshape(nbins), H 253 | -------------------------------------------------------------------------------- /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 | import numpy.random, numpy, quick_hist, scipy.stats, random 18 | 19 | __doc__ = """ 20 | Adaptive binner module 21 | Functions: 22 | hist: 23 | 1D adaptive histogram 24 | hist2d: 25 | 2D adaptive histogram 26 | 27 | """ 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), 34 | range=((0, 1), (0, 1)), 35 | nbins=[2**curhi, 2**curhi]) 36 | hhs[curhi] = hh 37 | pixcen = [] 38 | hh0 = hhs[hi2] * 1 # accumulator of the result 39 | area = hh0 * 0 40 | 41 | two = 2 42 | poiss = scipy.stats.poisson(thresh) 43 | 44 | DeepenOrNot = lambda x: random.random() < poiss.cdf(x) 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, 61 | jj * dx:(jj + 1) * dx] = curval 62 | area[ii * dx:(ii + 1) * dx, 63 | jj * dx:(jj + 1) * dx] = 2**(-it) 64 | pixcen.append( 65 | (ii * dx + dx / 2. + 0.5, jj * dx + dx / 2. + 0.5)) 66 | 67 | n1 = 2**hi1 68 | dn = 2**(hi2 - hi1) 69 | 70 | for i in range(n1): 71 | for j in range(n1): 72 | if DeepenOrNot(hhs[hi1][i, j]): 73 | doitit(hi1 + 1, i * two, j * two) 74 | else: 75 | hh0[i * dn:(i + 1) * dn, j * dn:(j + 1) * dn] = hhs[hi1][i, j] 76 | area[i * dn:(i + 1) * dn, j * dn:(j + 1) * dn] = 2**(-hi1) 77 | pixcen.append((i * dn + dn / 2. + 0.5, j * dn + dn / 2. + 0.5)) 78 | area = (2**hi1 * area)**2 # in smallest pixels squared 79 | return hh0 * 1. / area, pixcen, area 80 | 81 | 82 | def __doit1d(x, hi1=None, hi2=None, thresh=None): 83 | hhs = {} 84 | for curhi in range(hi1, hi2 + 1): 85 | hh = quick_hist.quick_hist((x, ), range=((0, 1), ), nbins=[2**curhi]) 86 | hhs[curhi] = hh 87 | 88 | hh0 = hhs[hi2] * 1 # accumulator of the result 89 | area = hh0 * 0 90 | 91 | two = 2 92 | poiss = scipy.stats.poisson(thresh) 93 | 94 | DeepenOrNot = lambda x: random.random() < poiss.cdf(x) 95 | 96 | def doitit(it, i): 97 | curhhs = hhs[it] 98 | if it == hi2: 99 | hh[i:i + 2] = curhhs[i:i + 2] 100 | area[i:i + 2] = 2**(-it) 101 | else: 102 | for ii in range(i, i + 2): 103 | curval = curhhs[ii] 104 | if DeepenOrNot(curval): 105 | doitit(it + 1, ii * two) 106 | else: 107 | dx = 2**(hi2 - it) 108 | hh0[ii * dx:(ii + 1) * dx] = curval 109 | area[ii * dx:(ii + 1) * dx] = 2**(-it) 110 | 111 | n1 = 2**hi1 112 | dn = 2**(hi2 - hi1) 113 | 114 | for i in range(n1): 115 | if DeepenOrNot(hhs[hi1][i]): 116 | doitit(hi1 + 1, i * two) 117 | else: 118 | hh0[i * dn:(i + 1) * dn] = hhs[hi1][i] 119 | area[i * dn:(i + 1) * dn] = 2**(-hi1) 120 | return hh0 * 1. / (2**hi1 * area) 121 | 122 | 123 | def hist2d(x, 124 | y, 125 | xmin=None, 126 | xmax=None, 127 | ymin=None, 128 | ymax=None, 129 | hi=[2, 10], 130 | thresh=30, 131 | full_output=False): 132 | """ 133 | This function does the 2D histogram with adaptive binning 134 | Example: 135 | >> hist2d(xs,ys, hi=[3,6], thresh=30) 136 | >> hh,xloc,yloc,pixcen,area = hist2d(xra,dec,full_output=True,thresh=100) 137 | 138 | Keyword parameters: 139 | ------------------ 140 | hi 141 | the list of two integer values: they describe how coarse the 142 | largest bin and how fine is the smallest bin, e.g. 143 | [2,5] means the largest possible bin has a size 144 | of 1/2**2 of the your dataset and the smallest bin has a 145 | size of 1/2**5 146 | thresh 147 | the minimum number of points within a bin allowed ( 148 | if the number is smaller than the threshold then further 149 | decreasing of the bin size is not allowed by the algorithm) 150 | xmin,xmax,ymin,ymax 151 | x-ranges and y-ranges. If not specified, they are determined 152 | from the x.min(),x.max(),y.min(),y.max() 153 | full_output 154 | Boolean controlling whether to output just just the histogram(full_output=False) 155 | or output the tuple with the histogram, grid-centers in x, grid-centers in y, 156 | pixel-centers, and area 157 | """ 158 | xmin = x.min() if xmin is None else xmin 159 | ymin = y.min() if ymin is None else ymin 160 | xmax = x.max() if xmax is None else xmax 161 | ymax = y.max() if ymax is None else ymax 162 | 163 | xmod = (x - xmin) / (xmax - xmin) 164 | ymod = (y - ymin) / (ymax - ymin) 165 | 166 | ind = (xmod >= 0) & (xmod <= 1) & (ymod >= 0) & (ymod <= 1) 167 | hh, pixcen, area = __doit2d(xmod[ind], 168 | ymod[ind], 169 | hi1=hi[0], 170 | hi2=hi[1], 171 | thresh=thresh) 172 | xloc = numpy.linspace(xmin, xmax, hh.shape[0], False) 173 | yloc = numpy.linspace(ymin, ymax, hh.shape[0], False) 174 | xloc += 0.5 * (xloc[1] - xloc[0]) 175 | yloc += 0.5 * (yloc[1] - yloc[0]) 176 | 177 | if full_output: 178 | out = hh, xloc, yloc, pixcen, area 179 | else: 180 | out = hh 181 | return out 182 | 183 | 184 | def hist(x, xmin=None, xmax=None, hi=[2, 10], thresh=30): 185 | """ 186 | This function does the 1D histogram with adaptive binning 187 | Example: 188 | >> loc, hh = hist(xs, hi=[3,6], thresh=30) 189 | 190 | Keyword parameters: 191 | ------------------ 192 | hi 193 | the list of two integer values: they describe how coarse the 194 | largest bin and how fine is the smallest bin, e.g. 195 | [2,5] means the largest possible bin has a size 196 | of 1/2**2 of the your dataset and the smallest bin has a 197 | size of 1/2**5 198 | thresh 199 | the minimum number of points within a bin allowed ( 200 | if the number is smaller than the threshold then further 201 | decreasing of the bin size is not allowed by the algorithm) 202 | xmin,xmax 203 | x-range. If not specified, they are determined 204 | from the x.min(),x.max() 205 | -------------------------------------------- 206 | Returns: 207 | the histogram and the bin edges vector 208 | """ 209 | xmin = x.min() if xmin is None else xmin 210 | xmax = x.max() if xmax is None else xmax 211 | 212 | xmod = (x - xmin) / (xmax - xmin) 213 | 214 | ind = (xmod >= 0) & (xmod <= 1) 215 | hh = __doit1d(xmod[ind], hi1=hi[0], hi2=hi[1], thresh=thresh) 216 | loc = numpy.linspace(xmin, xmax, len(hh) + 1, True) 217 | return hh, loc 218 | -------------------------------------------------------------------------------- /my_utils/sky_plotter.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import healpy 4 | 5 | # Flip factor for astronomical convention (flips the longitude direction) 6 | flip = -1 7 | 8 | 9 | class Formatter: 10 | """ 11 | Formatter for displaying coordinates in the Mollweide projection. 12 | Converts longitude and latitude from radians to degrees and applies 13 | a shift in right ascension (RA). 14 | """ 15 | 16 | def __init__(self, ra_shift=0): 17 | """ 18 | Parameters 19 | ---------- 20 | ra_shift : float 21 | Shift in right ascension (degrees). 22 | """ 23 | self.ra_shift = ra_shift 24 | 25 | def __call__(self, lon, lat): 26 | """ 27 | Format the coordinates for display. 28 | 29 | Parameters 30 | ---------- 31 | lon : float 32 | Longitude in radians. 33 | lat : float 34 | Latitude in radians. 35 | 36 | Returns 37 | ------- 38 | str 39 | Formatted string with longitude and latitude in degrees. 40 | """ 41 | lon, lat = np.rad2deg([lon, lat]) 42 | return '%f,%f' % (flip * lon + self.ra_shift, lat) 43 | 44 | 45 | class Info: 46 | """ 47 | Container for storing additional information about the plot, 48 | such as the RA shift. 49 | """ 50 | 51 | def __init__(self, ra_shift): 52 | """ 53 | Parameters 54 | ---------- 55 | ra_shift : float 56 | Shift in right ascension (degrees). 57 | """ 58 | self.ra_shift = ra_shift 59 | 60 | 61 | def make_axes(ra_shift=None, dra_label=30, plot_grid=True, grid_linestyle=':'): 62 | """ 63 | Create a Mollweide projection plot with customized RA labels. 64 | 65 | Parameters 66 | ---------- 67 | ra_shift : float, optional 68 | Shift in right ascension (degrees). Default is 0. 69 | dra_label : int, optional 70 | Step size for RA labels (degrees). Default is 30. 71 | """ 72 | if ra_shift is None: 73 | ra_shift = 0 74 | ax = plt.subplot(111, projection='mollweide') 75 | 76 | # Generate RA tick positions and labels 77 | xticks = np.arange(0, 360, dra_label) 78 | xticks_shift = (flip * xticks + ra_shift + 360 + 180) % 360 - 180 79 | xtick_labels = xticks.astype(int) 80 | xtick_labels = ['{:d}$^o$'.format(i) for i in xtick_labels] 81 | 82 | # Set RA ticks and labels 83 | plt.xticks(ticks=np.radians(xticks_shift), labels=xtick_labels, rotation=0) 84 | plt.grid(plot_grid, linestyle=grid_linestyle) 85 | 86 | # Set custom coordinate formatter 87 | ax.format_coord = Formatter(ra_shift) 88 | 89 | # Store additional information in the axes object 90 | ax._extra = Info(ra_shift) 91 | 92 | 93 | def _line_unwrapper(ra, dec): 94 | """ 95 | Handle line wrapping at the edges of the Mollweide projection. 96 | 97 | Parameters 98 | ---------- 99 | ra : array-like 100 | Right ascension (radians). 101 | dec : array-like 102 | Declination (radians). 103 | 104 | Returns 105 | ------- 106 | tuple 107 | Arrays of RA and Dec with NaNs inserted to avoid wrapping. 108 | """ 109 | dra = np.diff(ra) 110 | cross = np.abs(dra) > np.pi # Detect line crossings 111 | x1 = _insert_nans(ra, cross) 112 | y1 = _insert_nans(dec, cross) 113 | return x1, y1 114 | 115 | 116 | def _insert_nans(x, ind): 117 | """ 118 | Insert NaNs into an array to handle discontinuities. 119 | 120 | Parameters 121 | ---------- 122 | x : array-like 123 | Original 1D array. 124 | ind : array-like 125 | Boolean array indicating where to insert NaNs. 126 | 127 | Returns 128 | ------- 129 | array 130 | New array with NaNs inserted. 131 | """ 132 | N = len(x) 133 | num_nan = np.count_nonzero(ind) # Number of NaNs to insert 134 | 135 | # Create a new array with space for NaNs 136 | x1 = np.full(N + num_nan, np.nan, dtype=float) 137 | 138 | # Compute positions for original values 139 | shift_array = np.insert(np.cumsum(ind), 0, 0) 140 | positions = np.arange(N) + shift_array 141 | 142 | # Insert original values into the new array 143 | x1[positions] = x 144 | 145 | return x1 146 | 147 | 148 | def scatter(ra, dec, ra_shift=None, dra_label=30, overplot=False, **kwargs): 149 | """ 150 | Scatter plot in Mollweide projection. 151 | 152 | Parameters 153 | ---------- 154 | ra : array-like 155 | Right ascension (degrees). 156 | dec : array-like 157 | Declination (degrees). 158 | ra_shift : float, optional 159 | Shift in right ascension (degrees). Default is None. 160 | dra_label : int, optional 161 | Step size for RA labels (degrees). Default is 30. 162 | overplot : bool, optional 163 | If True, reuse the existing axes. Default is False. 164 | kwargs : dict 165 | Additional arguments passed to `plt.scatter`. 166 | """ 167 | if not overplot: 168 | make_axes(ra_shift=ra_shift, dra_label=dra_label) 169 | else: 170 | ra_shift = plt.gca()._extra.ra_shift 171 | 172 | # Convert RA and Dec to radians and apply RA shift 173 | ra_rad = np.deg2rad(flip * np.asarray(ra) + ra_shift) 174 | ra_rad = (ra_rad + 11 * np.pi) % (2 * 175 | np.pi) - np.pi # Wrap RA to [-pi, pi] 176 | dec_rad = np.deg2rad(dec) 177 | 178 | # Plot the scatter points 179 | plt.scatter(ra_rad, dec_rad, **kwargs) 180 | 181 | 182 | def plot(ra, dec, ra_shift=None, dra_label=30, overplot=False, **kwargs): 183 | """ 184 | Line plot in Mollweide projection. 185 | 186 | Parameters 187 | ---------- 188 | ra : array-like 189 | Right ascension (degrees). 190 | dec : array-like 191 | Declination (degrees). 192 | ra_shift : float, optional 193 | Shift in right ascension (degrees). Default is None. 194 | dra_label : int, optional 195 | Step size for RA labels (degrees). Default is 30. 196 | overplot : bool, optional 197 | If True, reuse the existing axes. Default is False. 198 | kwargs : dict 199 | Additional arguments passed to `plt.plot`. 200 | """ 201 | if not overplot: 202 | make_axes(ra_shift=ra_shift, dra_label=dra_label) 203 | ra_shift = ra_shift or 0 204 | else: 205 | ra_shift = plt.gca()._extra.ra_shift 206 | 207 | # Convert RA and Dec to radians and apply RA shift 208 | ra_rad = np.deg2rad(flip * np.asarray(ra) + ra_shift) 209 | ra_rad = (ra_rad + 11 * np.pi) % (2 * 210 | np.pi) - np.pi # Wrap RA to [-pi, pi] 211 | dec_rad = np.deg2rad(dec) 212 | 213 | # Handle line wrapping 214 | ra_rad1, dec_rad1 = _line_unwrapper(ra_rad, dec_rad) 215 | 216 | # Plot the line 217 | plt.plot(ra_rad1, dec_rad1, **kwargs) 218 | 219 | 220 | def hpx_show(im, 221 | ra_shift=None, 222 | overplot=False, 223 | nest=True, 224 | dra_label=30, 225 | pix_per_deg=10, 226 | **kwargs): 227 | """ 228 | Plot a HEALPix array on a Mollweide projection. 229 | 230 | Parameters 231 | ---------- 232 | im : ndarray 233 | HEALPix array (length 12 * nside^2). 234 | The assumption that the array is in the same coordinate 235 | system as the plot. 236 | ra_shift : float, optional 237 | Shift in right ascension (degrees). Default is None. 238 | overplot : bool, optional 239 | If True, reuse the existing axes. Default is False. 240 | nest : bool, optional 241 | If True, use nested HEALPix ordering. Default is True. 242 | pix_per_deg : float, optional 243 | Resolution of the plot (pixels per degree). Default is 10. 244 | kwargs : dict 245 | Additional arguments passed to `pcolormesh`. 246 | 247 | Returns 248 | ------- 249 | QuadMesh 250 | The plotted HEALPix map. 251 | """ 252 | if not overplot: 253 | make_axes(ra_shift=ra_shift, dra_label=dra_label) 254 | ra_shift = ra_shift or 0 255 | else: 256 | ra_shift = plt.gca()._extra.ra_shift 257 | 258 | # Generate a grid of longitude and latitude 259 | nlon = int(360 * pix_per_deg) 260 | nlat = int(180 * pix_per_deg) 261 | lon_rad = np.radians(np.linspace(-180, 180, nlon)) 262 | lat_rad = np.radians(np.linspace(-90, 90, nlat)) 263 | lon2d, lat2d = np.meshgrid(lon_rad, lat_rad) 264 | 265 | # Compute HEALPix pixel indices 266 | nside = int(round((len(im) / 12)**0.5)) 267 | assert len(im) == 12 * nside**2 268 | hpx = healpy.ang2pix(nside, 269 | flip * (np.rad2deg(lon2d) - ra_shift), 270 | np.rad2deg(lat2d), 271 | lonlat=True, 272 | nest=nest) 273 | 274 | # Map HEALPix values to the grid 275 | arr = im[hpx] 276 | 277 | # Plot the HEALPix map 278 | ax = plt.gca() 279 | R = ax.pcolormesh(lon2d, lat2d, arr, **kwargs) 280 | return R 281 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /my_utils/correct_pm.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import astropy.coordinates as acoo 3 | import astropy.units as auni 4 | 5 | # Use v4.0 defaults 6 | GCPARAMS = acoo.galactocentric_frame_defaults.get_from_registry( 7 | "v4.0")['parameters'] 8 | 9 | kms = auni.km / auni.s 10 | masyr = auni.mas / auni.year 11 | kpc = auni.kpc 12 | 13 | # --- Pre-calculate constants and solar velocity vector --- 14 | 15 | # Conversion factor: (km/s) / (kpc * mas/yr) 16 | # k_masyr_kpc = (kms / (kpc * masyr)).to_value(1) # ~4.74047 17 | k_masyr_kpc = 4.740470463498976 18 | 19 | # Get the Sun's velocity vector relative to the GC in Galactocentric Cartesian 20 | # coordinates (U, V, W) 21 | v_sun_gal_xyz = GCPARAMS['galcen_v_sun'].get_d_xyz().to_value( 22 | kms) # [U, V, W] in km/s 23 | 24 | 25 | # We need the Sun's velocity vector expressed in ICRS Cartesian coordinates 26 | # (vx, vy, vz). 27 | # The original Astropy code effectively calculates the apparent motion seen 28 | # by the Sun 29 | # due to an object at rest in the Galactocentric frame. This corresponds to 30 | # transforming the *negative* of the Sun's velocity vector (-U, -V, -W) from 31 | # the Galactocentric frame to the ICRS frame. 32 | # We can get this precise vector by performing the Astropy calculation once. 33 | def _get_apparent_solar_velocity_icrs(params): 34 | """ 35 | Helper to get the ICRS velocity vector corresponding to solar motion 36 | relative to the GC frame origin, consistent with the original code's logic. 37 | """ 38 | # Use a dummy coordinate transformation 39 | # A point at rest (0 velocity) in Galactocentric frame 40 | gc_rest = acoo.Galactocentric( 41 | x=0 * kpc, 42 | y=0 * kpc, 43 | z=0 * kpc, # Position doesn't affect velocity transform 44 | v_x=0 * kms, 45 | v_y=0 * kms, 46 | v_z=0 * kms, 47 | **params) # Pass GCPARAMS for frame definition 48 | # Transform this point *to* ICRS. Its velocity in ICRS represents the 49 | # apparent velocity induced by the Sun's motion relative to the GC frame. 50 | icrs_apparent = gc_rest.transform_to(acoo.ICRS()) 51 | # Return the Cartesian velocity components in ICRS 52 | return icrs_apparent.velocity.d_xyz.to_value(kms) 53 | 54 | 55 | # Calculate this vector once using the specified GCPARAMS 56 | V_APPARENT_XYZ_ICRS = _get_apparent_solar_velocity_icrs(GCPARAMS) 57 | vx, vy, vz = V_APPARENT_XYZ_ICRS # km/s 58 | 59 | 60 | def correct_vel(ra, dec, vel, vlsr=None): 61 | """ 62 | Corrects the radial velocity for the speed of the Sun using direct 63 | calculation. 64 | 65 | Arguments: 66 | ra - RA in deg (array or scalar) 67 | dec -- Declination in deg (array or scalar) 68 | vel -- heliocentric rv in km/s (array or scalar) 69 | vlsr (optional) -- ignored, kept for interface compatibility 70 | split (optional) -- ignored, calculations are vectorized 71 | 72 | Returns: 73 | radial velocity corrected for solar reflex motion (km/s) 74 | """ 75 | if vlsr is not None: 76 | print('WARNING vlsr is ignored') 77 | 78 | # Ensure inputs are numpy arrays for vectorized operations 79 | is_scalar = np.isscalar(ra) and np.isscalar(dec) and np.isscalar(vel) 80 | ra = np.atleast_1d(ra) 81 | dec = np.atleast_1d(dec) 82 | vel = np.atleast_1d(vel) 83 | 84 | # Convert angles to radians 85 | ra_rad = np.deg2rad(ra) 86 | dec_rad = np.deg2rad(dec) 87 | 88 | # Calculate unit vector components in line-of-sight direction (p_hat) 89 | cos_dec = np.cos(dec_rad) 90 | sin_dec = np.sin(dec_rad) 91 | del dec_rad 92 | cos_ra = np.cos(ra_rad) 93 | sin_ra = np.sin(ra_rad) 94 | del ra_rad 95 | # Unit vector p_hat = (cos(dec)cos(ra), cos(dec)sin(ra), sin(dec)) 96 | px = cos_dec * cos_ra 97 | py = cos_dec * sin_ra 98 | pz = sin_dec 99 | del cos_dec, sin_dec, cos_ra, sin_ra 100 | # Project apparent solar velocity (V_APPARENT_XYZ_ICRS) onto the line 101 | # of sight (p_hat) 102 | # This gives the radial velocity component *caused* by solar motion 103 | # (like C1.rv in original) 104 | # delta_rv = vx * px + vy * py + vz * pz # km/s 105 | # Use einsum for potentially better performance/clarity with large arrays 106 | delta_rv = np.dot(V_APPARENT_XYZ_ICRS, 107 | np.stack([px, py, pz], axis=0)) # CORRECTED using np.dot 108 | # Corrected velocity = Observed velocity - component due to Sun's motion 109 | corrected_vel = vel - delta_rv 110 | 111 | # Return scalar if input was scalar 112 | if is_scalar: 113 | return corrected_vel.item() 114 | return corrected_vel 115 | 116 | 117 | def correct_pm(ra, dec, pmra, pmdec, dist, vlsr=None): 118 | """ 119 | Corrects the proper motion for the speed of the Sun using direct 120 | calculation. 121 | 122 | Arguments: 123 | ra - RA in deg (array or scalar) 124 | dec -- Declination in deg (array or scalar) 125 | pmra -- pm in RA in mas/yr (with cosine term) (array or scalar) 126 | pmdec -- pm in declination in mas/yr (array or scalar) 127 | dist -- distance in kpc (array or scalar) 128 | split (optional) -- ignored, calculations are vectorized 129 | vlsr (optional) -- ignored, kept for interface compatibility 130 | 131 | Returns: 132 | (pmra, pmdec) tuple with proper motions corrected for Sun's motion 133 | (mas/yr) 134 | """ 135 | if vlsr is not None: 136 | print('WARNING vlsr is ignored') 137 | 138 | # Ensure inputs are numpy arrays 139 | is_scalar = (np.isscalar(ra) and np.isscalar(dec) and np.isscalar(pmra) 140 | and np.isscalar(pmdec) and np.isscalar(dist)) 141 | ra = np.atleast_1d(ra) 142 | dec = np.atleast_1d(dec) 143 | pmra = np.atleast_1d(pmra) 144 | pmdec = np.atleast_1d(pmdec) 145 | dist = np.atleast_1d(dist) 146 | 147 | # Handle case where dist is scalar but others are arrays 148 | if dist.size == 1 and ra.size > 1: 149 | dist = np.full_like(ra, dist.item()) 150 | elif dist.size != ra.size: 151 | raise ValueError("Shape mismatch between coordinates and distance") 152 | 153 | # Convert angles to radians 154 | ra_rad = np.deg2rad(ra) 155 | dec_rad = np.deg2rad(dec) 156 | 157 | # Calculate unit vector components for RA and Dec directions 158 | sin_ra = np.sin(ra_rad) 159 | cos_ra = np.cos(ra_rad) 160 | sin_dec = np.sin(dec_rad) 161 | cos_dec = np.cos(dec_rad) 162 | del ra_rad, dec_rad 163 | # RA direction unit vector (ra_hat = (-sin(ra), cos(ra), 0)) 164 | ra_hat_x = -sin_ra 165 | ra_hat_y = cos_ra 166 | ra_hat_z = np.zeros_like(sin_ra) # Explicitly zero for clarity 167 | 168 | # Dec direction unit vector 169 | # (dec_hat = (-sin(dec)cos(ra), -sin(dec)sin(ra), cos(dec))) 170 | dec_hat_x = -sin_dec * cos_ra 171 | dec_hat_y = -sin_dec * sin_ra 172 | dec_hat_z = cos_dec 173 | del sin_ra, cos_ra, cos_dec, sin_dec 174 | # Project apparent solar velocity (V_APPARENT_XYZ_ICRS) onto RA 175 | # and Dec directions 176 | # This gives the tangential velocity components *caused* by solar motion 177 | # v_ra = vx * ra_hat_x + vy * ra_hat_y + vz * ra_hat_z 178 | # v_dec = vx * dec_hat_x + vy * dec_hat_y + vz * dec_hat_z 179 | v_ra = np.dot(V_APPARENT_XYZ_ICRS, 180 | np.stack([ra_hat_x, ra_hat_y, ra_hat_z], 181 | axis=0)) # CORRECTED using np.dot 182 | v_dec = np.dot(V_APPARENT_XYZ_ICRS, 183 | np.stack([dec_hat_x, dec_hat_y, dec_hat_z], 184 | axis=0)) # CORRECTED using np.dot 185 | # Convert tangential velocities (km/s) to proper motions (mas/yr) 186 | # delta_pm = v_tangential / (distance_kpc * k_masyr_kpc) 187 | # Avoid division by zero or very small distances if necessary 188 | with np.errstate(divide='ignore', 189 | invalid='ignore'): # Suppress warnings for dist=0 190 | delta_pmra_cosdec = v_ra / (dist * k_masyr_kpc) # mas/yr 191 | delta_pmdec = v_dec / (dist * k_masyr_kpc) # mas/yr 192 | 193 | # Handle potential NaNs/Infs resulting from dist=0 or very small dist 194 | delta_pmra_cosdec[~np.isfinite(delta_pmra_cosdec 195 | )] = np.nan # Or np.nan if preferred 196 | delta_pmdec[~np.isfinite(delta_pmdec 197 | )] = np.nan # Or np.nan if preferred 198 | 199 | # Corrected PM = Observed PM - component due to Sun's motion 200 | corrected_pmra = pmra - delta_pmra_cosdec 201 | corrected_pmdec = pmdec - delta_pmdec 202 | 203 | # Return scalars if input was scalar 204 | if is_scalar: 205 | return corrected_pmra.item(), corrected_pmdec.item() 206 | return corrected_pmra, corrected_pmdec 207 | 208 | 209 | # --- Example Usage and Comparison (Optional) --- 210 | if __name__ == "__main__": 211 | # Import original functions for comparison 212 | from correct_pm import correct_pm as correct_pm_orig 213 | from correct_pm import correct_vel as correct_vel_orig 214 | 215 | # Sample Data (e.g., 5 stars) 216 | N_stars = 5000 217 | ra_deg = np.random.uniform(0, 360, N_stars) 218 | dec_deg = np.random.uniform(-90, 90, N_stars) 219 | pmra_masyr = np.random.normal(0, 5, N_stars) # pm_ra * cos(dec) 220 | pmdec_masyr = np.random.normal(0, 5, N_stars) 221 | dist_kpc = np.random.uniform(0.1, 10, N_stars) 222 | vel_kms = np.random.normal(0, 50, N_stars) 223 | 224 | # --- Test Velocity Correction --- 225 | print("--- Velocity Correction Test ---") 226 | corrected_vel_orig = correct_vel_orig(ra_deg, dec_deg, vel_kms) 227 | corrected_vel_fast = correct_vel(ra_deg, dec_deg, vel_kms) 228 | 229 | print("Original Astropy Corrected Vel:", corrected_vel_orig) 230 | print("Fast Numpy Corrected Vel: ", corrected_vel_fast) 231 | print("Difference:", corrected_vel_orig - corrected_vel_fast) 232 | print("Max absolute difference:", 233 | np.max(np.abs(corrected_vel_orig - corrected_vel_fast))) 234 | 235 | # --- Test Proper Motion Correction --- 236 | print("\n--- Proper Motion Correction Test ---") 237 | pmra_corr_orig, pmdec_corr_orig = correct_pm_orig(ra_deg, dec_deg, 238 | pmra_masyr, pmdec_masyr, 239 | dist_kpc) 240 | pmra_corr_fast, pmdec_corr_fast = correct_pm(ra_deg, dec_deg, pmra_masyr, 241 | pmdec_masyr, dist_kpc) 242 | 243 | print("Original Astropy Corrected PMRA:", pmra_corr_orig) 244 | print("Fast Numpy Corrected PMRA: ", pmra_corr_fast) 245 | print("Difference PMRA:", pmra_corr_orig - pmra_corr_fast) 246 | print("Max absolute difference PMRA:", 247 | np.max(np.abs(pmra_corr_orig - pmra_corr_fast))) 248 | 249 | print("\nOriginal Astropy Corrected PMDEC:", pmdec_corr_orig) 250 | print("Fast Numpy Corrected PMDEC: ", pmdec_corr_fast) 251 | print("Difference PMDEC:", pmdec_corr_orig - pmdec_corr_fast) 252 | print("Max absolute difference PMDEC:", 253 | np.max(np.abs(pmdec_corr_orig - pmdec_corr_fast))) 254 | 255 | # --- Test Scalar Input --- 256 | print("\n--- Scalar Input Test ---") 257 | ra_s, dec_s, pmra_s, pmdec_s, dist_s, vel_s = ra_deg[0], dec_deg[ 258 | 0], pmra_masyr[0], pmdec_masyr[0], dist_kpc[0], vel_kms[0] 259 | 260 | vel_orig_s = correct_vel_orig(ra_s, dec_s, vel_s) 261 | vel_fast_s = correct_vel(ra_s, dec_s, vel_s) 262 | print(f"Scalar Vel Orig: {vel_orig_s:.5f}, Fast: {vel_fast_s:.5f}," 263 | f"Diff: {vel_orig_s - vel_fast_s:.2e}") 264 | 265 | pm_orig_s = correct_pm_orig(ra_s, dec_s, pmra_s, pmdec_s, dist_s) 266 | pm_fast_s = correct_pm(ra_s, dec_s, pmra_s, pmdec_s, dist_s) 267 | print(f"Scalar PMRA Orig: {pm_orig_s[0]:.5f}, Fast: {pm_fast_s[0]:.5f}," 268 | f" Diff: {pm_orig_s[0] - pm_fast_s[0]:.2e}") 269 | print(f"Scalar PMDEC Orig: {pm_orig_s[1]:.5f}, Fast: {pm_fast_s[1]:.5f}," 270 | f" Diff: {pm_orig_s[1] - pm_fast_s[1]:.2e}") 271 | 272 | # --- Test Scalar Input2 --- 273 | print("\n--- Scalar Input Test ---") 274 | ra_s, dec_s, pmra_s, pmdec_s, dist_s, vel_s = ra_deg[0], dec_deg[ 275 | 0], pmra_masyr[0], pmdec_masyr[0], dist_kpc[0], vel_kms[0] 276 | 277 | pmra_corr_orig, pmdec_corr_orig = correct_pm_orig(ra_deg, dec_deg, 278 | pmra_masyr, pmdec_masyr, 279 | dist_s) 280 | pmra_corr_fast, pmdec_corr_fast = correct_pm(ra_deg, dec_deg, pmra_masyr, 281 | pmdec_masyr, dist_s) 282 | print("Original Astropy Corrected PMRA:", pmra_corr_orig) 283 | print("Fast Numpy Corrected PMRA: ", pmra_corr_fast) 284 | print("Difference PMRA:", pmra_corr_orig - pmra_corr_fast) 285 | print("Max absolute difference PMRA:", 286 | np.max(np.abs(pmra_corr_orig - pmra_corr_fast))) 287 | 288 | print("\nOriginal Astropy Corrected PMDEC:", pmdec_corr_orig) 289 | print("Fast Numpy Corrected PMDEC: ", pmdec_corr_fast) 290 | print("Difference PMDEC:", pmdec_corr_orig - pmdec_corr_fast) 291 | print("Max absolute difference PMDEC:", 292 | np.max(np.abs(pmdec_corr_orig - pmdec_corr_fast))) 293 | -------------------------------------------------------------------------------- /astrolib/baryvel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from numpy import sin, cos, array, reshape, transpose, dot, pi, sqrt 3 | from premat import premat 4 | def baryvel(dje, deq=0): 5 | """ 6 | NAME: 7 | BARYVEL 8 | PURPOSE: 9 | Calculates heliocentric and barycentric velocity components of Earth. 10 | 11 | EXPLANATION: 12 | BARYVEL takes into account the Earth-Moon motion, and is useful for 13 | radial velocity work to an accuracy of ~1 m/s. 14 | 15 | CALLING SEQUENCE: 16 | dvel_hel, dvel_bary = baryvel(dje, deq) 17 | 18 | INPUTS: 19 | DJE - (scalar) Julian ephemeris date. 20 | DEQ - (scalar) epoch of mean equinox of dvelh and dvelb. If deq=0 21 | then deq is assumed to be equal to dje. 22 | OUTPUTS: 23 | DVELH: (vector(3)) heliocentric velocity component. in km/s 24 | DVELB: (vector(3)) barycentric velocity component. in km/s 25 | 26 | The 3-vectors DVELH and DVELB are given in a right-handed coordinate 27 | system with the +X axis toward the Vernal Equinox, and +Z axis 28 | toward the celestial pole. 29 | 30 | OPTIONAL KEYWORD SET: 31 | JPL - if /JPL set, then BARYVEL will call the procedure JPLEPHINTERP 32 | to compute the Earth velocity using the full JPL ephemeris. 33 | The JPL ephemeris FITS file JPLEPH.405 must exist in either the 34 | current directory, or in the directory specified by the 35 | environment variable ASTRO_DATA. Alternatively, the JPL keyword 36 | can be set to the full path and name of the ephemeris file. 37 | A copy of the JPL ephemeris FITS file is available in 38 | http://idlastro.gsfc.nasa.gov/ftp/data/ 39 | PROCEDURES CALLED: 40 | Function PREMAT() -- computes precession matrix 41 | JPLEPHREAD, JPLEPHINTERP, TDB2TDT - if /JPL keyword is set 42 | NOTES: 43 | Algorithm taken from FORTRAN program of Stumpff (1980, A&A Suppl, 41,1) 44 | Stumpf claimed an accuracy of 42 cm/s for the velocity. A 45 | comparison with the JPL FORTRAN planetary ephemeris program PLEPH 46 | found agreement to within about 65 cm/s between 1986 and 1994 47 | 48 | If /JPL is set (using JPLEPH.405 ephemeris file) then velocities are 49 | given in the ICRS system; otherwise in the FK4 system. 50 | EXAMPLE: 51 | Compute the radial velocity of the Earth toward Altair on 15-Feb-1994 52 | using both the original Stumpf algorithm and the JPL ephemeris 53 | 54 | IDL> jdcnv, 1994, 2, 15, 0, jd ;==> JD = 2449398.5 55 | IDL> baryvel, jd, 2000, vh, vb ;Original algorithm 56 | ==> vh = [-17.07243, -22.81121, -9.889315] ;Heliocentric km/s 57 | ==> vb = [-17.08083, -22.80471, -9.886582] ;Barycentric km/s 58 | IDL> baryvel, jd, 2000, vh, vb, /jpl ;JPL ephemeris 59 | ==> vh = [-17.07236, -22.81126, -9.889419] ;Heliocentric km/s 60 | ==> vb = [-17.08083, -22.80484, -9.886409] ;Barycentric km/s 61 | 62 | IDL> ra = ten(19,50,46.77)*15/!RADEG ;RA in radians 63 | IDL> dec = ten(08,52,3.5)/!RADEG ;Dec in radians 64 | IDL> v = vb[0]*cos(dec)*cos(ra) + $ ;Project velocity toward star 65 | vb[1]*cos(dec)*sin(ra) + vb[2]*sin(dec) 66 | 67 | REVISION HISTORY: 68 | Jeff Valenti, U.C. Berkeley Translated BARVEL.FOR to IDL. 69 | W. Landsman, Cleaned up program sent by Chris McCarthy (SfSU) June 1994 70 | Converted to IDL V5.0 W. Landsman September 1997 71 | Added /JPL keyword W. Landsman July 2001 72 | Documentation update W. Landsman Dec 2005 73 | Converted to Python S. Koposov 2009-2010 74 | """ 75 | 76 | 77 | #Define constants 78 | dc2pi = 2 * pi 79 | cc2pi = 2 * pi 80 | dc1 = 1.0e0 81 | dcto = 2415020.0e0 82 | dcjul = 36525.0e0 #days in Julian year 83 | dcbes = 0.313e0 84 | dctrop = 365.24219572e0 #days in tropical year (...572 insig) 85 | dc1900 = 1900.0e0 86 | au = 1.4959787e8 87 | 88 | #Constants dcfel(i,k) of fast changing elements. 89 | dcfel = array([1.7400353e00, 6.2833195099091e02, 5.2796e-6, 6.2565836e00, 6.2830194572674e02, -2.6180e-6, 4.7199666e00, 8.3997091449254e03, -1.9780e-5, 1.9636505e-1, 8.4334662911720e03, -5.6044e-5, 4.1547339e00, 5.2993466764997e01, 5.8845e-6, 4.6524223e00, 2.1354275911213e01, 5.6797e-6, 4.2620486e00, 7.5025342197656e00, 5.5317e-6, 1.4740694e00, 3.8377331909193e00, 5.6093e-6]) 90 | dcfel = reshape(dcfel, (8, 3)) 91 | 92 | #constants dceps and ccsel(i,k) of slowly changing elements. 93 | dceps = array([4.093198e-1, -2.271110e-4, -2.860401e-8]) 94 | ccsel = array([1.675104e-2, -4.179579e-5, -1.260516e-7, 2.220221e-1, 2.809917e-2, 1.852532e-5, 1.589963e00, 3.418075e-2, 1.430200e-5, 2.994089e00, 2.590824e-2, 4.155840e-6, 8.155457e-1, 2.486352e-2, 6.836840e-6, 1.735614e00, 1.763719e-2, 6.370440e-6, 1.968564e00, 1.524020e-2, -2.517152e-6, 1.282417e00, 8.703393e-3, 2.289292e-5, 2.280820e00, 1.918010e-2, 4.484520e-6, 4.833473e-2, 1.641773e-4, -4.654200e-7, 5.589232e-2, -3.455092e-4, -7.388560e-7, 4.634443e-2, -2.658234e-5, 7.757000e-8, 8.997041e-3, 6.329728e-6, -1.939256e-9, 2.284178e-2, -9.941590e-5, 6.787400e-8, 4.350267e-2, -6.839749e-5, -2.714956e-7, 1.348204e-2, 1.091504e-5, 6.903760e-7, 3.106570e-2, -1.665665e-4, -1.590188e-7]) 95 | ccsel = reshape(ccsel, (17, 3)) 96 | 97 | #Constants of the arguments of the short-period perturbations. 98 | dcargs = array([5.0974222e0, -7.8604195454652e2, 3.9584962e0, -5.7533848094674e2, 1.6338070e0, -1.1506769618935e3, 2.5487111e0, -3.9302097727326e2, 4.9255514e0, -5.8849265665348e2, 1.3363463e0, -5.5076098609303e2, 1.6072053e0, -5.2237501616674e2, 1.3629480e0, -1.1790629318198e3, 5.5657014e0, -1.0977134971135e3, 5.0708205e0, -1.5774000881978e2, 3.9318944e0, 5.2963464780000e1, 4.8989497e0, 3.9809289073258e1, 1.3097446e0, 7.7540959633708e1, 3.5147141e0, 7.9618578146517e1, 3.5413158e0, -5.4868336758022e2]) 99 | dcargs = reshape(dcargs, (15, 2)) 100 | 101 | #Amplitudes ccamps(n,k) of the short-period perturbations. 102 | ccamps = array([-2.279594e-5, 1.407414e-5, 8.273188e-6, 1.340565e-5, -2.490817e-7, -3.494537e-5, 2.860401e-7, 1.289448e-7, 1.627237e-5, -1.823138e-7, 6.593466e-7, 1.322572e-5, 9.258695e-6, -4.674248e-7, -3.646275e-7, 1.140767e-5, -2.049792e-5, -4.747930e-6, -2.638763e-6, -1.245408e-7, 9.516893e-6, -2.748894e-6, -1.319381e-6, -4.549908e-6, -1.864821e-7, 7.310990e-6, -1.924710e-6, -8.772849e-7, -3.334143e-6, -1.745256e-7, -2.603449e-6, 7.359472e-6, 3.168357e-6, 1.119056e-6, -1.655307e-7, -3.228859e-6, 1.308997e-7, 1.013137e-7, 2.403899e-6, -3.736225e-7, 3.442177e-7, 2.671323e-6, 1.832858e-6, -2.394688e-7, -3.478444e-7, 8.702406e-6, -8.421214e-6, -1.372341e-6, -1.455234e-6, -4.998479e-8, -1.488378e-6, -1.251789e-5, 5.226868e-7, -2.049301e-7, 0.e0, -8.043059e-6, -2.991300e-6, 1.473654e-7, -3.154542e-7, 0.e0, 3.699128e-6, -3.316126e-6, 2.901257e-7, 3.407826e-7, 0.e0, 2.550120e-6, -1.241123e-6, 9.901116e-8, 2.210482e-7, 0.e0, -6.351059e-7, 2.341650e-6, 1.061492e-6, 2.878231e-7, 0.e0]) 103 | ccamps = reshape(ccamps, (15, 5)) 104 | 105 | #Constants csec3 and ccsec(n,k) of the secular perturbations in longitude. 106 | ccsec3 = -7.757020e-8 107 | ccsec = array([1.289600e-6, 5.550147e-1, 2.076942e00, 3.102810e-5, 4.035027e00, 3.525565e-1, 9.124190e-6, 9.990265e-1, 2.622706e00, 9.793240e-7, 5.508259e00, 1.559103e01]) 108 | ccsec = reshape(ccsec, (4, 3)) 109 | 110 | #Sidereal rates. 111 | dcsld = 1.990987e-7 #sidereal rate in longitude 112 | ccsgd = 1.990969e-7 #sidereal rate in mean anomaly 113 | 114 | #Constants used in the calculation of the lunar contribution. 115 | cckm = 3.122140e-5 116 | ccmld = 2.661699e-6 117 | ccfdi = 2.399485e-7 118 | 119 | #Constants dcargm(i,k) of the arguments of the perturbations of the motion 120 | # of the moon. 121 | dcargm = array([5.1679830e0, 8.3286911095275e3, 5.4913150e0, -7.2140632838100e3, 5.9598530e0, 1.5542754389685e4]) 122 | dcargm = reshape(dcargm, (3, 2)) 123 | 124 | #Amplitudes ccampm(n,k) of the perturbations of the moon. 125 | ccampm = array([1.097594e-1, 2.896773e-7, 5.450474e-2, 1.438491e-7, -2.223581e-2, 5.083103e-8, 1.002548e-2, -2.291823e-8, 1.148966e-2, 5.658888e-8, 8.249439e-3, 4.063015e-8]) 126 | ccampm = reshape(ccampm, (3, 4)) 127 | 128 | #ccpamv(k)=a*m*dl,dt (planets), dc1mme=1-mass(earth+moon) 129 | ccpamv = array([8.326827e-11, 1.843484e-11, 1.988712e-12, 1.881276e-12]) 130 | dc1mme = 0.99999696e0 131 | 132 | #Time arguments. 133 | dt = (dje - dcto) / dcjul 134 | tvec = array([1e0, dt, dt * dt]) 135 | 136 | #Values of all elements for the instant(aneous?) dje. 137 | temp = (transpose(dot(transpose(tvec), transpose(dcfel)))) % dc2pi 138 | dml = temp[0] 139 | forbel = temp[1:8] 140 | g = forbel[0] #old fortran equivalence 141 | 142 | deps = (tvec * dceps).sum() % dc2pi 143 | sorbel = (transpose(dot(transpose(tvec), transpose(ccsel)))) % dc2pi 144 | e = sorbel[0] #old fortran equivalence 145 | 146 | #Secular perturbations in longitude. 147 | dummy = cos(2.0) 148 | sn = sin((transpose(dot(transpose(tvec[0:2]), transpose(ccsec[:,1:3])))) % cc2pi) 149 | 150 | #Periodic perturbations of the emb (earth-moon barycenter). 151 | pertl = (ccsec[:,0] * sn).sum() + dt * ccsec3 * sn[2] 152 | pertld = 0.0 153 | pertr = 0.0 154 | pertrd = 0.0 155 | for k in range(0, 15): 156 | a = (dcargs[k,0] + dt * dcargs[k,1]) % dc2pi 157 | cosa = cos(a) 158 | sina = sin(a) 159 | pertl = pertl + ccamps[k,0] * cosa + ccamps[k,1] * sina 160 | pertr = pertr + ccamps[k,2] * cosa + ccamps[k,3] * sina 161 | if k < 11: 162 | pertld = pertld + (ccamps[k,1] * cosa - ccamps[k,0] * sina) * ccamps[k,4] 163 | pertrd = pertrd + (ccamps[k,3] * cosa - ccamps[k,2] * sina) * ccamps[k,4] 164 | 165 | #Elliptic part of the motion of the emb. 166 | phi = (e * e / 4e0) * (((8e0 / e) - e) * sin(g) + 5 * sin(2 * g) + (13 / 3e0) * e * sin(3 * g)) 167 | f = g + phi 168 | sinf = sin(f) 169 | cosf = cos(f) 170 | dpsi = (dc1 - e * e) / (dc1 + e * cosf) 171 | phid = 2 * e * ccsgd * ((1 + 1.5 * e * e) * cosf + e * (1.25 - 0.5 * sinf * sinf)) 172 | psid = ccsgd * e * sinf / sqrt(dc1 - e * e) 173 | 174 | #Perturbed heliocentric motion of the emb. 175 | d1pdro = dc1 + pertr 176 | drd = d1pdro * (psid + dpsi * pertrd) 177 | drld = d1pdro * dpsi * (dcsld + phid + pertld) 178 | dtl = (dml + phi + pertl) % dc2pi 179 | dsinls = sin(dtl) 180 | dcosls = cos(dtl) 181 | dxhd = drd * dcosls - drld * dsinls 182 | dyhd = drd * dsinls + drld * dcosls 183 | 184 | #Influence of eccentricity, evection and variation on the geocentric 185 | # motion of the moon. 186 | pertl = 0.0 187 | pertld = 0.0 188 | pertp = 0.0 189 | pertpd = 0.0 190 | for k in range(0, 3): 191 | a = (dcargm[k,0] + dt * dcargm[k,1]) % dc2pi 192 | sina = sin(a) 193 | cosa = cos(a) 194 | pertl = pertl + ccampm[k,0] * sina 195 | pertld = pertld + ccampm[k,1] * cosa 196 | pertp = pertp + ccampm[k,2] * cosa 197 | pertpd = pertpd - ccampm[k,3] * sina 198 | 199 | #Heliocentric motion of the earth. 200 | tl = forbel[1] + pertl 201 | sinlm = sin(tl) 202 | coslm = cos(tl) 203 | sigma = cckm / (1.0 + pertp) 204 | a = sigma * (ccmld + pertld) 205 | b = sigma * pertpd 206 | dxhd = dxhd + a * sinlm + b * coslm 207 | dyhd = dyhd - a * coslm + b * sinlm 208 | dzhd = -sigma * ccfdi * cos(forbel[2]) 209 | 210 | #Barycentric motion of the earth. 211 | dxbd = dxhd * dc1mme 212 | dybd = dyhd * dc1mme 213 | dzbd = dzhd * dc1mme 214 | for k in range(0, 4): 215 | plon = forbel[k + 3] 216 | pomg = sorbel[k + 1] 217 | pecc = sorbel[k + 9] 218 | tl = (plon + 2.0 * pecc * sin(plon - pomg)) % cc2pi 219 | dxbd = dxbd + ccpamv[k] * (sin(tl) + pecc * sin(pomg)) 220 | dybd = dybd - ccpamv[k] * (cos(tl) + pecc * cos(pomg)) 221 | dzbd = dzbd - ccpamv[k] * sorbel[k + 13] * cos(plon - sorbel[k + 5]) 222 | 223 | 224 | #Transition to mean equator of date. 225 | dcosep = cos(deps) 226 | dsinep = sin(deps) 227 | dyahd = dcosep * dyhd - dsinep * dzhd 228 | dzahd = dsinep * dyhd + dcosep * dzhd 229 | dyabd = dcosep * dybd - dsinep * dzbd 230 | dzabd = dsinep * dybd + dcosep * dzbd 231 | 232 | #Epoch of mean equinox (deq) of zero implies that we should use 233 | # Julian ephemeris date (dje) as epoch of mean equinox. 234 | if deq == 0: 235 | dvelh = au * (array([dxhd, dyahd, dzahd])) 236 | dvelb = au * (array([dxbd, dyabd, dzabd])) 237 | return (dvelh,dvelb) 238 | 239 | #General precession from epoch dje to deq. 240 | deqdat = (dje - dcto - dcbes) / dctrop + dc1900 241 | prema = premat(deqdat, deq, fk4=True) 242 | 243 | dvelh = au * (transpose(dot(transpose(prema), transpose(array([dxhd, dyahd, dzahd]))))) 244 | dvelb = au * (transpose(dot(transpose(prema), transpose(array([dxbd, dyabd, dzabd]))))) 245 | 246 | return (dvelh, dvelb) 247 | 248 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------