%pylab inline
from scipy import interpolate
from matplotlib.colors import LogNorm
from datetime import datetime, timedelta
from pyglow import pyglow
from scipy.stats import rv_discrete
import sys
import time
from IPython.display import display, clear_output
import mpld3
# Change matplotlib defaults
matplotlib.rcParams['savefig.dpi'] = 150
matplotlib.rcParams['figure.figsize'] = (4,3)
matplotlib.rcParams['font.size'] = 8
matplotlib.rcParams['savefig.bbox'] = 'tight'
matplotlib.rcParams['ytick.labelsize'] = 'small'
matplotlib.rcParams['xtick.labelsize'] = 'small'
matplotlib.rcParams['legend.fancybox'] = 'True'
matplotlib.rcParams['legend.fontsize'] = 'small'
matplotlib.rcParams['legend.numpoints'] = 1
import warnings
warnings.filterwarnings('once')
Populating the interactive namespace from numpy and matplotlib
What's included:
What's not included:
DSCS_FACTOR = 2*pi
def p_i92(tau):
p0 = 6e-17
tau0 = 1e3
tau1 = 200e3
b1 = 0.75
b2 = 2.
if tau < tau0:
p = p0
elif tau < tau1:
p = p0 * tau0**b1 * tau**(-b1)
else:
p = p0 * tau0**b1 * tau1**(-b1+b2) * tau**(-b2)
return p
def F(th_rad, e, th_min_rad=1e-9):
'''
un-normalized CDF of scattering angle at energy e, calculated analytically (see notebook).
F(pi, e) is the total cross section.
F(th, e)/F(pi, e) is the CDF of the scattering angle.
INPUTS:
th: angle (rad)
e: energy (eV)
OUTPUTS:
F: CDF of scattering angle, weighted by total cross section
'''
p0 = 2*pi*6e-17 # Include 2*pi factor for integration over azimuth
tau0 = 1e3
tau1 = 200e3
b1 = 0.75
b2 = 2.0
# Calculate intermediate variables
p1 = p0 * tau0**b1 * e**(-b1)
p2 = p0 * tau0**b1 * tau1**(-b1+b2) * e**(-b2)
th0 = tau0/e
th1 = tau1/e
th = th_rad * 180/pi # convert to degrees
th_min = th_min_rad * 180/pi # convert to degrees
if th_min > th0:
raise Exception('Error: th_min > th0, violating assumption')
if th < th0:
F = p0*log(th/th_min)
elif th < th1:
F = p0*log(th0/th_min) + p1/b1*(th0**(-b1) - th**(-b1))
else: # th >= th1
F = p0*log(th0/th_min) + p1/b1*(th0**(-b1) - th1**(-b1)) + p2/b2*(th1**(-b2) - th**(-b2))
F = DSCS_FACTOR*F
return F
th_min = 1e-6
e_min = 1e-1
e_vec = logspace(log10(e_min),7,101)
elastic_xsec_vec = zeros(len(e_vec))
for i in range(len(e_vec)):
e = e_vec[i]
elastic_xsec_vec[i] = F(pi, e, th_min)
loglog(e_vec,elastic_xsec_vec,'k-')
xlabel('Energy [eV]');
ylabel('Elastic Scattering Cross section [cm$^2$]');
def elastic_xsec(e, th_min_rad = 1e-9):
return F(pi ,e, th_min_rad)
def elastic_scatt_angle_icdf(e, u, th_min_rad = 1e-9):
'''
The inverse cdf of scattering angle. Given a random number in [0,1], calculate the
corresponding scattering angle.
INPUTS:
e - energy, eV
u - the value of the cdf, in [0,1]
OUTPUTS:
th - scattering angle in rad
'''
p0 = 2*pi*6e-17 # Include 2*pi factor for integration over azimuth
tau0 = 1e3
tau1 = 200e3
b1 = 0.75
b2 = 2.0
# Calculate intermediate variables
p1 = p0 * tau0**b1 * e**(-b1)
p2 = p0 * tau0**b1 * tau1**(-b1+b2) * e**(-b2)
th0 = tau0/e
th1 = tau1/e
th_min = th_min_rad * 180/pi # convert to degrees
if th_min > th0:
raise Exception('Error: th_min > th0, violating assumption')
# Total cross section to normalize by
sig = F(pi, e, th_min_rad)
up = u*sig
up = up/DSCS_FACTOR
# Calculate cases (see notebook)
F0 = p0*log(th0/th_min)
F1 = p1/b1*(th0**(-b1) - th1**(-b1))
if up < F0:
g = th_min*exp(up/p0)
elif up < F0 + F1:
g = (th0**(-b1) - b1/p1*(up - F0))**(-1/b1)
else:
g = (th1**(-b2) - b2/p2*(up - F0 - F1))**(-1/b2)
th = g*pi/180.
return th
Grean-McNeal formula (see Ishimoto 1994)
def excitation_xsec(e):
'''
The excitation cross section of the O + O --> O + O(1D) reaction,
as a function of energy, e
INPUT:
e - incident energy, eV
OUTPUT:
sigma - cross section, cm^2
'''
sig_max = 3.8e-18
nu = 1.0
om = 1.0
J = 5.4e3
pre = J**nu/(2*J**(om+nu))
sigma = sig_max/pre * (e**nu/(J**(om+nu) + e**(om+nu)))
# But if e < 1.96 eV, it can't excite
if e < 1.96:
sigma = 0.0
return sigma
e = logspace(0, 6, 1000)
sigma = zeros(len(e))
for i in range(len(e)):
sigma[i] = excitation_xsec(e[i])
loglog(e,sigma,'k-')
xlabel('Energy [eV]');
ylabel('Excitation Cross section [cm$^2$]');
lat = 45
lon = 0
t = datetime(2004,1,1,0) # Solar medium
altvec = linspace(30,1200,1000)
n_O = zeros(len(altvec))
n_N2 = zeros(len(altvec))
n_He = zeros(len(altvec))
n_tot = zeros(len(altvec))
for i in range(len(altvec)):
pt = pyglow.Point(t,lat,lon,altvec[i])
pt.run_msis()
n_O[i] = pt.nn['O']
n_N2[i] = pt.nn['N2']
n_He[i] = pt.nn['HE']
n_tot[i] = sum([pt.nn[x] for x in ['AR','H','HE','N','N2','O','O2']])
semilogx(n_tot,altvec,'k-',linewidth=2,label='total')
semilogx(n_O, altvec,'b-',label='O')
semilogx(n_N2, altvec,'r-', label='N2')
semilogx(n_He, altvec,'g-', label='He')
legend(loc='best')
xlim((1e5,1e12))
xlabel('Neutral density [cm^-3]')
ylabel('Altitude [km]')
<matplotlib.text.Text at 0x7f3735dfa810>
Use total density, but assume that it's all atomic oxygen
density_O = interpolate.InterpolatedUnivariateSpline(altvec*1e3, n_tot)
altvec = linspace(60e3,1000e3,100000)
nO_interp = zeros(len(altvec))
for i in range(len(altvec)):
nO_interp[i] = density_O(altvec[i])
semilogx(nO_interp, altvec/1e3, 'k-')
[<matplotlib.lines.Line2D at 0x7f3735c51410>]
Following notes in notebook on 4 Nov 2015.
Variables/functions used from above:
# Constants
m_O = 15.9994*1.66054e-27 # kg
q = 1.6e-19 # fundamental charge
k = 8.617e-5 # ev/K
#e_thermal = 1.5*k*200 # thermal energy at 200 K
#e_min = 5*e_thermal # stop when particle has less than this energy. Convert to heat.
th_min = 1e-6 # TODO: parameterize as e_tr_min, if it's worth doing so.
e_min = 1.96
# Initialize variables for this particle
e = 1e3 # eV
ru = rand() # randomized initial direction as per:
rv = rand() # http://mathworld.wolfram.com/SpherePointPicking.html
th = arccos(rv-1) # isotropic over downward hemisphere
ph = 2*pi*ru
v_mag = sqrt(2*e*q/m_O)
v = v_mag * array([sin(th)*sin(ph), sin(th)*cos(ph), cos(th)])
r = array([0, 0, 1000e3]) # m/s [x,y,z]
state = 0 # 0 = 3P, 1 = 1D
# Variables to record secondary particles
secondary_count = 0
secondary_v = []
secondary_r = []
secondary_state = []
# For local, thermalized primaries and secondaries, record their energy and altitude
final_e = [] # eV
final_alt = [] # m
# Temporary variables for tracking particles
r_history = []
r_elastic_collision = []
r_excitation_collision = []
t_history = []
steps = 0
t = 0
steps_max = 1e6
stop = False
while(not stop):
# Density and cross sections
e = 0.5*m_O/q*norm(v)**2 # eV
sigma_elastic = elastic_xsec(e, th_min) # cm^2
sigma_excitation = excitation_xsec(e) # cm^2
n_O = density_O(r[2]) # cm^-3
# Mean free path
mfp = 1e-2/(n_O*(sigma_elastic + sigma_excitation)) # m
# Step size
H = 50e3 # scale size, approximate for now
ds = 0.1 * min(H,mfp) # so that step is shorter than the scale size and collisional scale
dt = ds/norm(v) # sec
# Determine if collision happened
pc = 1 - exp(-ds/mfp)
collision = rand() < pc
if collision:
# Determine type of collision
collision_probs = array([sigma_elastic, sigma_excitation])
collision_pmf = collision_probs/sum(collision_probs)
# Randomly sample from the pmf
collision_cdf = cumsum(collision_pmf)
u = rand()
collision_type = sum(u>=collision_cdf)
################## Elastic collision ########################
if collision_type == 0:
# Random scattering angle
u = rand()
chi = elastic_scatt_angle_icdf(e, u, th_min)
if isnan(chi):
print e
print v
raise Exception('blah')
# Rotate to collision frame
v1 = v.copy()
v2 = 0.0 # assume ambient particle is at rest
ez = v1/norm(v1)
ey_tmp = cross(v1, array([0,1,0]))
ey = ey_tmp/norm(ey_tmp)
ex = cross(ey,ez)
A = zeros((3,3))
A[:,0] = ex
A[:,1] = ey
A[:,2] = ez
v1p = A.T.dot(v1-v2)
v2p = 0.0
# Velocity magnitudes after collision
m1 = m_O
m2 = m_O
v1p_mag_new = sqrt(m1**2 + 2*m1*m2*cos(chi) + m2**2)/(m1+m2) * norm(v1p)
v2p_mag_new = 2*m1/(m1+m2) * norm(v1p) * sin(chi/2)
# Scattering angles
th1p = arctan2(m2*sin(chi), m1 + m2*cos(chi))
th2p = (pi-chi)/2
phi1p = 2*pi*rand()
# New velocity vectors
v1p_new = v1p_mag_new * array([sin(th1p)*sin(phi1p), sin(th1p)*cos(phi1p), cos(th1p)])
v2p_new = v2p_mag_new * array([sin(th2p)*sin(phi1p+pi), sin(th2p)*cos(phi1p+pi), cos(th2p)])
v1_new = A.dot(v1p_new) + v2
v2_new = A.dot(v2p_new) + v2
# Record secondary particle for later
e2 = 0.5*m_O/q*norm(v2_new)**2
if e2 < e_min: # No need to trace secondary. Just record its energy.
final_e.append(e2)
final_alt.append(r[2])
else:
secondary_count += 1
secondary_v.append(v2_new)
secondary_r.append(r.copy())
secondary_state.append(0)
# Adjust this particle
v = v1_new
r_elastic_collision.append(r)
################# Excitation collision #######################
elif collision_type == 1:
# TODO: treat this as an elastic collision instead of a forward-scatter collision?
# Determine which particle gets excited
if rand() > 0.5: # Ambient particle
e2 = 0.0 # It is left undisturbed (for now)
if e2 < e_min: # Don't need to trace particle later.
# TODO: record O(1D) local production to be used later in emission calculation
pass
else: # This can't occur, for now
pass
else: # Incident particle
state = 1 # TODO: record information for O(1D) production rate throughout
# No need to record anything about secondary particle
# Subtract excitation energy from incident particle (not ambient particle?)
e -= 1.96 # eV
v_mag_new = sqrt(2*e*q/m_O)
v = v_mag_new * v / norm(v)
r_excitation_collision.append(r)
else:
# Step forward in time
r = r + ds*v/norm(v)
t = t + dt
r_history.append(r)
t_history.append(t)
# Check halting conditions
steps += 1
if r[2] > 1100e3: # Escape flux
stop = True
print 'Particle exited above 1100 km with energy %.1f eV' % e
if e < e_min:
stop = True
print 'Particle thermalized at %.1f km with energy %.2f eV' % (r[2]/1e3, e)
if r[2] < 20e3: # This probably shouldn't happen
stop = True
print 'Particle got to 20 km'
if steps > steps_max:
stop = True
print 'Max steps reached (TEMPORARY)'
print '%i steps' % steps
print '%i elastic collisions' % len(r_elastic_collision)
print '%i excitation collisions' % len(r_excitation_collision)
print '%i secondaries to trace' % secondary_count
print 'Total simulated time: %e sec' % t
Particle thermalized at 278.4 km with energy 1.15 eV 4066 steps 357 elastic collisions 0 excitation collisions 10 secondaries to trace Total simulated time: 3.411231e+01 sec
rh = array(r_history)
plot(rh[:,0]/1e3,rh[:,2]/1e3,'k-',linewidth=0.5) # Particle track
plot(rh[0,0]/1e3,rh[0,2]/1e3,'ko') # Particle start
rhel = array(r_elastic_collision)
plot(rhel[:,0]/1e3,rhel[:,2]/1e3,'k.',markersize=2)
if len(r_excitation_collision) > 0:
rhex = array(r_excitation_collision)
plot(rhex[:,0]/1e3,rhex[:,2]/1e3,'r.')
xlabel('x [km]');
ylabel('z [km]');
xlim((min(rh[:,0]/1e3)-10, max(rh[:,0]/1e3)+10));
ylim((50,1100));
#xlim((-3500,100));
title('Path of incident particle');
def trace_particle(r, v, state, depth, th_min = 1e-9, e_min = 1.96, max_depth = np.inf, verbose = True, steps_max=1e6):
'''
Recursive function to trace a single particle. It calls itself to trace its secondaries.
'''
# For local, thermalized primaries and secondaries, record their energy and altitude
final_e = [] # eV
final_alt = [] # m
# For tracking O(1D) production
excited_alt = []
excited_v = []
# Temporary variables for tracking this particle
r_history = []
r_elastic_collision = []
r_excitation_collision = []
t_history = []
# For tracking secondary particle locations:
r_histories = []
secondary_particles = 0
steps = 0
t = 0
stop = False
while(not stop):
# Density and cross sections
e = 0.5*m_O/q*norm(v)**2 # eV
sigma_elastic = elastic_xsec(e, th_min) # cm^2
sigma_excitation = excitation_xsec(e) # cm^2
n_O = density_O(r[2]) # cm^-3
# Mean free path
mfp = 1e-2/(n_O*(sigma_elastic + sigma_excitation)) # m
# Step size
H = 50e3 # scale size, approximate for now
ds = 0.1 * min(H,mfp) # so that step is shorter than the scale size and collisional scale
dt = ds/norm(v) # sec
# Determine if collision happened
pc = 1 - exp(-ds/mfp)
collision = rand() < pc
if collision:
# Determine type of collision
collision_probs = array([sigma_elastic, sigma_excitation])
collision_pmf = collision_probs/sum(collision_probs)
# Randomly sample from the pmf
collision_cdf = cumsum(collision_pmf)
u = rand()
collision_type = sum(u>=collision_cdf)
################## Elastic collision ########################
if collision_type == 0:
# Random scattering angle
u = rand()
chi = elastic_scatt_angle_icdf(e, u, th_min)
# Rotate to collision frame
v1 = v.copy()
v2 = 0.0 # assume ambient particle is at rest
ez = v1/norm(v1)
ey_tmp = cross(v1, array([0,1,0]))
ey = ey_tmp/norm(ey_tmp)
ex = cross(ey,ez)
A = zeros((3,3))
A[:,0] = ex
A[:,1] = ey
A[:,2] = ez
v1p = A.T.dot(v1-v2)
v2p = 0.0
# Velocity magnitudes after collision
m1 = m_O
m2 = m_O
v1p_mag_new = sqrt(m1**2 + 2*m1*m2*cos(chi) + m2**2)/(m1+m2) * norm(v1p)
v2p_mag_new = 2*m1/(m1+m2) * norm(v1p) * sin(chi/2)
# Scattering angles
th1p = arctan2(m2*sin(chi), m1 + m2*cos(chi))
th2p = (pi-chi)/2
phi1p = 2*pi*rand()
# New velocity vectors
v1p_new = v1p_mag_new * array([sin(th1p)*sin(phi1p), sin(th1p)*cos(phi1p), cos(th1p)])
v2p_new = v2p_mag_new * array([sin(th2p)*sin(phi1p+pi), sin(th2p)*cos(phi1p+pi), cos(th2p)])
v1_new = A.dot(v1p_new) + v2
v2_new = A.dot(v2p_new) + v2
# Record secondary particle for later
e2 = 0.5*m_O/q*norm(v2_new)**2
if e2 < e_min: # No need to trace secondary. Just record its energy.
final_e.append(e2)
final_alt.append(r[2])
elif depth >= max_depth: # Just record final quantities and move on
final_e.append(e2)
final_alt.append(r[2])
else: # Trace all secondaries
fe,fa,ea,ev,rh = trace_particle(r, v2_new, 0, depth+1, th_min=th_min, e_min=e_min,
max_depth=max_depth, verbose=verbose)
r_histories.extend(rh)
final_e.extend(fe)
final_alt.extend(fa)
excited_alt.extend(ea)
excited_v.extend(ev)
secondary_particles += 1
# Adjust this particle
v = v1_new
r_elastic_collision.append(r)
################# Excitation collision #######################
elif collision_type == 1:
# TODO: treat this as an elastic collision instead of a forward-scatter collision?
# Determine which particle gets excited
if rand() > 0.5: # Ambient particle
e2 = 0.0 # It is left undisturbed (for now)
if e2 < e_min: # Don't need to trace particle later.
# TODO: record O(1D) local production to be used later in emission calculation
excited_alt.append(r[2])
excited_v.append(array([0,0,0]))
else: # This can't occur, for now
pass
else: # Incident particle
# Record information for O(1D) production rate. No need to record secondary particle
state = 1 # TODO: record information for O(1D) production rate throughout
excited_alt.append(r[2])
excited_v.append(v)
# Subtract excitation energy from incident particle (not ambient particle?)
e -= 1.96 # eV
v_mag_new = sqrt(2*e*q/m_O)
v = v_mag_new * v / norm(v)
r_excitation_collision.append(r)
else:
# Step forward in time
r = r + ds*v/norm(v)
t = t + dt
r_history.append(r)
t_history.append(t)
# Check halting conditions
steps += 1
if r[2] > 1100e3: # Escape flux
stop = True
if verbose:
print 'Particle exited above 1100 km with energy %.1f eV' % e
if e < e_min:
stop = True
if verbose:
print 'Particle thermalized at %.1f km with energy %.2f eV' % (r[2]/1e3, e)
if steps > steps_max:
stop = True
if verbose:
print 'Max steps reached (TEMPORARY)'
# Concatenate this path with the secondaries
r_histories.append(r_history)
final_e.append(e)
final_alt.append(r[2])
if verbose:
print '\t%i Depth level' % depth
print '\t%i steps' % steps
print '\t%i elastic collisions' % len(r_elastic_collision)
print '\t%i excitation collisions' % len(excited_alt)
print '\t%i secondaries to trace' % secondary_particles
return final_e, final_alt, excited_alt, excited_v, r_histories
# Use monoenergetic flux
e_0 = 1e3 # eV
N_particles = 50
max_depth = np.inf
th_min = 1e-6
e_min = 1.96
verbose = False
steps_max = 1e6
final_e_all = []
final_alt_all = []
excited_alt_all = []
excited_v_all = []
t0 = datetime.now()
for i in range(N_particles):
# Initialize variables for this particle
e = e_0
ru = rand() # randomized initial direction as per:
rv = rand() # http://mathworld.wolfram.com/SpherePointPicking.html
th = arccos(rv-1) # isotropic over downward hemisphere
ph = 2*pi*ru
v_mag = sqrt(2*e*q/m_O)
v = v_mag * array([sin(th)*sin(ph), sin(th)*cos(ph), cos(th)])
r = array([0, 0, 1000e3]) # m/s [x,y,z]
state = 0 # 0 = 3P, 1 = 1D
final_e, final_alt, excited_alt, excited_v, rh_all = trace_particle(r,v,state,0, th_min, e_min,
max_depth, verbose, steps_max)
final_e_all.extend(final_e)
final_alt_all.extend(final_alt)
excited_alt_all.extend(excited_alt)
excited_v_all.extend(excited_v)
if not verbose: # if no detailed output, at least output particle number:
clear_output(wait=True)
time.sleep(0.1)
print '%03i/%03i' % (i+1, N_particles)
sys.stdout.flush()
t1 = datetime.now()
050/050
t1 = datetime.now()
N_particles = i+1 # However many were actually run
t = (t1-t0).total_seconds()
t_per = t/N_particles
print '%f sec per particle' % t_per
print '%f hours for N=1e3' % (t_per*1e3/3600)
print '%f days for N=1e6' % (t_per*1e6/86400)
6.169289 sec per particle 1.713691 hours for N=1e3 71.403809 days for N=1e6
final_e = array(final_e_all)
final_alt = array(final_alt_all)
excited_alt = array(excited_alt_all)
excited_v = array(excited_v_all)
rh_all = array(rh_all)
figure(figsize=(5,5))
rh = array(rh_all[-1])
# Create subsampled track
s = 10
idx = range(0,shape(rh)[0],s) + [-1]
plot(rh[idx,0]/1e3,rh[idx,2]/1e3,'k-',linewidth=2) # Particle track
for i in range(len(rh_all)-1):
#for i in range(2):
rh = array(rh_all[i])
if len(rh) > 0:
idx = range(0,shape(rh)[0],s) + [-1]
plot(rh[idx,0]/1e3,rh[idx,2]/1e3,'-',linewidth=0.5) # Particle track
title('Example Path')
xlabel('x [km]')
ylabel('z [km]')
#xlim((min(rh[:,0]/1e3)-10, max(rh[:,0]/1e3)+10));
ylim((75,1100))
clear_output()
#xlim((350,360));
#mpld3.display()
figure(figsize=(10,10))
rh = array(rh_all[-1])
plot(rh[:,0]/1e3,rh[:,2]/1e3,'k-',linewidth=2) # Particle track
xfinal = rh[-1,0]
zfinal = rh[-1,2]
for i in range(len(rh_all)-1):
rh = array(rh_all[i])
if len(rh) > 0:
plot(rh[:,0]/1e3,rh[:,2]/1e3,'-',linewidth=0.5) # Particle track
rh = array(rh_all[-1])
plot(rh[:,0]/1e3,rh[:,2]/1e3,'k-',linewidth=2) # Particle track
xlabel('x [km]');
ylabel('z [km]');
#xlim((min(rh[:,0]/1e3)-10, max(rh[:,0]/1e3)+10))
#ylim((100,300));
ylim((zfinal/1e3-100,zfinal/1e3+100))
xlim((xfinal/1e3-100,xfinal/1e3+100));
print '%i secondaries' % len(rh_all)
414 secondaries
idx_esc = (final_alt >= 1100e3)
e_tot = sum(final_e) + 1.96*len(excited_alt)
e_esc = sum(final_e[idx_esc])
e_heat = sum(final_e[~idx_esc])
e_exc = 1.96*len(excited_alt)
#print 'Escape energy: %f eV (%.2f %%)' % (e_esc, 100*e_esc/e_tot)
#print 'Heat energy: %f eV (%.2f %%)' % (e_heat, 100*e_heat/e_tot)
#print 'Excitation energy: %f eV (%.2f %%)' % (e_exc, 100*e_exc/e_tot)
#print 'Total energy: %f eV' % (e_tot)
print 'Energy partition for %.0f keV monoenergetic flux' % (e_0/1e3)
print '\tPrimary simulated particles: %i' % N_particles
print '\tTotal simulated particles: %i' % len(final_e)
print ''
print ' My model Ishimoto et al. 1992'
print 'Heat: %05.2f %% 78 %%' % (100*e_heat/e_tot)
print 'Escape: %05.2f %% 17 %%' % (100*e_esc/e_tot)
print 'Ionization: 5 %'
print '1D Excitation: %05.2f %% ' % (100*e_exc/e_tot)
Energy partition for 1 keV monoenergetic flux Primary simulated particles: 5 Total simulated particles: 30222 My model Ishimoto et al. 1992 Heat: 72.48 % 78 % Escape: 27.49 % 17 % Ionization: 5 % 1D Excitation: 00.04 %
alt_bin_edge = logspace(log10(80e3),log10(1100e3),200)
alt = (alt_bin_edge[1:] + alt_bin_edge[:-1])/2
e_dep = zeros(len(alt))
for i in range(len(alt)):
idx = (final_alt < alt_bin_edge[i+1]) & (final_alt >= alt_bin_edge[i])
e_dep[i] = sum(final_e[idx])
# Scale to a 1 erg/cm^3/s source
e_model = e_0*N_particles # particle energy * number of particles
d_alt = alt_bin_edge[1:]-alt_bin_edge[:-1]
heat_rate = e_dep/d_alt*6.2415e11/e_model*1e-2 # eV/cm^3/s. See notes.
#heat_rate[heat_rate==0.0] = 1e-12 # because
semilogx(heat_rate,alt/1e3,'k-')
xlabel('Heating rate [eV/cm^3/s]')
ylabel('Altitude [km]')
<matplotlib.text.Text at 0x7f37341bb610>
# Extracted from Ishimoto 1992 using: http://arohatgi.info/WebPlotDigitizer/
# 20 keV
xy = array([
1.05103e+1, 4.78217e+2,
1.37053e+1, 4.63678e+2,
1.97419e+1, 4.48222e+2,
3.08968e+1, 4.30946e+2,
3.70821e+1, 4.23672e+2,
6.73799e+1, 4.01846e+2,
1.14572e+2, 3.83655e+2,
2.29970e+2, 3.54562e+2,
4.93269e+2, 3.24557e+2,
8.38747e+2, 3.04551e+2,
1.09372e+3, 2.91826e+2,
2.12368e+3, 2.69088e+2,
3.85883e+3, 2.46355e+2,
6.56150e+3, 2.28164e+2,
9.60968e+3, 2.14522e+2,
1.38424e+4, 2.00882e+2,
2.27692e+4, 1.83601e+2,
3.33468e+4, 1.70867e+2,
4.27685e+4, 1.62680e+2,
4.96553e+4, 1.57224e+2,
5.57696e+4, 1.51770e+2,
6.16063e+4, 1.45410e+2,
6.16063e+4, 1.39058e+2,
5.67025e+4, 1.34527e+2,
4.88383e+4, 1.31817e+2,
4.06921e+4, 1.28202e+2,
2.96908e+4, 1.26412e+2,
2.09568e+4, 1.23717e+2,
1.31703e+4, 1.21031e+2,
6.78285e+3, 1.18361e+2,
3.32364e+3, 1.17510e+2,
1.89085e+3, 1.15739e+2,
1.05802e+3, 1.13970e+2,
6.22226e+2, 1.14012e+2,
3.91038e+2, 1.12234e+2,
1.67797e+2, 1.09578e+2,
4.23449e+1, 1.10594e+2,
1.00000e+1, 1.09800e+2,
])
# Extracted from Ishimoto 1992 using: http://arohatgi.info/WebPlotDigitizer/
# 1 keV
xy = array([
4.69239e+2, 4.96767e+2,
5.88666e+2, 4.85321e+2,
9.26287e+2, 4.67719e+2,
1.82851e+3, 4.38671e+2,
4.38347e+3, 4.02582e+2,
8.93747e+3, 3.73536e+2,
1.79319e+4, 3.41844e+2,
2.56061e+4, 3.25998e+2,
3.26487e+4, 3.12789e+2,
3.83896e+4, 3.03983e+2,
4.30026e+4, 2.95173e+2,
4.73946e+4, 2.88126e+2,
4.97631e+4, 2.80193e+2,
4.89789e+4, 2.71374e+2,
4.66716e+4, 2.63434e+2,
3.84510e+4, 2.54601e+2,
2.87493e+4, 2.45760e+2,
2.18446e+4, 2.38684e+2,
1.26086e+4, 2.32469e+2,
6.39475e+3, 2.25363e+2,
2.84962e+3, 2.19128e+2,
1.02916e+3, 2.11113e+2,
3.65718e+2, 2.03979e+2,
1.43180e+2, 2.01262e+2,
4.84675e+1, 1.95888e+2,
1.06024e+1, 1.91363e+2,
])
alt_i92 = xy[1::2]
heat_rate_i92 = xy[0::2]
semilogx(heat_rate,alt/1e3,'k-', label='My model')
semilogx(heat_rate_i92, alt_i92, 'g-', linewidth=2, label='Ishimoto92')
legend(loc='best');
xlim((1e0, 1e6));
ylim((0,600));
xlabel('Heating rate [eV/cm^3/s]');
ylabel('Altitude [km]');
print ' My model Ishimoto et al. 1992'
print 'Peak Heating Altitude %.0f km %.0f km' % \
(alt[argmax(heat_rate)]/1e3, alt_i92[argmax(heat_rate_i92)])
My model Ishimoto et al. 1992 Peak Heating Altitude 222 km 280 km
alt_bin_edge = logspace(log10(80e3),log10(1100e3),100)
alt = (alt_bin_edge[1:] + alt_bin_edge[:-1])/2
excitation_counts = zeros(len(alt))
for i in range(len(alt)):
idx = (excited_alt < alt_bin_edge[i+1]) & (excited_alt >= alt_bin_edge[i])
excitation_counts[i] = sum(idx)
# Scale to a 1 erg/cm^3/s source
e_model = e_0*N_particles # particle energy * number of particles
d_alt = alt_bin_edge[1:]-alt_bin_edge[:-1]
prod_rate = excitation_counts/d_alt*6.2415e11/e_model*1e-2 # cm^-3 s^-1. See notes.
#heat_rate[heat_rate==0.0] = 1e-12 # because
semilogx(prod_rate,alt/1e3,'k.-',markersize=2)
xlabel('$O(^1\!D)$ production rate [$cm^{-3} s^{-1}$]');
ylabel('Altitude [km]');
brightness_R = sum(prod_rate*d_alt*1e2)*1e4*1e-10
print 'Column brightness if all of those O(1D) immediately emitted: %f R' % brightness_R
Column brightness if all of those O(1D) immediately emitted: 87.131340 R
import subprocess
import shutil
nb_name = 'Model_v01.ipynb' # There's probably a way to get this programmatically
cmd = ['ipython', 'nbconvert', '--to', 'slides', nb_name, \
'--reveal-prefix', '"http://cdn.jsdelivr.net/reveal.js/2.6.2"',\
'--template','output_toggle']
# The --template command points to a custom template which opens and closes the input code cells with a click
subprocess.call(cmd)
html_name = '%s.slides.html' % (nb_name.split('.')[0])
shutil.copy(html_name, '/home/bhardin2/public_html/slideshows/')