Skip to content

Commit

Permalink
1.4.0 Upgrade
Browse files Browse the repository at this point in the history
Added Shapely Support
  • Loading branch information
bsumlin committed Jan 25, 2018
1 parent 21703de commit 8cb263f
Show file tree
Hide file tree
Showing 12 changed files with 60 additions and 149 deletions.
4 changes: 2 additions & 2 deletions PyMieScatt.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
Metadata-Version: 1.1
Name: PyMieScatt
Version: 1.3.7
Version: 1.4.0
Summary: A collection of forward and inverse Mie solving routines based on Bohren and Huffman's Mie Theory derivations.
Home-page: http://air.eece.wustl.edu/people/ben-sumlin/
Author: Benjamin Sumlin
Author-email: bsumlin@wustl.edu
License: GPL
Description-Content-Type: UNKNOWN
Description: 1.3.7 - Added error bounds as an option for the graphical inversion method. Added new automatic inversion methods Inversion() and Inversion_SD(). Significantly improved the iterative methods.
Description: 1.4.0 - Added error bounds as an option for the graphical inversion method. Added new automatic inversion methods Inversion() and Inversion_SD(). Significantly improved the iterative methods.
Docs are hosted at `ReadTheDocs <http://pymiescatt.readthedocs.io/>`_.
Keywords: Mie Rayleigh scattering absorption extinction light refraction
Platform: UNKNOWN
Expand Down
1 change: 1 addition & 0 deletions PyMieScatt.egg-info/requires.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
numpy
scipy
matplotlib
shapely
91 changes: 22 additions & 69 deletions PyMieScatt/Inverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from matplotlib.offsetbox import AnchoredText
from scipy.ndimage import zoom
from scipy.integrate import trapz
from shapely import geometry
import warnings

