-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
improve sphere handling
1 parent
852a462
commit 2d1a04f
Showing
15 changed files
with
1,488 additions
and
728 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,3 +51,6 @@ | |
from .iad import * | ||
from .grid import * | ||
from .rxt import * | ||
from .port import * | ||
from .mc_sphere import * | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
# pylint: disable=invalid-name | ||
# pylint: disable=too-many-instance-attributes | ||
# pylint: disable=too-many-arguments | ||
# pylint: disable=consider-using-f-string | ||
# pylint: disable=line-too-long | ||
|
||
""" | ||
Class for managing integrating spheres. | ||
This module contains the Sphere class, designed to simulate and analyze the | ||
behavior of light within an integrating sphere. An integrating sphere is a | ||
device used in optical measurements, which allows for the uniform scattering | ||
of light. It is commonly used for reflection and transmission measurements | ||
of materials. | ||
The Sphere class models the geometrical and optical properties of an | ||
integrating sphere, enabling the calculation of various parameters such as | ||
the areas of spherical caps given port diameters, relative port areas, | ||
and the gain caused by reflections within the sphere. It supports different | ||
measurements scenarios by adjusting port diameters, detector reflectivity, | ||
wall reflectivity, and using a reflectance standard. | ||
Attributes: | ||
d_sphere (float): Diameter of the integrating sphere in millimeters. | ||
d_sample (float): Diameter of the port that holds the sample. | ||
d_empty (float): Diameter of the empty port. | ||
d_detector (float): Diameter of the port with the detector. | ||
r_detector (float): Reflectivity of the detector. | ||
r_wall (float): Reflectivity of the sphere's internal wall. | ||
r_std (float): Reflectivity of the standard used for calibration. | ||
Methods: | ||
cap_area: actual area of a spherical cap for a port | ||
relative_cap_area: relative area of spherical cap to the sphere's area. | ||
gain: sphere gain relative to a black sphere | ||
multiplier: multiplier for wall power due to sphere | ||
Example usage: | ||
>>> import iadpython | ||
>>> s = iadpython.Sphere(250,20) | ||
>>> print(s) | ||
>>> s = iadpython.Sphere(200, 20, d_empty=10, d_detector=10, r_detector=0.8, r_wall=0.99, r_std=0.99) | ||
>>> print(sphere) | ||
>>> area_sample = sphere.cap_area(sphere.d_sample) | ||
>>> print(f"Sample port area: {area_sample:.2f} mm²") | ||
""" | ||
|
||
import numpy as np | ||
import random | ||
from enum import Enum | ||
from iadpython import Sphere | ||
|
||
class PortType(Enum): | ||
"""Possible sphere wall locations.""" | ||
EMPTY = 0 | ||
WALL = 1 | ||
SAMPLE = 2 | ||
DETECTOR = 3 | ||
|
||
class MCSphere(Sphere): | ||
"""Class for an Monte Carlo integrating sphere calcs. | ||
The center of the sphere is at (0,0,0) | ||
For a reflection measurement, the empty port is the diameter of through | ||
which the light enters to hit the sample. For a transmission measurement | ||
this is the port that might allow unscattered transmission to leave. In | ||
either case, the reflectance from this port is assumed to be zero. | ||
Attributes: | ||
- d_sphere: diameter of integrating sphere [mm] | ||
- d_sample: diameter of the port that has the sample [mm] | ||
- d_empty: diameter of the empty port [mm] | ||
- d_detector: diameter of port with detector [mm] | ||
- r_detector: reflectivity of the detector | ||
- r_wall: reflectivity of the wall | ||
- r_std: reflectivity of the standard used with the sphere | ||
Example:: | ||
>>> import iadpython as iad | ||
>>> s = iad.Sphere(200, 20) | ||
>>> print(s) | ||
""" | ||
|
||
def __init__(self, d_sphere, d_sample, d_empty=0, | ||
d_detector=0, r_detector=0, r_wall=0.99, r_std=0.99): | ||
|
||
super().__init__(d_sphere, d_sample, d_empty, d_detector, | ||
r_detector, r_wall, r_std) | ||
self.weight = 1 | ||
|
||
def __str__(self): | ||
"""Return basic details as a string for printing.""" | ||
s = super().__str__() | ||
s = "\n" | ||
s += "Sample Port\n" | ||
s += " center = (%.1f, %.1f %.1f) mm\n" % (self.sample_x, self.sample_y, self.sample_z) | ||
s += " chord = %.1f mm\n" % np.sqrt(self.sample_chord_sqr) | ||
s += " radius = %.1f mm\n" % (np.sample_d/2) | ||
s += " sagitta = %.1f mm\n" % (np.sample_sagitta) | ||
s += "Detector Port\n" | ||
s += " center = (%.1f, %.1f %.1f) mm\n" % (self.detector_x, self.detector_y, self.detector_z) | ||
s += " chord = %.1f mm\n" % np.sqrt(self.detector_chord_sqr) | ||
s += " radius = %.1f mm\n" % (np.detector_d/2) | ||
s += " sagitta = %.1f mm\n" % (np.detector_sagitta) | ||
s += "Empty Port\n" | ||
s += " center = (%.1f, %.1f %.1f) mm\n" % (self.empty_x, self.empty_y, self.empty_z) | ||
s += " chord = %.1f mm\n" % np.sqrt(self.empty_chord_sqr) | ||
s += " radius = %.1f mm\n" % (np.empty_d/2) | ||
s += " sagitta = %.1f mm\n" % (np.empty_sagitta) | ||
return s | ||
|
||
def do_one_photon(self): | ||
"""Bounce photon inside sphere until it leaves.""" | ||
bounces = 0 | ||
detected = 0 | ||
|
||
# assume photon launched form sample | ||
weight = 1 | ||
last_location = PortType.SAMPLE | ||
|
||
while weight > 1e-4: | ||
|
||
self.x, self.y, self.z = self.uniform() | ||
|
||
if self.detector.hit(): | ||
if last_location == PortType.DETECTOR: # avoid hitting self | ||
continue | ||
if last_location == PortType.SAMPLE and self.baffle: # sample --> detector prohibited | ||
continue | ||
|
||
# record detected light and update weight | ||
transmitted = weight * (1-self.r_detector) | ||
detected += transmitted | ||
weight -= transmitted | ||
last_location = PortType.DETECTOR | ||
|
||
elif self.sample.hit(): | ||
if last_location == PortType.SAMPLE: # avoid hitting self | ||
continue | ||
if last_location == PortType.DETECTOR and self.baffle: # detector --> sample prohibited | ||
continue | ||
weight *= self.r_sample | ||
last_location = PortType.SAMPLE | ||
|
||
elif self.empty.hit(): | ||
weight = 0 | ||
last_location = PortType.EMPTY | ||
|
||
else: | ||
# must have hit wall | ||
weight *= self.r_wall | ||
last_location = PortType.WALL | ||
|
||
bounces +=1 | ||
|
||
return detected, bounces | ||
|
||
def do_N_photons(self): | ||
|
||
total_detected = 0 | ||
total_bounces = 0 | ||
|
||
for i in range(N): | ||
detected, bounces = self.do_one_photon() | ||
total_detected += detected | ||
total_bounces += bounces | ||
|
||
print("average detected = %.5f" % (total_detected/N)) | ||
print("average bounces = %.5f" % (total_bounces/N)) | ||
|
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import unittest | ||
import numpy as np | ||
from iadpython import Sphere, Port | ||
|
||
class TestPort(unittest.TestCase): | ||
"""Unit tests for the Port class.""" | ||
|
||
def setUp(self): | ||
"""Set up test conditions for Port tests.""" | ||
d_sample = 25 # 20 mm sample port | ||
d_sphere = 200 # 200 mm diameter sphere | ||
R = d_sphere /2 | ||
d_port = 20 | ||
uru_port = 0.5 | ||
self.sphere = Sphere(d_sphere, d_sample) # | ||
self.sphere.z = R | ||
self.port = Port(self.sphere, d_port, uru=uru_port, z=R) # Example port | ||
|
||
def test_cap_area(self): | ||
"""Test the cap_area method.""" | ||
area = self.port.cap_area() | ||
self.assertTrue(np.isclose(area, 314.94861522998946), "Cap area calculation is incorrect") | ||
|
||
def test_approx_relative_cap_area(self): | ||
"""Test the approx_relative_cap_area method.""" | ||
approx_area = self.port.approx_relative_cap_area() | ||
self.assertTrue(np.isclose(approx_area, 0.0025), "Approximate relative cap area calculation is incorrect") | ||
|
||
def test_calculate_sagitta(self): | ||
"""Test the calculate_sagitta method.""" | ||
sagitta = self.port.calculate_sagitta() | ||
self.assertTrue(np.isclose(sagitta, 0.5012562893380021), "Sagitta calculation is incorrect") | ||
|
||
def test_max_center_chord(self): | ||
"""Test the max_center_chord method.""" | ||
max_chord = self.port.max_center_chord() | ||
self.assertTrue(np.isclose(max_chord, 10.012555011963775), "Max center chord calculation is incorrect") | ||
|
||
def test_relative_cap_area(self): | ||
"""Test the relative_cap_area method.""" | ||
rel_area = self.port.relative_cap_area() | ||
self.assertTrue(np.isclose(rel_area, 0.002506281446690011), "Relative cap area calculation is incorrect") | ||
|
||
def test_hit(self): | ||
"""Test the hit method.""" | ||
self.assertTrue(self.port.hit(), "Hit detection is incorrect") | ||
|
||
def test_uniform_01(self): | ||
"""Test the generating points on the top of the sphere.""" | ||
for i in range(20): | ||
x, y, z = self.port.uniform() | ||
self.assertTrue(self.port.hit(), "Hit detection is incorrect") | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters