.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gallery/experiment/sphere/size_distribution_phase_function.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_gallery_experiment_sphere_size_distribution_phase_function.py: How distribution width changes the scattering phase function ============================================================ Compare lognormal number distributions at the same median diameter. The right panel shows the unpolarized phase function per unit solid angle, normalized so its integral over the sphere is one. Intensities are averaged before normalization; averaging complex amplitudes or equally weighted normalized single-particle phase functions would give a different result. ``diameter_sampling`` controls the number of diameter quadrature nodes, not the number of physical particles. ``angular_sampling`` independently controls the resolution of the plotted scattering angles. Increase diameter sampling to check convergence of the ensemble phase function. This uses the non-interacting approximation: no interparticle electromagnetic coupling, multiple scattering, or interparticle interference. Refer to PackLab for correlation-based dependent-scattering approximations when correlations matter. The comparison isolates width at fixed number median; the arithmetic mean diameter and particle volume therefore also change with width. .. GENERATED FROM PYTHON SOURCE LINES 22-134 .. image-sg:: /gallery/experiment/sphere/images/sphx_glr_size_distribution_phase_function_001.png :alt: How particle-size width changes angular scattering, A Number-based size distributions, B Scattering phase function :srcset: /gallery/experiment/sphere/images/sphx_glr_size_distribution_phase_function_001.png :class: sphx-glr-single-img .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from PyMieSim import Measure, ParticleSizeDistribution, PlaneWave, PolarizationState, Simulation, Sphere, ureg MEDIAN_DIAMETER = 1.0 * ureg.micrometer WAVELENGTH = 532 * ureg.nanometer PARTICLE_INDEX = 1.59 MEDIUM_INDEX = 1.33 WIDTHS = (1.0, 1.05, 1.15, 1.30) DIAMETER_SAMPLING = 128 ANGULAR_SAMPLING = 901 def ensemble_phase_function(distribution, angles): """Return an incoherent, normalized unpolarized phase function and mean g. The two orthogonal polarization intensities are averaged for each sphere. All diameters share wavelength and medium, so the common amplitude scale cancels during normalization. Gauss-Legendre angular integration checks the normalization independently of the displayed angular grid. """ source = PlaneWave( wavelength=WAVELENGTH, amplitude=1 * ureg.volt / ureg.meter, polarization=PolarizationState(angle=0 * ureg.degree), ) cosine, angular_weights = np.polynomial.legendre.leggauss(512) evaluation_angles = np.concatenate([angles, np.arccos(cosine)]) * ureg.radian mean_intensity = np.zeros(len(evaluation_angles)) scattering_weights = [] asymmetries = [] for diameter, fraction in zip(distribution.diameters, distribution.number_fractions): simulation = Simulation( source=source, scatterer=Sphere(diameter=diameter, material=PARTICLE_INDEX, medium=MEDIUM_INDEX), ) s1, s2 = simulation.get_s1s2(evaluation_angles) intensity = (np.abs(s1.magnitude) ** 2 + np.abs(s2.magnitude) ** 2) / 2 mean_intensity += fraction * intensity scattering_weights.append(fraction * simulation.run(Measure.CSCA).magnitude) asymmetries.append(simulation.run(Measure.G).magnitude) integrated = mean_intensity[len(angles):] normalization = 2 * np.pi * np.dot(angular_weights, integrated) phase = mean_intensity[:len(angles)] / normalization g_from_angles = 2 * np.pi * np.dot(angular_weights, cosine * integrated) / normalization g_from_cross_sections = np.dot(scattering_weights, asymmetries) / np.sum(scattering_weights) np.testing.assert_allclose(g_from_angles, g_from_cross_sections, atol=2e-7, rtol=0) # The unscaled angular integral must agree with the Mie scattering cross # section up to the common wavenumber-squared factor for all diameters. wavenumber = 2 * np.pi * MEDIUM_INDEX / WAVELENGTH.to('meter').magnitude np.testing.assert_allclose(normalization / wavenumber ** 2, np.sum(scattering_weights), rtol=2e-7, atol=0) return phase, g_from_angles def make_figure(diameter_sampling=DIAMETER_SAMPLING, angular_sampling=ANGULAR_SAMPLING): """Plot the number distributions and their ensemble phase functions.""" angles = np.linspace(0, np.pi, angular_sampling) diameters_um = np.linspace(0.2, 2.5, 1000) colors = ('#253449', '#007f86', '#4676c4', '#d26435') phase_functions = [] with plt.rc_context({'font.size': 11, 'axes.spines.top': False, 'axes.spines.right': False}): figure, (size_axis, phase_axis) = plt.subplots( 1, 2, figsize=(13, 6.3), gridspec_kw={'width_ratios': [1, 1.85]}, ) figure.subplots_adjust(left=0.07, right=0.98, bottom=0.19, top=0.76, wspace=0.27) figure.suptitle('How particle-size width changes angular scattering', x=0.07, y=0.97, ha='left', fontsize=20, fontweight='bold', color='#253449') figure.text(0.07, 0.88, 'Same number median: 1 µm | Wavelength: 532 nm | Unpolarized light\n' 'Sphere refractive index: 1.59 · Medium refractive index: 1.33', color='#526071', linespacing=1.6) for width, color in zip(WIDTHS, colors): distribution = ParticleSizeDistribution.lognormal(MEDIAN_DIAMETER, width, sampling=diameter_sampling) phase, asymmetry = ensemble_phase_function(distribution, angles) phase_functions.append(phase) if width == 1: label = 'Single size' size_axis.axvline(1, color=color, linewidth=2, linestyle='--') size_axis.annotate('Single size', xy=(1, 7.5), xytext=(1.25, 8.6), color=color, arrowprops={'arrowstyle': '->', 'color': color}, fontsize=10) else: label = f'Geometric SD = {width:.2f}' sigma = np.log(width) density = np.exp(-0.5 * (np.log(diameters_um) / sigma) ** 2) / (diameters_um * sigma * np.sqrt(2 * np.pi)) size_axis.plot(diameters_um, density, color=color, linewidth=2) size_axis.fill_between(diameters_um, density, color=color, alpha=0.09) phase_axis.semilogy(np.rad2deg(angles), phase, color=color, linewidth=1.8 if width == 1 else 2.3, linestyle='--' if width == 1 else '-', label=f'{label} (g = {asymmetry:.3f})') size_axis.set(title='A Number-based size distributions', xlabel='Particle diameter [µm]', ylabel='Number probability density [µm⁻¹]', xlim=(0.2, 2.5), ylim=(0, 10)) phase_axis.set(title='B Scattering phase function', xlabel='Scattering angle [degrees]', ylabel='Normalized phase function [sr⁻¹]', xlim=(0, 180)) phase_axis.set_xticks(np.arange(0, 181, 30)) phase_axis.legend(loc='upper right', frameon=False, fontsize=10) phase_axis.grid(which='major', alpha=0.15) size_axis.grid(axis='y', alpha=0.15) phase_axis.text(0, -0.17, '0° = forward', transform=phase_axis.transAxes, fontsize=10, color='#526071') phase_axis.text(1, -0.17, '180° = backward', transform=phase_axis.transAxes, ha='right', fontsize=10, color='#526071') figure.text(0.07, 0.035, 'Non-interacting particles only. Intensities are averaged, then normalized over solid angle.\n' f'{diameter_sampling} diameter quadrature points per continuous distribution; ' f'{angular_sampling} plotted angles. The single-size case uses one diameter.', color='#526071', fontsize=9.5, linespacing=1.6) return figure, np.asarray(phase_functions) figure, phase_functions = make_figure() plt.show() .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 1.535 seconds) .. _sphx_glr_download_gallery_experiment_sphere_size_distribution_phase_function.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: size_distribution_phase_function.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: size_distribution_phase_function.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: size_distribution_phase_function.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_