"""Planetary mapping routines.
phi = 0 faces toward the observer
phi = pi thus faces away from the observer
theta=pi/2 is the z-axis or 'north pole'
theta=-pi/2 is the 'south pole'
"""
# 2010-01-15 20:31 IJC: Started . . .
from numpy import pi
[docs]def makegrid(nphi,ntheta):
"""Make grids of phi and theta values with the specified number of
points in each direction. Phi ranges from 0 to 2pi, and theta
ranges from -pi/2 to pi/2.
Returns (phi, theta)"""
# 2010-01-15 20:29 IJC: Created
from numpy import meshgrid, linspace
phi,theta = meshgrid(linspace(0,2*pi,nphi),linspace(-pi/2,pi/2,ntheta))
return phi,theta
[docs]def rotcoord(phi,theta,iangle,rangle):
"""rotate coordinate system from local (planetary) coordinates into
observer-oriented coordinages. rangle and iangle are the rotation
and inclination angles of the planet in radians.
returns (phi2,theta2)
phi2 will be in the range (0,2pi) and
theta2 will be in the range (-pi/2,pi/2)
"""
# 2010-01-15 21:09 IJC: Created
from numpy import array,cos,sin,vstack,dot,arctan2,sqrt,pi
nphi,ntheta = phi.shape
phir = phi+rangle
costheta = cos(theta)
x = cos(phir)*costheta
y = sin(phir)*costheta
z = sin(theta)
xyz = vstack((x.ravel(),y.ravel(),z.ravel()))
beta = pi/2.-iangle
sinbeta = sin(beta)
cosbeta = cos(beta)
rotmat = array([[cosbeta,0,-sinbeta],[0,1,0],[sinbeta,0,cosbeta]])
xyz2 = dot(rotmat,xyz)
x2,y2,z2 = xyz2[0].reshape(nphi,ntheta),xyz2[1].reshape(nphi,ntheta),xyz2[2].reshape(nphi,ntheta)
phi2 = arctan2(y2,x2) % (2*pi)
theta2 = arctan2(z2,sqrt(x2**2+y2**2))
return phi2,theta2
[docs]def visiblemap(phi,theta,iangle,rangle):
"""Return a 2D boolean map that's True for the planetary latitude,
longitude values visible from an observer. For rangle (rotation
angle) zero and iangle (inclination angle) equal to pi/2, phi=pi
is toward the observer.
"""
# 2010-01-18 08:13 IJC: Created
nphi,ntheta = phi.shape
phi2,theta2 = rotcoord(phi,theta,iangle,rangle)
vismap = (phi2<pi/2)+(phi2>1.5*pi)
return vismap
[docs]def wedgemap(phi,theta,phi0,phi1):
"""Return a 2D boolean map that's True for the latitude,
longitude values in a given longitudinal planet 'wedge'.
phi0,phi1 should be in the range (0,2*pi)
"""
# 2010-01-18 08:13 IJC: Created
phi0 = phi0 % (2*pi)
phi1 = phi1 % (2*pi)
if phi1>=phi0:
wedge = (phi<phi1)*(phi>=phi0)
elif phi1<phi0:
wedge = (phi>=phi0)+(phi<phi1)
return wedge
[docs]def wedgestack(phi,theta,nwedge,phi0):
"""Return a stack of 2D boolean maps via wedgemap.
phi0 defines the center of wedge zero.
"""
# 2010-01-18 08:13 IJC: Created
from numpy import zeros,arange
dphi = 2*pi/nwedge
pp0=arange(-.5,nwedge-.5,dtype=float)*dphi+phi0
pp1=arange(.5,nwedge+.5,dtype=float)*dphi+phi0
wedges = zeros((nwedge,)+phi.shape,float)
for ii in range(nwedge):
wedges[ii,:,:]= wedgemap(phi,theta,pp0[ii],pp1[ii])
return wedges
[docs]def projarea(phi,theta):
"""Return a 2D map of the projected area. Note that you need to
determine for yourself which parts of the areal map are actually
visible to the observer.
Assumes dA = cos(t) dt df (theta=t, phi=f)
and thus da = cos(f) cos(t)**2 dt df
EXAMPLE:
import maps, pylab
phi, theta = maps.makegrid(300,240)
vis = maps.visiblemap(phi,theta,pi/2,0)
da = maps.projarea(phi,theta)
pylab.imshow(da*vis)
pylab.title('visible projected area sums to: %f (pi)' % (da*vis).sum())
"""
# 2010-01-18 09:23 IJC: Created
from numpy import cos
df = phi[0,1]-phi[0,0]
dt = theta[1,0]-theta[0,0]
da = (cos(theta)**2) * cos(phi) * dt * df
return da
[docs]def wedgebasis(phi,theta,iangle,rangle,nwedge,phi0, fwedge=None):
"""Return a set of basis functions for the flux from each wedge.
phi,theta are the observer-centered grids from MAPS.MAKEGRID
iangle is the inclination of the system (0 = pole-on)
rangle (seq.) is a sequence of (0,2pi) rotation values at which
the wedge-based flux is evaluated.
nwedge is the number of wedges
phi0 defines the center of wedge zero.
fwedge -- an optional sequence to set individual flux values for each wedge.
"""
# 2010-01-18 08:13 IJC: Created
from numpy import array,arange,zeros,tile
rangle = array(rangle,copy=True)
nrot = len(rangle)
bases = zeros((nrot,nwedge),float)
da = projarea(phi,theta)
vis = (phi<=0.5*pi) + (phi>1.5*pi)
phi2,theta2 = rotcoord(phi,theta,iangle,rangle[0])
for ii in range(nrot):
# Faster to shift reference point than entire phi2 grid:
wedges = wedgestack(phi2,theta2,nwedge,phi0-rangle[ii]+rangle[0])
bases[ii,:] = (wedges*da*vis).sum(2).sum(1)
if fwedge<>None:
fwedge = tile(fwedge,(nrot,1))
bases = bases * fwedge
return bases
[docs]def errseries(param,phi,theta,rangle,meas,err):
"""Give the chi-squared to an input timeseries to get the wedge
coefficients and inclination angle.
param -- [inclination, phi0, wedge coefficients]
phi,theta -- from MAKEGRID
rangle -- rotation angles at which flux was measured
meas -- measured flux
err -- error on measurement (one-sigma)
Usage:
from scipy import optimize
optimize.fmin(maps.errseries, guess, args=(...))
"""
# 2010-01-18 14:04 IJC: Created
iangle = param[0]
phi0 = param[1]
fcoef = param[2::]
nwedge = len(fcoef)
model = wedgebasis(phi,theta,iangle,rangle,nwedge,phi0,fwedge=fcoef).sum(1)
chisq = (((model-meas)/err)**2).sum()
print "param, chisq>>" +str(param)+', '+str(chisq)
return chisq
[docs]def makespot(phi, theta, da=None, vmap=None, inc=None, rot=None, long=0, lat=0, siz=pi/4, plotalot=False):
"""Make a spot-map.
EXAMPLE:
import maps
inc, rot = pi/2., 0.
phi, theta = maps.makecoord(120,60)
phi2, theta2 = maps.rotcoord(phi,theta,inc,rot)
vmap = maps.visiblemap(phi,theta,inc,rot)
maps.makespot(phi,theta,inc=pi/2,rot=0.,lat=pi/4,long=1.*pi)
maps.makespot(phi2,theta2,vmap=vmap,lat=pi/4,long=1.*pi)
"""
if plotalot:
from pylab import *
from tools import nextfig
figure(nextfig(), [15,15]); clf()
if da is None:
da = projarea(phi,theta)
if plotalot:
subplot(3,3,1)
imshow(phi); colorbar(); title('phi')
subplot(3,3,2)
imshow(theta); colorbar(); title('theta')
subplot(3,3,3)
imshow(da); colorbar(); title('d_area')
if inc is not None and rot is not None:
phi, theta = rotcoord(phi,theta,inc,rot)
if vmap is None:
vmap = visiblemap(phi,theta,inc,rot)
if plotalot:
subplot(3,3,4)
imshow(phi); colorbar(); title('phi')
subplot(3,3,5)
imshow(theta); colorbar(); title('theta')
subplot(3,3,6)
imshow(vmap); title('vmap: inc=%s, rot=%s'%(inc,rot)); colorbar()
if long is not None and lat is not None:
phi, theta, = rotcoord(phi,theta,-lat, -long)
spot = vmap*da*((theta-theta.max())>-siz)
if plotalot:
subplot(3,3,7)
imshow(phi); colorbar(); title('phi')
subplot(3,3,8)
imshow(theta); colorbar(); title('theta')
subplot(3,3,9)
imshow(spot); title('spot: lat=%s, long=%s'%(lat,long)); colorbar()
return phi, theta, da, vmap, spot, (inc, rot, long, lat, siz)