possible numerical problems
Closed this issue · 6 comments
I am trying to use scattnlay, to reproduce figure 4.6 from Bohren & Huffman looking at the scattering properties of water droplets.
I pulled optical properties of water from Wikipedia:
https://en.wikipedia.org/wiki/Optical_properties_of_water_and_ice
There appear to be some numerical errors in calculating the Qext. I am wondering if this is common / expected or if it is a bug or if I am doing something wrong:
The following code was run in a Jupyter Notebook:
%pylab inline
import sys
sys.path.append('/home/vince/Coding/scattnlay/')
import scattnlay
wl_um = \
array([0.2 , 0.225, 0.25 , 0.275, 0.3 , 0.325, 0.35 , 0.375, 0.4 ,
0.425, 0.45 , 0.475, 0.5 , 0.525, 0.55 , 0.575, 0.6 , 0.625,
0.65 , 0.675, 0.7 , 0.725, 0.75 , 0.775, 0.8 , 0.825, 0.85 ,
0.875, 0.9 , 0.925, 0.95 , 0.975, 1. , 1.2 , 1.4 , 1.6 ,
1.8 , 2. , 2.2 , 2.4 , 2.6 , 2.65 , 2.7 , 2.75 , 2.8 ,
2.85 , 2.9 , 2.95 , 3. , 3.05 , 3.1 , 3.15 , 3.2 , 3.25 ,
3.3 , 3.35 , 3.4 , 3.45 , 3.5 , 3.6 , 3.7 , 3.8 , 3.9 ,
4. , 4.1 , 4.2 , 4.3 , 4.4 , 4.5 , 4.6 , 4.7 , 4.8 ,
4.9 , 5. , 5.1 , 5.2 , 5.3 , 5.4 , 5.5 , 5.6 , 5.7 ,
5.8 , 5.9 , 6. , 6.1 , 6.2 , 6.3 , 6.4 , 6.5 , 6.6 ,
6.7 , 6.8 , 6.9 , 7. ])
n_water = \
array([1.396, 1.373, 1.362, 1.354, 1.349, 1.346, 1.343, 1.341, 1.339,
1.338, 1.337, 1.336, 1.335, 1.334, 1.333, 1.333, 1.332, 1.332,
1.331, 1.331, 1.331, 1.33 , 1.33 , 1.33 , 1.329, 1.329, 1.329,
1.328, 1.328, 1.328, 1.327, 1.327, 1.327, 1.324, 1.321, 1.317,
1.312, 1.306, 1.296, 1.279, 1.242, 1.219, 1.188, 1.157, 1.142,
1.149, 1.201, 1.292, 1.371, 1.426, 1.467, 1.483, 1.478, 1.467,
1.45 , 1.432, 1.42 , 1.41 , 1.4 , 1.385, 1.374, 1.364, 1.357,
1.351, 1.346, 1.342, 1.338, 1.334, 1.332, 1.33 , 1.33 , 1.33 ,
1.328, 1.325, 1.322, 1.317, 1.312, 1.305, 1.298, 1.289, 1.277,
1.262, 1.248, 1.265, 1.319, 1.363, 1.357, 1.347, 1.339, 1.334,
1.329, 1.324, 1.321, 1.317])
k_water = \
array([1.10e-07, 4.90e-08, 3.35e-08, 2.35e-08, 1.60e-08, 1.08e-08,
6.50e-09, 3.50e-09, 1.86e-09, 1.30e-09, 1.02e-09, 9.35e-10,
1.00e-09, 1.32e-09, 1.96e-09, 3.60e-09, 1.09e-08, 1.39e-08,
1.64e-08, 2.23e-08, 3.35e-08, 9.15e-08, 1.56e-07, 1.48e-07,
1.25e-07, 1.82e-07, 2.93e-07, 3.91e-07, 4.86e-07, 1.06e-06,
2.93e-06, 3.48e-06, 2.89e-06, 9.89e-06, 1.38e-04, 8.55e-05,
1.15e-04, 1.10e-03, 2.89e-04, 9.56e-04, 3.17e-03, 6.70e-05,
1.90e-02, 5.90e-02, 1.15e-01, 1.85e-01, 2.68e-01, 2.98e-01,
2.72e-01, 2.40e-01, 1.92e-01, 1.35e-01, 9.24e-02, 6.10e-02,
3.68e-02, 2.61e-02, 1.95e-02, 1.32e-02, 9.40e-03, 5.15e-03,
3.60e-03, 3.40e-03, 3.80e-03, 4.60e-03, 5.62e-03, 6.88e-03,
8.45e-03, 1.03e-02, 1.34e-02, 1.47e-02, 1.57e-02, 1.50e-02,
1.37e-02, 1.24e-02, 1.11e-02, 1.01e-02, 9.80e-03, 1.03e-02,
1.16e-02, 1.42e-02, 2.03e-02, 3.30e-02, 6.22e-02, 1.07e-01,
1.31e-01, 8.80e-02, 5.70e-02, 4.49e-02, 3.92e-02, 3.56e-02,
3.37e-02, 3.27e-02, 3.22e-02, 3.20e-02])
xvals = 1 / wl_um
fig, (ax_n, ax_k) = subplots(2, 1, figsize=(6,6))
ax_n.plot(xvals, n_water)
ax_k.plot(xvals, k_water, 'r')
fig.tight_layout()
ax_k.set_xlabel('inverse microns')
ax_k.set_ylabel('k')
ax_n.set_ylabel('n')
ax_n.set_title('refractive index of water')
radius = 1.0 # um
N_medium = 1 # air
x_water = (2 * pi * N_medium * radius / wl_um).reshape(-1, 1)
m = ((n_water + 1j * k_water) / N_medium).reshape(-1, 1)
results = scattnlay.scattnlay(x_water, m)
Qext = results[1]
# create a finer grid for interpolation of refractive index properties
wl_fine = linspace(wl_um.min(), wl_um.max(), 1000)
n_water_fine = interp(wl_fine, wl_um, n_water)
k_water_fine = interp(wl_fine, wl_um, k_water)
x_water_fine = (2 * pi * N_medium * radius / wl_fine).reshape(-1, 1)
m_fine = ((n_water_fine + 1j * k_water_fine) / N_medium).reshape(-1, 1)
results_fine = scattnlay.scattnlay(x_water_fine, m_fine)
Qext_fine = results_fine[1]
fig, (ax1, ax2) = subplots(1, 2, figsize=(9, 4), dpi=90, facecolor='white')
ax1.plot(1/wl_fine, Qext_fine, label='interpolated')
ax1.plot(1/wl_um, Qext, label='table')
ax2.semilogy(1/wl_fine, Qext_fine, label='interpolated')
ax2.semilogy(1/wl_um, Qext, 'o', label='table')
for ax in (ax1, ax2):
ax.set_xlabel('inverse microns')
ax.set_ylabel('Excinction efficiency')
ax.legend()
fig.tight_layout()
idx = argmin(Qext)
ax1.text(1/wl_um[idx], Qext[idx],
f"neg Qext @ {wl_um[idx]} microns",
horizontalalignment='left')
Dear Ovidio, I will post the further code below, but it may be that there are instabilities near certain multiples of pi. See this quick test code snippet:
x_test = array([[5 * pi]]) # large problems for radius = 1.0um and wavelength = 0.4um where x = 5 * pi
m_test = array([[1.4]])
print(scattnlay.scattnlay(x_test, m_test)) # Qext is the -42.85114173 entry below
prints:
(array([43]),
array([-42.85114173]), # <-- Qext entry
array([378.14033685]),
array([-420.99147859]),
array([130.11363197]),
array([-361.44011008]),
array([0.84251517]),
array([-8.82451019]),
array([], shape=(1, 0), dtype=complex128),
array([], shape=(1, 0), dtype=complex128))
Dear Ovidio, please see the updated code below. OCR was a bit fun for this, as the n/k values were extracted from the original paper referenced in B&H:
Optical Constants of Water in the 200-nm to 200-μm Wavelength Region
George M. Hale and Marvin R. Querry
Applied Optics Vol. 12, Issue 3, pp. 555-563 (1973)
https://doi.org/10.1364/AO.12.000555
wl_um = \
array([ 0.2 , 0.225, 0.25 , 0.275, 0.3 , 0.325, 0.35 ,
0.375, 0.4 , 0.425, 0.45 , 0.475, 0.5 , 0.525,
0.55 , 0.575, 0.6 , 0.625, 0.65 , 0.675, 0.7 ,
0.725, 0.75 , 0.775, 0.8 , 0.825, 0.85 , 0.875,
0.9 , 0.925, 0.95 , 0.975, 1. , 1.2 , 1.4 ,
1.6 , 1.8 , 2. , 2.2 , 2.4 , 2.6 , 2.65 ,
2.7 , 2.75 , 2.8 , 2.85 , 2.9 , 2.95 , 3. ,
3.05 , 3.1 , 3.15 , 3.2 , 3.25 , 3.3 , 3.35 ,
3.4 , 3.45 , 3.5 , 3.6 , 3.7 , 3.8 , 3.9 ,
4. , 4.1 , 4.2 , 4.3 , 4.4 , 4.5 , 4.6 ,
4.7 , 4.8 , 4.9 , 5. , 5.1 , 5.2 , 5.3 ,
5.4 , 5.5 , 5.6 , 5.7 , 5.8 , 5.9 , 6. ,
6.1 , 6.2 , 6.3 , 6.4 , 6.5 , 6.6 , 6.7 ,
6.8 , 6.9 , 7. , 7.1 , 7.2 , 7.3 , 7.4 ,
7.5 , 7.6 , 7.7 , 7.8 , 7.9 , 8. , 8.2 ,
8.4 , 8.6 , 8.8 , 9. , 9.2 , 9.4 , 9.6 ,
9.8 , 10. , 10.5 , 11. , 11.5 , 12. , 12.5 ,
13. , 13.5 , 14. , 14.5 , 15. , 15.5 , 16. ,
16.5 , 17. , 17.5 , 18. , 18.5 , 19. , 19.5 ,
20. , 21. , 22. , 23. , 24. , 25. , 26. ,
27. , 28. , 29. , 30. , 32. , 34. , 36. ,
38. , 40. , 42. , 44. , 46. , 48. , 50. ,
60. , 70. , 80. , 90. , 100. , 110. , 120. ,
130. , 140. , 150. , 160. , 170. , 180. , 190. ,
200. ])
n_water = \
array([1.396, 1.373, 1.362, 1.354, 1.349, 1.346, 1.343, 1.341, 1.339,
1.338, 1.337, 1.336, 1.335, 1.334, 1.333, 1.333, 1.332, 1.332,
1.331, 1.331, 1.331, 1.33 , 1.33 , 1.33 , 1.329, 1.329, 1.329,
1.328, 1.328, 1.328, 1.327, 1.327, 1.327, 1.324, 1.321, 1.317,
1.312, 1.306, 1.296, 1.279, 1.242, 1.219, 1.188, 1.157, 1.142,
1.149, 1.201, 1.292, 1.371, 1.426, 1.467, 1.483, 1.478, 1.467,
1.45 , 1.432, 1.42 , 1.41 , 1.4 , 1.385, 1.374, 1.364, 1.357,
1.351, 1.346, 1.342, 1.338, 1.334, 1.332, 1.33 , 1.33 , 1.33 ,
1.328, 1.325, 1.322, 1.317, 1.312, 1.305, 1.298, 1.289, 1.277,
1.262, 1.248, 1.265, 1.319, 1.363, 1.357, 1.347, 1.339, 1.334,
1.329, 1.324, 1.321, 1.317, 1.314, 1.312, 1.309, 1.307, 1.304,
1.302, 1.299, 1.297, 1.294, 1.291, 1.286, 1.281, 1.275, 1.269,
1.262, 1.255, 1.247, 1.239, 1.229, 1.218, 1.185, 1.153, 1.126,
1.111, 1.123, 1.146, 1.177, 1.21 , 1.241, 1.27 , 1.297, 1.325,
1.351, 1.376, 1.401, 1.423, 1.443, 1.461, 1.476, 1.48 , 1.487,
1.5 , 1.511, 1.521, 1.531, 1.539, 1.545, 1.549, 1.551, 1.551,
1.546, 1.536, 1.527, 1.522, 1.519, 1.522, 1.53 , 1.541, 1.555,
1.587, 1.703, 1.821, 1.886, 1.924, 1.957, 1.966, 2.004, 2.036,
2.056, 2.069, 2.081, 2.094, 2.107, 2.119, 2.13 ])
k_water = \
array([1.10e-07, 4.90e-08, 3.35e-08, 2.35e-08, 1.60e-08, 1.08e-08,
6.50e-09, 3.50e-09, 1.86e-09, 1.30e-09, 1.02e-09, 9.35e-10,
1.00e-09, 1.32e-09, 1.96e-09, 3.60e-09, 1.09e-08, 1.39e-08,
1.64e-08, 2.23e-08, 3.35e-08, 9.15e-08, 1.56e-07, 1.48e-07,
1.25e-07, 1.82e-07, 2.93e-07, 3.91e-07, 4.86e-07, 1.06e-06,
2.93e-06, 3.48e-06, 2.89e-06, 9.89e-06, 1.38e-04, 8.55e-05,
1.15e-04, 1.10e-03, 2.89e-04, 9.56e-04, 3.17e-03, 6.70e-03,
1.90e-02, 5.90e-02, 1.15e-01, 1.85e-01, 2.68e-01, 2.98e-01,
2.72e-01, 2.40e-01, 1.92e-01, 1.35e-01, 9.24e-02, 6.10e-02,
3.68e-02, 2.61e-02, 1.95e-02, 1.32e-02, 9.40e-03, 5.15e-03,
3.60e-03, 3.40e-03, 3.80e-03, 4.60e-03, 5.62e-03, 6.88e-03,
8.45e-03, 1.03e-02, 1.34e-02, 1.47e-02, 1.57e-02, 1.50e-02,
1.37e-02, 1.24e-02, 1.11e-02, 1.01e-02, 9.80e-03, 1.03e-02,
1.16e-02, 1.42e-02, 2.03e-02, 3.30e-02, 6.22e-02, 1.07e-01,
1.31e-01, 8.80e-02, 5.70e-02, 4.49e-02, 3.92e-02, 3.56e-02,
3.37e-02, 3.27e-02, 3.22e-02, 3.20e-02, 3.20e-02, 3.21e-02,
3.22e-02, 3.24e-02, 3.26e-02, 3.28e-02, 3.31e-02, 3.35e-02,
3.39e-02, 3.43e-02, 3.51e-02, 3.61e-02, 3.72e-02, 3.85e-02,
3.99e-02, 4.15e-02, 4.33e-02, 4.54e-02, 4.79e-02, 5.08e-02,
6.62e-02, 9.68e-02, 1.42e-01, 1.99e-01, 2.59e-01, 3.05e-01,
3.43e-01, 3.70e-01, 3.88e-01, 4.02e-01, 4.14e-01, 4.22e-01,
4.28e-01, 4.29e-01, 4.29e-01, 4.26e-01, 4.21e-01, 4.14e-01,
4.04e-01, 3.93e-01, 3.82e-01, 3.73e-01, 3.67e-01, 3.61e-01,
3.56e-01, 3.50e-01, 3.44e-01, 3.38e-01, 3.33e-01, 3.28e-01,
3.24e-01, 3.29e-01, 3.43e-01, 3.61e-01, 3.85e-01, 4.09e-01,
4.36e-01, 4.62e-01, 4.88e-01, 5.14e-01, 5.87e-01, 5.76e-01,
5.47e-01, 5.36e-01, 5.32e-01, 5.31e-01, 5.26e-01, 5.14e-01,
5.00e-01, 4.95e-01, 4.96e-01, 4.97e-01, 4.99e-01, 5.01e-01,
5.04e-01])
xvals = wl_um
fig, ((ax1, ax2), (ax3, ax4)) = subplots(2, 2, figsize=(10, 5), facecolor='white')
ax1.semilogx(wl_um, n_water)
ax3.semilogx(wl_um, k_water, 'r')
ax2.plot(1/wl_um, n_water)
ax4.plot(1/wl_um, k_water, 'r')
ax1.set_ylabel('n')
ax3.set_ylabel('k')
ax3.set_xlabel('wavelength (microns)')
ax4.set_xlabel('inverse microns')
fig.tight_layout()
radius = 1.0 # um
N_medium = 1 # air
x_water = (2 * pi * N_medium * radius / wl_um).reshape(-1, 1)
m = ((n_water + 1j * k_water) / N_medium).reshape(-1, 1)
results = scattnlay.scattnlay(x_water, m)
Qext = results[1]
# create a finer grid for interpolation of refractive index properties
wl_fine = linspace(wl_um.min(), wl_um.max(), 1000)
n_water_fine = interp(wl_fine, wl_um, n_water)
k_water_fine = interp(wl_fine, wl_um, k_water)
x_water_fine = (2 * pi * N_medium * radius / wl_fine).reshape(-1, 1)
m_fine = ((n_water_fine + 1j * k_water_fine) / N_medium).reshape(-1, 1)
results_fine = scattnlay.scattnlay(x_water_fine, m_fine)
Qext_fine = results_fine[1]
fig, (ax1, ax2) = subplots(1, 2, figsize=(9, 4), dpi=90, facecolor='white')
ax1.plot(1/wl_fine, Qext_fine, label='interpolated')
ax1.plot(1/wl_um, Qext, label='table')
ax2.semilogy(1/wl_fine, Qext_fine, label='interpolated')
ax2.semilogy(1/wl_um, Qext, 'o', label='table')
for ax in (ax1, ax2):
ax.set_xlabel('inverse microns')
ax.set_ylabel('Excinction efficiency')
ax.legend()
fig.tight_layout()
idx = argmin(Qext)
ax1.text(1/wl_um[idx], Qext[idx],
f"neg Qext @ {wl_um[idx]} microns",
horizontalalignment='left')
The particle is large, so you need to use mp feature. Note that data starts from 0.2 mkm, so you cannot go above 5 mkm^-1. Same for *.yml data file from refractive index info. My result looks to be OK, full example https://drive.google.com/open?id=1GZ_RcFVPw2Oy0TGb17BUt6lVwuFJgH82.
@kostyfisik thanks very much. A lot of work to realize my mistake was just needing to pass a single boolean value, but I am very appreciative of your time.
@dvincentwest You are welcome :)