def coerceDType(d):
Expand Down Expand Up @@ -161,31 +162,10 @@ def ContourIntersection(Qsca,Qabs,wavelength,diameter,Qback=None,nMin=1,nMax=3,k
ax.contourf(n,k,QbackList,backErrorLevels,origin='lower',colors=('green'),alpha=0.15)
ax.contour(n,k,QbackList,backErrorLevels,origin='lower',linewidths=0.5,colors=('green'),alpha=0.5)

distinctScaPaths = len(scaChart.collections[0].get_paths())
distinctAbsPaths = len(absChart.collections[0].get_paths())
if Qback is not None:
distinctBackPaths = len(backChart.collections[0].get_paths())
scaVertices = []
absVertices = []
if Qback is not None:
backVertices = []
for i in range(distinctScaPaths):
scaVertices.append(scaChart.collections[0].get_paths()[i].vertices)
for i in range(distinctAbsPaths):
absVertices.append(absChart.collections[0].get_paths()[i].vertices)
if Qback is not None:
for i in range(distinctBackPaths):
backVertices.append(backChart.collections[0].get_paths()[i].vertices)

scaVertices = np.concatenate(scaVertices)
absVertices = np.concatenate(absVertices)
if Qback is not None:
backVertices = np.concatenate(backVertices)

nSolution,kSolution = find_intersections(scaVertices,absVertices)
nSolution,kSolution = find_intersections(scaChart,absChart)
trials = [(x+y*1j) for x,y in zip(nSolution,kSolution)]
if Qback is not None:
oneTrueN,oneTrueK = find_intersections(backVertices,absVertices)
oneTrueN,oneTrueK = find_intersections(backChart,absChart)
_these = [np.round(x+y*1j,3) for x,y in zip(oneTrueN,oneTrueK)]
_match = list(set(np.round(trials,3)).intersection(np.round(_these,3)))
if len(_match) > 0:
Expand Down Expand Up @@ -385,31 +365,10 @@ def ContourIntersection_SD(Bsca,Babs,wavelength,dp,ndp,nMin=1,nMax=3,kMin=0.0000
ax.contourf(n,k,BbackList,backErrorLevels,origin='lower',colors=('green'),alpha=0.15)
ax.contour(n,k,BbackList,backErrorLevels,origin='lower',linewidths=0.5,colors=('green'),alpha=0.5)

distinctScaPaths = len(scaChart.collections[0].get_paths())
distinctAbsPaths = len(absChart.collections[0].get_paths())
if Bback is not None:
distinctBackPaths = len(backChart.collections[0].get_paths())
scaVertices = []
absVertices = []
if Bback is not None:
backVertices = []
for i in range(distinctScaPaths):
scaVertices.append(scaChart.collections[0].get_paths()[i].vertices)
for i in range(distinctAbsPaths):
absVertices.append(absChart.collections[0].get_paths()[i].vertices)
if Bback is not None:
for i in range(distinctBackPaths):
backVertices.append(backChart.collections[0].get_paths()[i].vertices)

scaVertices = np.concatenate(scaVertices)
absVertices = np.concatenate(absVertices)
if Bback is not None:
backVertices = np.concatenate(backVertices)

nSolution,kSolution = find_intersections(scaVertices,absVertices)
nSolution,kSolution = find_intersections(scaChart,absChart)
trials = [(x+y*1j) for x,y in zip(nSolution,kSolution)]
if Bback is not None:
oneTrueN,oneTrueK = find_intersections(backVertices,absVertices)
oneTrueN,oneTrueK = find_intersections(backChart,absChart)
_these = [np.round(x+y*1j,3) for x,y in zip(oneTrueN,oneTrueK)]
_match = list(set(np.round(trials,3)).intersection(np.round(_these,3)))
if len(_match) > 0:
Expand Down Expand Up @@ -523,29 +482,23 @@ def ContourIntersection_SD(Bsca,Babs,wavelength,dp,ndp,nMin=1,nMax=3,kMin=0.0000
return solutionSet,forwardCalculations,solutionErrors, fig, ax, graphElements

def find_intersections(A,B):
arrayMinimum = lambda x1, x2: np.where(x1<x2, x1, x2)
arrayMaximum = lambda x1, x2: np.where(x1>x2, x1, x2)
arrayAll = lambda abools: np.dstack(abools).all(axis=2)
slope = lambda line: (lambda d: d[:,1]/d[:,0])(np.diff(line, axis=0))

x11, x21 = np.meshgrid(A[:-1, 0], B[:-1, 0])
x12, x22 = np.meshgrid(A[1:, 0], B[1:, 0])
y11, y21 = np.meshgrid(A[:-1, 1], B[:-1, 1])
y12, y22 = np.meshgrid(A[1:, 1], B[1:, 1])

m1, m2 = np.meshgrid(slope(A), slope(B))
# Here we use masked arrays to properly treat the rare case where a line segment is perfectly vertical
_m1 = np.ma.masked_array(m1,m1==-np.inf)
_m2 = np.ma.masked_array(m2,m2==-np.inf)
yi = (_m1*(x21-x11-y21/_m2)+y11)/(1-_m1/_m2)
xi = (yi-y21)/_m2+x21

xconds = (arrayMinimum(x11, x12) < xi, xi <= arrayMaximum(x11, x12),
arrayMinimum(x21, x22) < xi, xi <= arrayMaximum(x21, x22) )
yconds = (arrayMinimum(y11, y12) < yi, yi <= arrayMaximum(y11, y12),
arrayMinimum(y21, y22) < yi, yi <= arrayMaximum(y21, y22) )

return xi[arrayAll(xconds)], yi[arrayAll(yconds)]
X = []
Y = []
for p1 in A.collections[0].get_paths():
for p2 in B.collections[0].get_paths():
v1 = p1.vertices
v2 = p2.vertices

poly1 = geometry.LineString(v1)
poly2 = geometry.LineString(v2)

sol = poly1.intersection(poly2)

for s in sol:
X.append(s.x)
Y.append(s.y)

return X,Y

def SurveyIteration(Qsca,Qabs,wavelength,diameter,tolerance=0.0005):
# http://pymiescatt.readthedocs.io/en/latest/inverse.html#IterativeInversion
Expand Down
2 changes: 1 addition & 1 deletion PyMieScatt/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.3.7"
__version__ = "1.4.0"
91 changes: 22 additions & 69 deletions build/lib/PyMieScatt/Inverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from matplotlib.offsetbox import AnchoredText
from scipy.ndimage import zoom
from scipy.integrate import trapz
from shapely import geometry
import warnings

def coerceDType(d):
Expand Down Expand Up @@ -161,31 +162,10 @@ def ContourIntersection(Qsca,Qabs,wavelength,diameter,Qback=None,nMin=1,nMax=3,k
ax.contourf(n,k,QbackList,backErrorLevels,origin='lower',colors=('green'),alpha=0.15)
ax.contour(n,k,QbackList,backErrorLevels,origin='lower',linewidths=0.5,colors=('green'),alpha=0.5)

distinctScaPaths = len(scaChart.collections[0].get_paths())
distinctAbsPaths = len(absChart.collections[0].get_paths())
if Qback is not None:
distinctBackPaths = len(backChart.collections[0].get_paths())
scaVertices = []
absVertices = []
if Qback is not None:
backVertices = []
for i in range(distinctScaPaths):
scaVertices.append(scaChart.collections[0].get_paths()[i].vertices)
for i in range(distinctAbsPaths):
absVertices.append(absChart.collections[0].get_paths()[i].vertices)
if Qback is not None:
for i in range(distinctBackPaths):
backVertices.append(backChart.collections[0].get_paths()[i].vertices)

scaVertices = np.concatenate(scaVertices)
absVertices = np.concatenate(absVertices)
if Qback is not None:
backVertices = np.concatenate(backVertices)

nSolution,kSolution = find_intersections(scaVertices,absVertices)
nSolution,kSolution = find_intersections(scaChart,absChart)
trials = [(x+y*1j) for x,y in zip(nSolution,kSolution)]
if Qback is not None:
oneTrueN,oneTrueK = find_intersections(backVertices,absVertices)
oneTrueN,oneTrueK = find_intersections(backChart,absChart)
_these = [np.round(x+y*1j,3) for x,y in zip(oneTrueN,oneTrueK)]
_match = list(set(np.round(trials,3)).intersection(np.round(_these,3)))
if len(_match) > 0:
Expand Down Expand Up @@ -385,31 +365,10 @@ def ContourIntersection_SD(Bsca,Babs,wavelength,dp,ndp,nMin=1,nMax=3,kMin=0.0000
ax.contourf(n,k,BbackList,backErrorLevels,origin='lower',colors=('green'),alpha=0.15)
ax.contour(n,k,BbackList,backErrorLevels,origin='lower',linewidths=0.5,colors=('green'),alpha=0.5)

distinctScaPaths = len(scaChart.collections[0].get_paths())
distinctAbsPaths = len(absChart.collections[0].get_paths())
if Bback is not None:
distinctBackPaths = len(backChart.collections[0].get_paths())
scaVertices = []
absVertices = []
if Bback is not None:
backVertices = []
for i in range(distinctScaPaths):
scaVertices.append(scaChart.collections[0].get_paths()[i].vertices)
for i in range(distinctAbsPaths):
absVertices.append(absChart.collections[0].get_paths()[i].vertices)
if Bback is not None:
for i in range(distinctBackPaths):
backVertices.append(backChart.collections[0].get_paths()[i].vertices)

scaVertices = np.concatenate(scaVertices)
absVertices = np.concatenate(absVertices)
if Bback is not None:
backVertices = np.concatenate(backVertices)

nSolution,kSolution = find_intersections(scaVertices,absVertices)
nSolution,kSolution = find_intersections(scaChart,absChart)
trials = [(x+y*1j) for x,y in zip(nSolution,kSolution)]
if Bback is not None:
oneTrueN,oneTrueK = find_intersections(backVertices,absVertices)
oneTrueN,oneTrueK = find_intersections(backChart,absChart)
_these = [np.round(x+y*1j,3) for x,y in zip(oneTrueN,oneTrueK)]
_match = list(set(np.round(trials,3)).intersection(np.round(_these,3)))
if len(_match) > 0:
Expand Down Expand Up @@ -523,29 +482,23 @@ def ContourIntersection_SD(Bsca,Babs,wavelength,dp,ndp,nMin=1,nMax=3,kMin=0.0000
return solutionSet,forwardCalculations,solutionErrors, fig, ax, graphElements

def find_intersections(A,B):
arrayMinimum = lambda x1, x2: np.where(x1<x2, x1, x2)
arrayMaximum = lambda x1, x2: np.where(x1>x2, x1, x2)
arrayAll = lambda abools: np.dstack(abools).all(axis=2)
slope = lambda line: (lambda d: d[:,1]/d[:,0])(np.diff(line, axis=0))

x11, x21 = np.meshgrid(A[:-1, 0], B[:-1, 0])
x12, x22 = np.meshgrid(A[1:, 0], B[1:, 0])
y11, y21 = np.meshgrid(A[:-1, 1], B[:-1, 1])
y12, y22 = np.meshgrid(A[1:, 1], B[1:, 1])

m1, m2 = np.meshgrid(slope(A), slope(B))
# Here we use masked arrays to properly treat the rare case where a line segment is perfectly vertical
_m1 = np.ma.masked_array(m1,m1==-np.inf)
_m2 = np.ma.masked_array(m2,m2==-np.inf)
yi = (_m1*(x21-x11-y21/_m2)+y11)/(1-_m1/_m2)
xi = (yi-y21)/_m2+x21

xconds = (arrayMinimum(x11, x12) < xi, xi <= arrayMaximum(x11, x12),
arrayMinimum(x21, x22) < xi, xi <= arrayMaximum(x21, x22) )
yconds = (arrayMinimum(y11, y12) < yi, yi <= arrayMaximum(y11, y12),
arrayMinimum(y21, y22) < yi, yi <= arrayMaximum(y21, y22) )

return xi[arrayAll(xconds)], yi[arrayAll(yconds)]
X = []
Y = []
for p1 in A.collections[0].get_paths():
for p2 in B.collections[0].get_paths():
v1 = p1.vertices
v2 = p2.vertices

poly1 = geometry.LineString(v1)
poly2 = geometry.LineString(v2)

sol = poly1.intersection(poly2)

for s in sol:
X.append(s.x)
Y.append(s.y)

return X,Y

def SurveyIteration(Qsca,Qabs,wavelength,diameter,tolerance=0.0005):
# http://pymiescatt.readthedocs.io/en/latest/inverse.html#IterativeInversion
Expand Down
2 changes: 1 addition & 1 deletion build/lib/PyMieScatt/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.3.7"
__version__ = "1.4.0"
Binary file removed dist/PyMieScatt-1.3.7-py3-none-any.whl
Binary file not shown.
Binary file removed dist/PyMieScatt-1.3.7.tar.gz
Binary file not shown.
Binary file added dist/PyMieScatt-1.4.0-py3-none-any.whl
Binary file not shown.
Binary file added dist/PyMieScatt-1.4.0.tar.gz
Binary file not shown.
16 changes: 10 additions & 6 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Documentation is currently under development, but almost complete. A manuscript
Install PyMieScatt
------------------

The current version is 1.3.7. You can install PyMieScatt from `The Python Package Index (PyPI) <https://pypi.python.org/pypi/PyMieScatt>`_ with ::
The current version is 1.4.0. You can install PyMieScatt from `The Python Package Index (PyPI) <https://pypi.python.org/pypi/PyMieScatt>`_ with ::

$ pip install PyMieScatt

Expand All @@ -30,14 +30,18 @@ or from `GitHub <https://github.com/bsumlin/PyMieScatt>`_. Clone the repository

$ python setup.py install

Revision Notes - version 1.3.7
Revision Notes - version 1.4.0
------------------------------

- Fixed a major bug in :py:func:`ContourIntersection` and :py:func:`ContourIntersection_SD` that prevented them from using the actual input values to derive solutions.
- Added `Shapely <https://shapely.readthedocs.io/>`_ support! Shapely is a geometric manipulation ana analysis package. I wrote it in as a slightly faster, more robust way to look for intersections in n-k space when doing inversions. It also makes the code more readable and makes it clearer how the intersection method works.

Revision History
----------------

- 1.3.7

- Fixed a major bug in :py:func:`ContourIntersection` and :py:func:`ContourIntersection_SD` that prevented them from using the actual input values to derive solutions.

- 1.3.6

- Added new normalization options to :py:func:`ScatteringFunction` and :py:func:`SF_SD`. Docs for those functions have details.
Expand Down Expand Up @@ -74,9 +78,9 @@ Revision History
Revisions in Progress
---------------------

- Would like to re-write the inverse functions to take any two of scattering, absorption, or backscatter for the non-compact inverse problem. Still need all three for a fully-constrained inversion.
- Would like to be able to pass array objects directly to all functions (within reason).
- Would like to include auto-graphing capabilities for sacttering functions.
- Would like to re-write the inversion functions to be as general as possible, i.e., if I pass scattering, absorption, particle size, and refractive index, it would solve for the wavelength.
- Ablility to pass array objects directly to all functions (within reason).
- Auto-graphing capabilities for sacttering functions.

Documentation To-Do List
------------------------
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@
packages=['PyMieScatt'],
keywords=['Mie Rayleigh scattering absorption extinction light refraction'],
classifiers = ['Development Status :: 5 - Production/Stable','Intended Audience :: Science/Research','Programming Language :: Python :: 3 :: Only','Topic :: Scientific/Engineering :: Physics'],
install_requires=['numpy','scipy','matplotlib'],
install_requires=['numpy','scipy','matplotlib','shapely'],
zip_safe=False)

0 comments on commit 8cb263f

Please sign in to comment.