radphen-iadpython 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iadpython/__init__.py +59 -0
- iadpython/ad.py +1377 -0
- iadpython/agrid.py +306 -0
- iadpython/cache.py +133 -0
- iadpython/combine.py +620 -0
- iadpython/constants.py +3 -0
- iadpython/data/M38597.csv +759 -0
- iadpython/double.py +376 -0
- iadpython/fresnel.py +642 -0
- iadpython/grid.py +125 -0
- iadpython/iad.py +615 -0
- iadpython/iadc.py +1003 -0
- iadpython/iadcommand.py +712 -0
- iadpython/layer.py +241 -0
- iadpython/nist.py +114 -0
- iadpython/port.py +242 -0
- iadpython/quadrature.py +172 -0
- iadpython/redistribution.py +318 -0
- iadpython/rxt.py +201 -0
- iadpython/sphere.py +582 -0
- iadpython/start.py +377 -0
- iadpython/txt.py +147 -0
- radphen_iadpython-0.7.0.dist-info/METADATA +264 -0
- radphen_iadpython-0.7.0.dist-info/RECORD +28 -0
- radphen_iadpython-0.7.0.dist-info/WHEEL +5 -0
- radphen_iadpython-0.7.0.dist-info/entry_points.txt +2 -0
- radphen_iadpython-0.7.0.dist-info/licenses/LICENSE.txt +18 -0
- radphen_iadpython-0.7.0.dist-info/top_level.txt +1 -0
iadpython/iadc.py
ADDED
|
@@ -0,0 +1,1003 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for direct and inverse adding-doubling calculations.
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
>>> import iadpython as iad
|
|
6
|
+
|
|
7
|
+
>>> a = 0.9
|
|
8
|
+
>>> b = 10
|
|
9
|
+
>>> g = 0.9
|
|
10
|
+
>>> n_top = 1.5
|
|
11
|
+
>>> n_bot = 1.5
|
|
12
|
+
>>> ur1, ut1, uru, utu = iad.rt(n_top, n_bot, a, b, g)
|
|
13
|
+
>>> print("total reflected light for normal incidence %.5f" % UR1)
|
|
14
|
+
>>> print("total transmitted light for normal incidence %.5f" % UT1)
|
|
15
|
+
>>> print("total reflected light for diffuse incidence %.5f" % URU)
|
|
16
|
+
>>> print("total transmitted light for diffuse incidence %.5f" % UTU)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
import ctypes
|
|
21
|
+
import ctypes.util
|
|
22
|
+
import numpy as np
|
|
23
|
+
|
|
24
|
+
__all__ = (
|
|
25
|
+
"basic_rt",
|
|
26
|
+
"basic_rt_unscattered",
|
|
27
|
+
"basic_rt_cone",
|
|
28
|
+
"basic_rt_oblique",
|
|
29
|
+
"basic_rt_inverse",
|
|
30
|
+
"rt",
|
|
31
|
+
"rt_unscattered",
|
|
32
|
+
"rt_cone",
|
|
33
|
+
"rt_oblique",
|
|
34
|
+
"rt_inverse",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
libiad_path = ctypes.util.find_library("libiad")
|
|
38
|
+
|
|
39
|
+
if not libiad_path:
|
|
40
|
+
print("Unable to find the libiad library.")
|
|
41
|
+
print(" macOS = 'libiad.dylib'")
|
|
42
|
+
print(" unix = 'libiad.so'")
|
|
43
|
+
print(" Windows = 'libiad.dll'")
|
|
44
|
+
print("Paths searched:")
|
|
45
|
+
for p in sys.path:
|
|
46
|
+
print(" ", p)
|
|
47
|
+
sys.exit()
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
libiad = ctypes.CDLL(libiad_path)
|
|
51
|
+
except OSError:
|
|
52
|
+
print("Unable to load the libiad library")
|
|
53
|
+
print("Sorry")
|
|
54
|
+
sys.exit()
|
|
55
|
+
|
|
56
|
+
libiad.ez_RT.argtypes = (
|
|
57
|
+
ctypes.c_int, # n quadrature points
|
|
58
|
+
ctypes.c_double, # slab index of refraction
|
|
59
|
+
ctypes.c_double, # top slide index of refraction
|
|
60
|
+
ctypes.c_double, # bottom slide index of refraction
|
|
61
|
+
ctypes.c_double, # albedo mus/(mus+mua)
|
|
62
|
+
ctypes.c_double, # optical thickness d*(mua+mus)
|
|
63
|
+
ctypes.c_double, # scattering anisotropy g
|
|
64
|
+
ctypes.POINTER(ctypes.c_double), # UR1
|
|
65
|
+
ctypes.POINTER(ctypes.c_double), # UT1
|
|
66
|
+
ctypes.POINTER(ctypes.c_double), # URU
|
|
67
|
+
ctypes.POINTER(ctypes.c_double), # UTU
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def basic_rt(n, nslab, ntop, nbot, a, b, g):
|
|
72
|
+
"""
|
|
73
|
+
Calculate the total reflection and transmission for a turbid slab.
|
|
74
|
+
|
|
75
|
+
`n` is the number of quadrature angles (more angles give better accuracy but slow
|
|
76
|
+
the calculation by `n**3`, `n` must be a multiple of 4, typically 16 is good for forward
|
|
77
|
+
calculations and 4 is appropriate for inverse calculations).
|
|
78
|
+
|
|
79
|
+
The semi-infinite slab may be bounded by glass slides.
|
|
80
|
+
The top glass slides have an index of refraction `ntop` and the bottom slide has
|
|
81
|
+
an index `nbot`. If there are no glass slides, set `ntop` and `nbottom` to 1.
|
|
82
|
+
|
|
83
|
+
All input parameters must be scalars.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
n: number of points in quadrature (multiple of 4)
|
|
87
|
+
nslab: index of refraction of the slab
|
|
88
|
+
ntop: index of refraction of the top slide
|
|
89
|
+
nbot: index of refraction of the bottom slide
|
|
90
|
+
a: single scattering albedo of the slab
|
|
91
|
+
b: optical thickness of the slab
|
|
92
|
+
g: anisotropy of single scattering
|
|
93
|
+
Returns:
|
|
94
|
+
- `UR1` is the total reflection for normally incident collimated light.
|
|
95
|
+
- `UT1` is the total transmission for normally incident collimated light.
|
|
96
|
+
- `URU` is the total reflection for diffuse incident light.
|
|
97
|
+
- `UTU` is the total transmission for diffuse incident light.
|
|
98
|
+
"""
|
|
99
|
+
ur1 = ctypes.c_double()
|
|
100
|
+
ut1 = ctypes.c_double()
|
|
101
|
+
uru = ctypes.c_double()
|
|
102
|
+
utu = ctypes.c_double()
|
|
103
|
+
libiad.ez_RT(n, nslab, ntop, nbot, a, b, g, ur1, ut1, uru, utu)
|
|
104
|
+
return ur1.value, ut1.value, uru.value, utu.value
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
libiad.ez_RT_unscattered.argtypes = (
|
|
108
|
+
ctypes.c_int, # n quadrature points
|
|
109
|
+
ctypes.c_double, # slab index of refraction
|
|
110
|
+
ctypes.c_double, # top slide index of refraction
|
|
111
|
+
ctypes.c_double, # bottom slide index of refraction
|
|
112
|
+
ctypes.c_double, # albedo mus/(mus+mua)
|
|
113
|
+
ctypes.c_double, # optical thickness d*(mua+mus)
|
|
114
|
+
ctypes.c_double, # scattering anisotropy g
|
|
115
|
+
ctypes.POINTER(ctypes.c_double), # UR1
|
|
116
|
+
ctypes.POINTER(ctypes.c_double), # UT1
|
|
117
|
+
ctypes.POINTER(ctypes.c_double), # URU
|
|
118
|
+
ctypes.POINTER(ctypes.c_double), # UTU
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def basic_rt_unscattered(n, nslab, ntop, nbot, a, b, g):
|
|
123
|
+
"""
|
|
124
|
+
Calculate the unscattered reflection and transmission for a turbid slab.
|
|
125
|
+
|
|
126
|
+
`n` is the number of quadrature angles (more angles give better accuracy but slow
|
|
127
|
+
the calculation by `n**3`, `n` must be a multiple of 4, typically 16 is good for forward
|
|
128
|
+
calculations and 4 is appropriate for inverse calculations).
|
|
129
|
+
|
|
130
|
+
The semi-infinite slab may be bounded by glass slides.
|
|
131
|
+
The top glass slides have an index of refraction `ntop` and the bottom slide has
|
|
132
|
+
an index `nbot`. If there are no glass slides, set `ntop` and `nbottom` to 1.
|
|
133
|
+
|
|
134
|
+
All input parameters must be scalars.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
n: number of points in quadrature (multiple of 4)
|
|
138
|
+
nslab: index of refraction of the slab
|
|
139
|
+
ntop: index of refraction of the top slide
|
|
140
|
+
nbot: index of refraction of the bottom slide
|
|
141
|
+
a: single scattering albedo of the slab
|
|
142
|
+
b: optical thickness of the slab
|
|
143
|
+
g: anisotropy of single scattering
|
|
144
|
+
Returns:
|
|
145
|
+
- `UR1` is the total reflection for normally incident collimated light.
|
|
146
|
+
- `UT1` is the total transmission for normally incident collimated light.
|
|
147
|
+
- `URU` is the total reflection for diffuse incident light.
|
|
148
|
+
- `UTU` is the total transmission for diffuse incident light.
|
|
149
|
+
"""
|
|
150
|
+
ur1 = ctypes.c_double()
|
|
151
|
+
ut1 = ctypes.c_double()
|
|
152
|
+
uru = ctypes.c_double()
|
|
153
|
+
utu = ctypes.c_double()
|
|
154
|
+
libiad.ez_RT_unscattered(n, nslab, ntop, nbot, a, b, g, ur1, ut1, uru, utu)
|
|
155
|
+
return ur1.value, ut1.value, uru.value, utu.value
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
libiad.ez_RT_Cone.argtypes = (
|
|
159
|
+
ctypes.c_int, # n quadrature points
|
|
160
|
+
ctypes.c_double, # slab index of refraction
|
|
161
|
+
ctypes.c_double, # top slide index of refraction
|
|
162
|
+
ctypes.c_double, # bottom slide index of refraction
|
|
163
|
+
ctypes.c_double, # albedo mus/(mus+mua)
|
|
164
|
+
ctypes.c_double, # optical thickness d*(mua+mus)
|
|
165
|
+
ctypes.c_double, # scattering anisotropy g
|
|
166
|
+
ctypes.c_double, # cosine of cone angle
|
|
167
|
+
ctypes.POINTER(ctypes.c_double), # UR1
|
|
168
|
+
ctypes.POINTER(ctypes.c_double), # UT1
|
|
169
|
+
ctypes.POINTER(ctypes.c_double), # URU
|
|
170
|
+
ctypes.POINTER(ctypes.c_double), # UTU
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def basic_rt_cone(n, nslab, ntop, nbot, a, b, g, cos_cone_angle):
|
|
175
|
+
"""
|
|
176
|
+
Calculate reflection and transmission for a turbid slab exiting within a cone.
|
|
177
|
+
|
|
178
|
+
This routine assumes normally incident or uniformly diffuse incident light
|
|
179
|
+
and returns the total reflected or transmitted light that exits within a cone.
|
|
180
|
+
The cosine of the cone angle is given by `cos_cone_angle`.
|
|
181
|
+
|
|
182
|
+
`n` is the number of quadrature angles (more angles give better accuracy but slow
|
|
183
|
+
the calculation by `n**3`, `n` must be a multiple of 4, typically 16 is good for forward
|
|
184
|
+
calculations and 4 is appropriate for inverse calculations).
|
|
185
|
+
|
|
186
|
+
The semi-infinite slab may be bounded by glass slides.
|
|
187
|
+
The top glass slides have an index of refraction `ntop` and the bottom slide has
|
|
188
|
+
an index `nbot`. If there are no glass slides, set `ntop` and `nbottom` to 1.
|
|
189
|
+
|
|
190
|
+
All input parameters must be scalars.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
n: number of points in quadrature (multiple of 4)
|
|
194
|
+
nslab: index of refraction of the slab
|
|
195
|
+
ntop: index of refraction of the top slide
|
|
196
|
+
nbot: index of refraction of the bottom slide
|
|
197
|
+
a: single scattering albedo of the slab
|
|
198
|
+
b: optical thickness of the slab
|
|
199
|
+
g: anisotropy of single scattering
|
|
200
|
+
cos_cone_angle: cosine of cone of exiting light
|
|
201
|
+
Returns:
|
|
202
|
+
- `UR1` is the reflection within cone for normally incident collimated light.
|
|
203
|
+
- `UT1` is the transmission within cone for normally incident collimated light.
|
|
204
|
+
- `URU` is the reflection within cone for diffuse incident light.
|
|
205
|
+
- `UTU` is the transmission within cone for diffuse incident light.
|
|
206
|
+
"""
|
|
207
|
+
ur1 = ctypes.c_double()
|
|
208
|
+
ut1 = ctypes.c_double()
|
|
209
|
+
uru = ctypes.c_double()
|
|
210
|
+
utu = ctypes.c_double()
|
|
211
|
+
libiad.ez_RT_Cone(n, nslab, ntop, nbot, a, b, g, cos_cone_angle, ur1, ut1, uru, utu)
|
|
212
|
+
return ur1.value, ut1.value, uru.value, utu.value
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
libiad.ez_RT_Oblique.argtypes = (
|
|
216
|
+
ctypes.c_int, # n quadrature points
|
|
217
|
+
ctypes.c_double, # slab index of refraction
|
|
218
|
+
ctypes.c_double, # top slide index of refraction
|
|
219
|
+
ctypes.c_double, # bottom slide index of refraction
|
|
220
|
+
ctypes.c_double, # albedo mus/(mus+mua)
|
|
221
|
+
ctypes.c_double, # optical thickness d*(mua+mus)
|
|
222
|
+
ctypes.c_double, # scattering anisotropy g
|
|
223
|
+
ctypes.c_double, # cosine of oblique angle
|
|
224
|
+
ctypes.POINTER(ctypes.c_double), # UR1
|
|
225
|
+
ctypes.POINTER(ctypes.c_double), # UT1
|
|
226
|
+
ctypes.POINTER(ctypes.c_double), # URU
|
|
227
|
+
ctypes.POINTER(ctypes.c_double), # UTU
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def basic_rt_oblique(n, nslab, ntop, nbot, a, b, g, cos_oblique):
|
|
232
|
+
"""
|
|
233
|
+
Calculate reflection and transmission for light incident at a oblique angle.
|
|
234
|
+
|
|
235
|
+
This returns the total R and T for light incident at an oblique angle or
|
|
236
|
+
incident within a cone. The cosine of the oblique angle (or the cone) is
|
|
237
|
+
`cos_oblique`.
|
|
238
|
+
|
|
239
|
+
n is the number of quadrature angles (more angles give better accuracy but slow
|
|
240
|
+
the calculation by n**3, n must be a multiple of 4, typically 16 is good for forward
|
|
241
|
+
calculations and 4 is appropriate for inverse calculations).
|
|
242
|
+
|
|
243
|
+
The semi-infinite slab may be bounded by glass slides.
|
|
244
|
+
The top glass slides have an index of refraction `ntop` and the bottom slide has
|
|
245
|
+
an index `nbot`. If there are no glass slides, set `ntop` and `nbottom` to 1.
|
|
246
|
+
|
|
247
|
+
All input parameters must be scalars.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
n: number of points in quadrature (multiple of 4)
|
|
251
|
+
nslab: index of refraction of the slab
|
|
252
|
+
ntop: index of refraction of the top slide
|
|
253
|
+
nbot: index of refraction of the bottom slide
|
|
254
|
+
a: single scattering albedo of the slab
|
|
255
|
+
b: optical thickness of the slab
|
|
256
|
+
g: anisotropy of single scattering
|
|
257
|
+
cos_oblique: cosine of cone of incident light
|
|
258
|
+
Returns:
|
|
259
|
+
- `URx` is the total reflection for obliquely incident collimated light.
|
|
260
|
+
- `UTx` is the total transmission for obliquely incident collimated light.
|
|
261
|
+
- `URU` is the total reflection for diffuse light incident within a cone.
|
|
262
|
+
- `UTU` is the total transmission for diffuse light incident within a cone.
|
|
263
|
+
"""
|
|
264
|
+
ur1 = ctypes.c_double()
|
|
265
|
+
ut1 = ctypes.c_double()
|
|
266
|
+
uru = ctypes.c_double()
|
|
267
|
+
utu = ctypes.c_double()
|
|
268
|
+
libiad.ez_RT_Oblique(n, nslab, ntop, nbot, a, b, g, cos_oblique, ur1, ut1, uru, utu)
|
|
269
|
+
return ur1.value, ut1.value, uru.value, utu.value
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
libiad.ez_Inverse_RT.argtypes = (
|
|
273
|
+
ctypes.c_double, # slab index of refraction
|
|
274
|
+
ctypes.c_double, # slide index of refraction
|
|
275
|
+
ctypes.c_double, # UR1
|
|
276
|
+
ctypes.c_double, # UT1
|
|
277
|
+
ctypes.c_double, # unscattered transmission
|
|
278
|
+
ctypes.POINTER(ctypes.c_double), # a
|
|
279
|
+
ctypes.POINTER(ctypes.c_double), # b
|
|
280
|
+
ctypes.POINTER(ctypes.c_double), # g
|
|
281
|
+
ctypes.POINTER(ctypes.c_int), # error
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def basic_rt_inverse(nslab, nslide, ur1, ut1, tc):
|
|
286
|
+
"""
|
|
287
|
+
Calculate optical properties given reflection and transmission values.
|
|
288
|
+
|
|
289
|
+
Finds the optical properties `[a,b,g]` for a slab
|
|
290
|
+
with total reflectance `ur1`, total transmission `ut1`, unscattered transmission `Tc`.
|
|
291
|
+
The index of refraction of the slab is `nslab`, the index of refraction of the
|
|
292
|
+
top and bottom slides is `nslide`.
|
|
293
|
+
|
|
294
|
+
The semi-infinite slab may be bounded by glass slides.
|
|
295
|
+
The top glass slides have an index of refraction `ntop` and the bottom slide has
|
|
296
|
+
an index `nbot`. If there are no glass slides, set `ntop` and `nbottom` to 1.
|
|
297
|
+
|
|
298
|
+
All input parameters must be scalars.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
nslab: index of refraction of the slab
|
|
302
|
+
nslide: index of refraction of the slides
|
|
303
|
+
ur1: is total reflection for normally incident collimated light.
|
|
304
|
+
ut1: is total transmission for normally incident collimated light.
|
|
305
|
+
tc: unscattered transmission through sample
|
|
306
|
+
Returns:
|
|
307
|
+
- `a` is the single scattering albedo of the slab
|
|
308
|
+
- `b` is the optical thickness of the slab
|
|
309
|
+
- `g` is the anisotropy of single scattering
|
|
310
|
+
"""
|
|
311
|
+
a = ctypes.c_double()
|
|
312
|
+
b = ctypes.c_double()
|
|
313
|
+
g = ctypes.c_double()
|
|
314
|
+
error = ctypes.c_int()
|
|
315
|
+
libiad.ez_Inverse_RT(nslab, nslide, ur1, ut1, tc, a, b, g, error)
|
|
316
|
+
return a.value, b.value, g.value, error.value
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def rt(nslab, nslide, a, b, g):
|
|
320
|
+
"""
|
|
321
|
+
Calculate the total reflection and transmission for a turbid slab.
|
|
322
|
+
|
|
323
|
+
This routine should be the primary entry point because the optical properties
|
|
324
|
+
can be either scalars or arrays.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
nslab: index of refraction of the slab
|
|
328
|
+
nslide: index of refraction of the slides
|
|
329
|
+
a: single scattering albedo of the slab
|
|
330
|
+
b: optical thickness of the slab
|
|
331
|
+
g: anisotropy of single scattering
|
|
332
|
+
Returns:
|
|
333
|
+
- `UR1` is the total reflection for normally incident collimated light.
|
|
334
|
+
- `UT1` is the total transmission for normally incident collimated light.
|
|
335
|
+
- `URU` is the total reflection for diffuse incident light.
|
|
336
|
+
- `UTU` is the total transmission for diffuse incident light.
|
|
337
|
+
"""
|
|
338
|
+
N_QUADRATURE = 16 # should be a multiple of 16
|
|
339
|
+
|
|
340
|
+
if np.isscalar(a):
|
|
341
|
+
len_a = 0
|
|
342
|
+
aa = a
|
|
343
|
+
else:
|
|
344
|
+
len_a = len(a)
|
|
345
|
+
|
|
346
|
+
if np.isscalar(b):
|
|
347
|
+
len_b = 0
|
|
348
|
+
bb = b
|
|
349
|
+
else:
|
|
350
|
+
len_b = len(b)
|
|
351
|
+
|
|
352
|
+
if np.isscalar(g):
|
|
353
|
+
len_g = 0
|
|
354
|
+
gg = g
|
|
355
|
+
else:
|
|
356
|
+
len_g = len(g)
|
|
357
|
+
|
|
358
|
+
thelen = max(len_a, len_b, len_g)
|
|
359
|
+
|
|
360
|
+
if thelen == 0:
|
|
361
|
+
return basic_rt(N_QUADRATURE, nslab, nslide, nslide, aa, bb, gg)
|
|
362
|
+
|
|
363
|
+
if len_a and len_b and len_a != len_b:
|
|
364
|
+
raise RuntimeError("rt: a and b arrays must be same length")
|
|
365
|
+
|
|
366
|
+
if len_a and len_g and len_a != len_g:
|
|
367
|
+
raise RuntimeError("rt: a and g arrays must be same length")
|
|
368
|
+
|
|
369
|
+
if len_b and len_g and len_b != len_g:
|
|
370
|
+
raise RuntimeError("rt: b and g arrays must be same length")
|
|
371
|
+
|
|
372
|
+
ur1 = np.empty(thelen)
|
|
373
|
+
ut1 = np.empty(thelen)
|
|
374
|
+
uru = np.empty(thelen)
|
|
375
|
+
utu = np.empty(thelen)
|
|
376
|
+
|
|
377
|
+
for i in range(thelen):
|
|
378
|
+
if len_a > 0:
|
|
379
|
+
aa = a[i]
|
|
380
|
+
|
|
381
|
+
if len_b > 0:
|
|
382
|
+
bb = b[i]
|
|
383
|
+
|
|
384
|
+
if len_g > 0:
|
|
385
|
+
gg = g[i]
|
|
386
|
+
|
|
387
|
+
ur1[i], ut1[i], uru[i], utu[i] = basic_rt(N_QUADRATURE, nslab, nslide, nslide, aa, bb, gg)
|
|
388
|
+
|
|
389
|
+
return ur1, ut1, uru, utu
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def rt_unscattered(nslab, nslide, a, b, g):
|
|
393
|
+
"""
|
|
394
|
+
Calculate the unscattered reflection and transmission for a turbid slab.
|
|
395
|
+
|
|
396
|
+
This routine should be the primary entry point because the optical properties
|
|
397
|
+
can be either scalars or arrays.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
nslab: index of refraction of the slab
|
|
401
|
+
nslide: index of refraction of the slides
|
|
402
|
+
a: single scattering albedo of the slab
|
|
403
|
+
b: optical thickness of the slab
|
|
404
|
+
g: anisotropy of single scattering
|
|
405
|
+
Returns:
|
|
406
|
+
- `UR1` is the unscattered reflection for normally incident collimated light.
|
|
407
|
+
- `UT1` is the unscattered transmission for normally incident collimated light.
|
|
408
|
+
- `URU` is the unscattered reflection for diffuse incident light.
|
|
409
|
+
- `UTU` is the unscattered transmission for diffuse incident light.
|
|
410
|
+
"""
|
|
411
|
+
N_QUADRATURE = 16 # should be a multiple of 16
|
|
412
|
+
|
|
413
|
+
if np.isscalar(a):
|
|
414
|
+
len_a = 0
|
|
415
|
+
aa = a
|
|
416
|
+
else:
|
|
417
|
+
len_a = len(a)
|
|
418
|
+
|
|
419
|
+
if np.isscalar(b):
|
|
420
|
+
len_b = 0
|
|
421
|
+
bb = b
|
|
422
|
+
else:
|
|
423
|
+
len_b = len(b)
|
|
424
|
+
|
|
425
|
+
if np.isscalar(g):
|
|
426
|
+
len_g = 0
|
|
427
|
+
gg = g
|
|
428
|
+
else:
|
|
429
|
+
len_g = len(g)
|
|
430
|
+
|
|
431
|
+
thelen = max(len_a, len_b, len_g)
|
|
432
|
+
|
|
433
|
+
if thelen == 0:
|
|
434
|
+
return basic_rt_unscattered(N_QUADRATURE, nslab, nslide, nslide, aa, bb, gg)
|
|
435
|
+
|
|
436
|
+
if len_a and len_b and len_a != len_b:
|
|
437
|
+
raise RuntimeError("rt_unscattered: a and b arrays must be same length")
|
|
438
|
+
|
|
439
|
+
if len_a and len_g and len_a != len_g:
|
|
440
|
+
raise RuntimeError("rt_unscattered: a and g arrays must be same length")
|
|
441
|
+
|
|
442
|
+
if len_b and len_g and len_b != len_g:
|
|
443
|
+
raise RuntimeError("rt_unscattered: b and g arrays must be same length")
|
|
444
|
+
|
|
445
|
+
ur1 = np.empty(thelen)
|
|
446
|
+
ut1 = np.empty(thelen)
|
|
447
|
+
uru = np.empty(thelen)
|
|
448
|
+
utu = np.empty(thelen)
|
|
449
|
+
|
|
450
|
+
for i in range(thelen):
|
|
451
|
+
if len_a > 0:
|
|
452
|
+
aa = a[i]
|
|
453
|
+
|
|
454
|
+
if len_b > 0:
|
|
455
|
+
bb = b[i]
|
|
456
|
+
|
|
457
|
+
if len_g > 0:
|
|
458
|
+
gg = g[i]
|
|
459
|
+
|
|
460
|
+
ur1[i], ut1[i], uru[i], utu[i] = basic_rt_unscattered(N_QUADRATURE, nslab, nslide, nslide, aa, bb, gg)
|
|
461
|
+
|
|
462
|
+
return ur1, ut1, uru, utu
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def rt_cone(nslab, nslide, a, b, g, cos_cone):
|
|
466
|
+
"""
|
|
467
|
+
Calculate reflection and transmission for a turbid slab exiting within a cone.
|
|
468
|
+
|
|
469
|
+
This routine should be the primary entry point because the optical properties
|
|
470
|
+
can be either scalars or arrays.
|
|
471
|
+
|
|
472
|
+
Args:
|
|
473
|
+
n: number of points in quadrature (multiple of 4)
|
|
474
|
+
nslab: index of refraction of the slab
|
|
475
|
+
nslide: index of refraction of the top and bottom slides
|
|
476
|
+
a: single scattering albedo of the slab
|
|
477
|
+
b: optical thickness of the slab
|
|
478
|
+
g: anisotropy of single scattering
|
|
479
|
+
cos_cone: cosine of cone of exiting light
|
|
480
|
+
Returns:
|
|
481
|
+
- `UR1` is the reflection within cone for normally incident collimated light.
|
|
482
|
+
- `UT1` is the transmission within cone for normally incident collimated light.
|
|
483
|
+
- `URU` is the reflection within cone for diffuse incident light.
|
|
484
|
+
- `UTU` is the transmission within cone for diffuse incident light.
|
|
485
|
+
"""
|
|
486
|
+
N_QUADRATURE = 16 # should be a multiple of 16
|
|
487
|
+
|
|
488
|
+
if np.isscalar(a):
|
|
489
|
+
len_a = 0
|
|
490
|
+
aa = a
|
|
491
|
+
else:
|
|
492
|
+
len_a = len(a)
|
|
493
|
+
|
|
494
|
+
if np.isscalar(b):
|
|
495
|
+
len_b = 0
|
|
496
|
+
bb = b
|
|
497
|
+
else:
|
|
498
|
+
len_b = len(b)
|
|
499
|
+
|
|
500
|
+
if np.isscalar(g):
|
|
501
|
+
len_g = 0
|
|
502
|
+
gg = g
|
|
503
|
+
else:
|
|
504
|
+
len_g = len(g)
|
|
505
|
+
|
|
506
|
+
if np.isscalar(cos_cone):
|
|
507
|
+
len_mu = 0
|
|
508
|
+
mu = cos_cone
|
|
509
|
+
else:
|
|
510
|
+
len_mu = len(cos_cone)
|
|
511
|
+
|
|
512
|
+
thelen = max(len_a, len_b, len_g, len_mu)
|
|
513
|
+
|
|
514
|
+
if thelen == 0:
|
|
515
|
+
return basic_rt_cone(N_QUADRATURE, nslab, nslide, nslide, aa, bb, gg, mu)
|
|
516
|
+
|
|
517
|
+
if len_a and len_b and len_a != len_b:
|
|
518
|
+
raise RuntimeError("rt_cone: a and b arrays must be same length")
|
|
519
|
+
|
|
520
|
+
if len_a and len_g and len_a != len_g:
|
|
521
|
+
raise RuntimeError("rt_cone: a and g arrays must be same length")
|
|
522
|
+
|
|
523
|
+
if len_a and len_mu and len_a != len_mu:
|
|
524
|
+
raise RuntimeError("rt_cone: a and mu arrays must be same length")
|
|
525
|
+
|
|
526
|
+
if len_b and len_g and len_b != len_g:
|
|
527
|
+
raise RuntimeError("rt_cone: b and g arrays must be same length")
|
|
528
|
+
|
|
529
|
+
if len_b and len_mu and len_b != len_mu:
|
|
530
|
+
raise RuntimeError("rt_cone: b and mu arrays must be same length")
|
|
531
|
+
|
|
532
|
+
if len_g and len_mu and len_g != len_mu:
|
|
533
|
+
raise RuntimeError("rt_cone: g and mu arrays must be same length")
|
|
534
|
+
|
|
535
|
+
ur1 = np.empty(thelen)
|
|
536
|
+
ut1 = np.empty(thelen)
|
|
537
|
+
uru = np.empty(thelen)
|
|
538
|
+
utu = np.empty(thelen)
|
|
539
|
+
|
|
540
|
+
for i in range(thelen):
|
|
541
|
+
if len_a > 0:
|
|
542
|
+
aa = a[i]
|
|
543
|
+
|
|
544
|
+
if len_b > 0:
|
|
545
|
+
bb = b[i]
|
|
546
|
+
|
|
547
|
+
if len_g > 0:
|
|
548
|
+
gg = g[i]
|
|
549
|
+
|
|
550
|
+
if len_mu > 0:
|
|
551
|
+
mu = cos_cone[i]
|
|
552
|
+
|
|
553
|
+
ur1[i], ut1[i], uru[i], utu[i] = basic_rt_cone(N_QUADRATURE, nslab, nslide, nslide, aa, bb, gg, mu)
|
|
554
|
+
|
|
555
|
+
return ur1, ut1, uru, utu
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def rt_oblique(nslab, nslide, a, b, g, cos_oblique):
|
|
559
|
+
"""
|
|
560
|
+
Calculate reflection and transmission for light incident at a oblique angle.
|
|
561
|
+
|
|
562
|
+
This routine should be the primary entry point because the optical properties
|
|
563
|
+
can be either scalars or arrays.
|
|
564
|
+
|
|
565
|
+
All input parameters must be scalars.
|
|
566
|
+
|
|
567
|
+
Args:
|
|
568
|
+
nslab: index of refraction of the slab
|
|
569
|
+
nslide: index of refraction of the top and bottom slides
|
|
570
|
+
a: single scattering albedo of the slab
|
|
571
|
+
b: optical thickness of the slab
|
|
572
|
+
g: anisotropy of single scattering
|
|
573
|
+
cos_oblique: cosine of cone of incident light
|
|
574
|
+
Returns:
|
|
575
|
+
- `URx` is the total reflection for obliquely incident collimated light.
|
|
576
|
+
- `UTx` is the total transmission for obliquely incident collimated light.
|
|
577
|
+
- `URU` is the total reflection for diffuse light incident within a cone.
|
|
578
|
+
- `UTU` is the total transmission for diffuse light incident within a cone.
|
|
579
|
+
"""
|
|
580
|
+
N_QUADRATURE = 16 # should be a multiple of 16
|
|
581
|
+
|
|
582
|
+
if np.isscalar(a):
|
|
583
|
+
len_a = 0
|
|
584
|
+
aa = a
|
|
585
|
+
else:
|
|
586
|
+
len_a = len(a)
|
|
587
|
+
|
|
588
|
+
if np.isscalar(b):
|
|
589
|
+
len_b = 0
|
|
590
|
+
bb = b
|
|
591
|
+
else:
|
|
592
|
+
len_b = len(b)
|
|
593
|
+
|
|
594
|
+
if np.isscalar(g):
|
|
595
|
+
len_g = 0
|
|
596
|
+
gg = g
|
|
597
|
+
else:
|
|
598
|
+
len_g = len(g)
|
|
599
|
+
|
|
600
|
+
if np.isscalar(cos_oblique):
|
|
601
|
+
len_mu = 0
|
|
602
|
+
mu = cos_oblique
|
|
603
|
+
else:
|
|
604
|
+
len_mu = len(cos_oblique)
|
|
605
|
+
|
|
606
|
+
thelen = max(len_a, len_b, len_g, len_mu)
|
|
607
|
+
|
|
608
|
+
if thelen == 0:
|
|
609
|
+
return basic_rt_oblique(N_QUADRATURE, nslab, nslide, nslide, aa, bb, gg, mu)
|
|
610
|
+
|
|
611
|
+
if len_a and len_b and len_a != len_b:
|
|
612
|
+
raise RuntimeError("rt_oblique: a and b arrays must be same length")
|
|
613
|
+
|
|
614
|
+
if len_a and len_g and len_a != len_g:
|
|
615
|
+
raise RuntimeError("rt_oblique: a and g arrays must be same length")
|
|
616
|
+
|
|
617
|
+
if len_a and len_mu and len_a != len_mu:
|
|
618
|
+
raise RuntimeError("rt_oblique: a and mu arrays must be same length")
|
|
619
|
+
|
|
620
|
+
if len_b and len_g and len_b != len_g:
|
|
621
|
+
raise RuntimeError("rt_oblique: b and g arrays must be same length")
|
|
622
|
+
|
|
623
|
+
if len_b and len_mu and len_b != len_mu:
|
|
624
|
+
raise RuntimeError("rt_oblique: b and mu arrays must be same length")
|
|
625
|
+
|
|
626
|
+
if len_g and len_mu and len_g != len_mu:
|
|
627
|
+
raise RuntimeError("rt_oblique: g and mu arrays must be same length")
|
|
628
|
+
|
|
629
|
+
ur1 = np.empty(thelen)
|
|
630
|
+
ut1 = np.empty(thelen)
|
|
631
|
+
uru = np.empty(thelen)
|
|
632
|
+
utu = np.empty(thelen)
|
|
633
|
+
|
|
634
|
+
for i in range(thelen):
|
|
635
|
+
if len_a > 0:
|
|
636
|
+
aa = a[i]
|
|
637
|
+
|
|
638
|
+
if len_b > 0:
|
|
639
|
+
bb = b[i]
|
|
640
|
+
|
|
641
|
+
if len_g > 0:
|
|
642
|
+
gg = g[i]
|
|
643
|
+
|
|
644
|
+
if len_mu > 0:
|
|
645
|
+
mu = cos_oblique[i]
|
|
646
|
+
|
|
647
|
+
ur1[i], ut1[i], uru[i], utu[i] = basic_rt_oblique(N_QUADRATURE, nslab, nslide, nslide, aa, bb, gg, mu)
|
|
648
|
+
|
|
649
|
+
return ur1, ut1, uru, utu
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
def rt_inverse(nslab, nslide, ur1, ut1, t_unscattered):
|
|
653
|
+
"""
|
|
654
|
+
Calculate [a,b,g] for a slab.
|
|
655
|
+
|
|
656
|
+
This routine should be the primary entry point because the reflection
|
|
657
|
+
and transmission can be either scalars or arrays.
|
|
658
|
+
|
|
659
|
+
All input parameters must be scalars.
|
|
660
|
+
|
|
661
|
+
Args:
|
|
662
|
+
nslab: index of refraction of the slab
|
|
663
|
+
nslide: index of refraction of the slides
|
|
664
|
+
ur1: is total reflection for normally incident collimated light.
|
|
665
|
+
ut1: is total transmission for normally incident collimated light.
|
|
666
|
+
t_unscattered: unscattered transmission through sample
|
|
667
|
+
Returns:
|
|
668
|
+
- `a` is the single scattering albedo of the slab
|
|
669
|
+
- `b` is the optical thickness of the slab
|
|
670
|
+
- `g` is the anisotropy of single scattering
|
|
671
|
+
"""
|
|
672
|
+
if np.isscalar(ur1):
|
|
673
|
+
len_r1 = 0
|
|
674
|
+
else:
|
|
675
|
+
len_r1 = len(ur1)
|
|
676
|
+
|
|
677
|
+
if np.isscalar(ut1):
|
|
678
|
+
len_t1 = 0
|
|
679
|
+
else:
|
|
680
|
+
len_t1 = len(ut1)
|
|
681
|
+
|
|
682
|
+
if np.isscalar(t_unscattered):
|
|
683
|
+
len_tc = 0
|
|
684
|
+
tc = t_unscattered
|
|
685
|
+
else:
|
|
686
|
+
len_tc = len(t_unscattered)
|
|
687
|
+
|
|
688
|
+
thelen = max(len_r1, len_t1, len_tc)
|
|
689
|
+
|
|
690
|
+
if thelen == 0:
|
|
691
|
+
return basic_rt_inverse(nslab, nslide, ur1, ut1, tc)
|
|
692
|
+
|
|
693
|
+
if len_r1 and len_t1 and len_r1 != len_t1:
|
|
694
|
+
raise RuntimeError("inverse_rt: ur1 and ut1 arrays must be same length")
|
|
695
|
+
|
|
696
|
+
if len_r1 and len_tc and len_r1 != len_tc:
|
|
697
|
+
raise RuntimeError("inverse_rt: ur1 and tc arrays must be same length")
|
|
698
|
+
|
|
699
|
+
if len_t1 and len_tc and len_t1 != len_tc:
|
|
700
|
+
raise RuntimeError("inverse_rt: t1 and tc arrays must be same length")
|
|
701
|
+
|
|
702
|
+
a = np.empty(thelen)
|
|
703
|
+
b = np.empty(thelen)
|
|
704
|
+
g = np.empty(thelen)
|
|
705
|
+
error = np.empty(thelen)
|
|
706
|
+
|
|
707
|
+
for i in range(thelen):
|
|
708
|
+
if len_r1 > 0:
|
|
709
|
+
r1 = ur1[i]
|
|
710
|
+
|
|
711
|
+
if len_t1 > 0:
|
|
712
|
+
t1 = ut1[i]
|
|
713
|
+
|
|
714
|
+
if len_tc > 0:
|
|
715
|
+
tc = t_unscattered[i]
|
|
716
|
+
|
|
717
|
+
a[i], b[i], g[i], error[i] = basic_rt_inverse(nslab, nslide, r1, t1, tc)
|
|
718
|
+
|
|
719
|
+
return a, b, g, error
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
class CSample:
|
|
723
|
+
"""Class for samples."""
|
|
724
|
+
|
|
725
|
+
def __init__(self):
|
|
726
|
+
"""Initialize class."""
|
|
727
|
+
self.sample_index = 1.4
|
|
728
|
+
self.top_slide_index = 1.5
|
|
729
|
+
self.bot_slide_index = 1.5
|
|
730
|
+
self.sample_thickness = 1 # mm
|
|
731
|
+
self.top_slide_thickness = 1 # mm
|
|
732
|
+
self.bot_slide_thickness = 1 # mm
|
|
733
|
+
|
|
734
|
+
def as_array(self):
|
|
735
|
+
"""Representation class as an array."""
|
|
736
|
+
return [
|
|
737
|
+
self.sample_index,
|
|
738
|
+
self.top_slide_index,
|
|
739
|
+
self.bot_slide_index,
|
|
740
|
+
self.sample_thickness,
|
|
741
|
+
self.top_slide_thickness,
|
|
742
|
+
self.bot_slide_thickness,
|
|
743
|
+
]
|
|
744
|
+
|
|
745
|
+
def init_from_array(self, a):
|
|
746
|
+
"""Initialize with an array."""
|
|
747
|
+
self.sample_index = a[0]
|
|
748
|
+
self.top_slide_index = a[1]
|
|
749
|
+
self.bot_slide_index = a[2]
|
|
750
|
+
self.sample_thickness = a[3]
|
|
751
|
+
self.top_slide_thickness = a[4]
|
|
752
|
+
self.bot_slide_thickness = a[5]
|
|
753
|
+
|
|
754
|
+
def as_c_array(self):
|
|
755
|
+
"""Represent class as C array."""
|
|
756
|
+
pyarr = self.as_array()
|
|
757
|
+
return (ctypes.c_double * len(pyarr))(*pyarr)
|
|
758
|
+
|
|
759
|
+
def __str__(self):
|
|
760
|
+
"""Represent class as string."""
|
|
761
|
+
s = ""
|
|
762
|
+
s += "sample index = %.3f\n" % self.sample_index
|
|
763
|
+
s += "top slide index = %.3f\n" % self.top_slide_index
|
|
764
|
+
s += "bottom slide index = %.3f\n" % self.bot_slide_index
|
|
765
|
+
|
|
766
|
+
s += "sample thickness = %.1f mm\n" % self.sample_thickness
|
|
767
|
+
s += "top slide thickness = %.1f mm\n" % self.top_slide_thickness
|
|
768
|
+
s += "bottom slide thickness = %.1f mm" % self.bot_slide_thickness
|
|
769
|
+
return s
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
class CSphere:
|
|
773
|
+
"""Class for spheres."""
|
|
774
|
+
|
|
775
|
+
def __init__(self):
|
|
776
|
+
"""Initialize class."""
|
|
777
|
+
self.d_sphere = 8.0 * 25.4
|
|
778
|
+
self.d_sample = 1.0 * 25.4
|
|
779
|
+
self.d_empty = 1.0 * 25.4
|
|
780
|
+
self.d_detector = 0.5 * 25.4
|
|
781
|
+
self.refl_wall = 0.98
|
|
782
|
+
self.refl_detector = 0.05
|
|
783
|
+
|
|
784
|
+
def init_from_array(self, a):
|
|
785
|
+
"""Initialize with an array."""
|
|
786
|
+
self.d_sphere = a[0]
|
|
787
|
+
self.d_sample = a[1]
|
|
788
|
+
self.d_empty = a[2]
|
|
789
|
+
self.d_detector = a[3]
|
|
790
|
+
self.refl_wall = a[4]
|
|
791
|
+
self.refl_detector = a[5]
|
|
792
|
+
|
|
793
|
+
def as_array(self):
|
|
794
|
+
"""Representation class as an array."""
|
|
795
|
+
return [
|
|
796
|
+
self.d_sphere,
|
|
797
|
+
self.d_sample,
|
|
798
|
+
self.d_empty,
|
|
799
|
+
self.d_detector,
|
|
800
|
+
self.refl_wall,
|
|
801
|
+
self.refl_detector,
|
|
802
|
+
]
|
|
803
|
+
|
|
804
|
+
def as_c_array(self):
|
|
805
|
+
"""Represent class as C array."""
|
|
806
|
+
pyarr = self.as_array()
|
|
807
|
+
return (ctypes.c_double * len(pyarr))(*pyarr)
|
|
808
|
+
|
|
809
|
+
def __str__(self):
|
|
810
|
+
"""Represent class as string."""
|
|
811
|
+
s = ""
|
|
812
|
+
s += "sphere diameter = %.1f mm\n" % self.d_sphere
|
|
813
|
+
s += "sample port diameter = %.1f mm\n" % self.d_sample
|
|
814
|
+
s += "empty port diameter = %.1f mm\n" % self.d_empty
|
|
815
|
+
s += "detector port diameter = %.1f mm\n" % self.d_detector
|
|
816
|
+
s += "wall reflectivity = %.3f\n" % self.refl_wall
|
|
817
|
+
s += "detector reflectivity = %.3f" % self.refl_detector
|
|
818
|
+
return s
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
class CIllumination:
|
|
822
|
+
"""Class for illumination."""
|
|
823
|
+
|
|
824
|
+
def __init__(self):
|
|
825
|
+
"""Initialize class."""
|
|
826
|
+
self.beam_diameter = 5 # mm
|
|
827
|
+
self.specular_reflection_excluded = 0
|
|
828
|
+
self.direct_transmission_excluded = 0
|
|
829
|
+
self.diffuse_illumination = 0
|
|
830
|
+
self.num_spheres = 0
|
|
831
|
+
self.lambda0 = 632.8 # nm (unused, tracks wavelength)
|
|
832
|
+
|
|
833
|
+
def init_from_array(self, a):
|
|
834
|
+
"""Initialize with an array."""
|
|
835
|
+
self.beam_diameter = a[0]
|
|
836
|
+
self.specular_reflection_excluded = a[1]
|
|
837
|
+
self.direct_transmission_excluded = a[2]
|
|
838
|
+
self.diffuse_illumination = a[3]
|
|
839
|
+
self.lambda0 = a[4]
|
|
840
|
+
self.num_spheres = a[5]
|
|
841
|
+
|
|
842
|
+
def as_array(self):
|
|
843
|
+
"""Representation class as an array."""
|
|
844
|
+
return [
|
|
845
|
+
self.beam_diameter,
|
|
846
|
+
self.specular_reflection_excluded,
|
|
847
|
+
self.direct_transmission_excluded,
|
|
848
|
+
self.diffuse_illumination,
|
|
849
|
+
self.lambda0,
|
|
850
|
+
self.num_spheres,
|
|
851
|
+
]
|
|
852
|
+
|
|
853
|
+
def as_c_array(self):
|
|
854
|
+
"""Represent class as C array."""
|
|
855
|
+
pyarr = self.as_array()
|
|
856
|
+
return (ctypes.c_double * len(pyarr))(*pyarr)
|
|
857
|
+
|
|
858
|
+
def __str__(self):
|
|
859
|
+
"""Represent class as string."""
|
|
860
|
+
s = ""
|
|
861
|
+
s += "diameter of beam = %.1f mm\n" % self.beam_diameter
|
|
862
|
+
s += "wavelength = %.1f nm\n" % self.lambda0
|
|
863
|
+
s += "exclude specular refl? = %s\n" % (bool(self.specular_reflection_excluded))
|
|
864
|
+
s += "exclude direct trans? = %s\n" % (bool(self.direct_transmission_excluded))
|
|
865
|
+
s += "illumination diffuse? = %s\n" % (bool(self.diffuse_illumination))
|
|
866
|
+
s += "number of spheres = %.0f" % self.num_spheres
|
|
867
|
+
return s
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
class CAnalysis:
|
|
871
|
+
"""Class for analysis."""
|
|
872
|
+
|
|
873
|
+
def __init__(self):
|
|
874
|
+
"""Initialize class."""
|
|
875
|
+
self.quadrature_points = 8
|
|
876
|
+
self.monte_carlo_runs = 0
|
|
877
|
+
self.num_photons = 10000
|
|
878
|
+
|
|
879
|
+
def init_from_array(self, a):
|
|
880
|
+
"""Initialize with an array."""
|
|
881
|
+
self.quadrature_points = a[0]
|
|
882
|
+
self.monte_carlo_runs = a[1]
|
|
883
|
+
self.num_photons = a[2]
|
|
884
|
+
|
|
885
|
+
def as_array(self):
|
|
886
|
+
"""Representation class as an array."""
|
|
887
|
+
return [self.quadrature_points, self.monte_carlo_runs, self.num_photons]
|
|
888
|
+
|
|
889
|
+
def as_c_array(self):
|
|
890
|
+
"""Represent class as C array."""
|
|
891
|
+
pyarr = self.as_array()
|
|
892
|
+
return (ctypes.c_double * len(pyarr))(*pyarr)
|
|
893
|
+
|
|
894
|
+
def __str__(self):
|
|
895
|
+
"""Represent class as string."""
|
|
896
|
+
s = ""
|
|
897
|
+
s += "quadrature points = %.0f\n" % self.quadrature_points
|
|
898
|
+
s += "monte carlo runs = %.0f\n" % self.monte_carlo_runs
|
|
899
|
+
s += "number of photons = %.0f" % self.num_photons
|
|
900
|
+
return s
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
class CMeasurement:
|
|
904
|
+
"""Class for a measurement."""
|
|
905
|
+
|
|
906
|
+
def __init__(self):
|
|
907
|
+
"""Initialize class."""
|
|
908
|
+
self.standard_reflectance = 0.98
|
|
909
|
+
self.reflectance = 0.5
|
|
910
|
+
self.transmittance = 0.1
|
|
911
|
+
self.unscattered_transmittance = 0
|
|
912
|
+
|
|
913
|
+
def init_from_array(self, a):
|
|
914
|
+
"""Initialize with an array."""
|
|
915
|
+
self.standard_reflectance = a[0]
|
|
916
|
+
self.reflectance = a[1]
|
|
917
|
+
self.transmittance = a[2]
|
|
918
|
+
self.unscattered_transmittance = a[3]
|
|
919
|
+
|
|
920
|
+
def as_array(self):
|
|
921
|
+
"""Representation class as an array."""
|
|
922
|
+
return [
|
|
923
|
+
self.standard_reflectance,
|
|
924
|
+
self.reflectance,
|
|
925
|
+
self.transmittance,
|
|
926
|
+
self.unscattered_transmittance,
|
|
927
|
+
]
|
|
928
|
+
|
|
929
|
+
def as_c_array(self):
|
|
930
|
+
"""Represent class as C array."""
|
|
931
|
+
pyarr = self.as_array()
|
|
932
|
+
return (ctypes.c_double * len(pyarr))(*pyarr)
|
|
933
|
+
|
|
934
|
+
def __str__(self):
|
|
935
|
+
"""Represent class as string."""
|
|
936
|
+
s = ""
|
|
937
|
+
s += "standard reflectance = %8.5f\n" % self.standard_reflectance
|
|
938
|
+
s += "reflectance = %8.5f\n" % self.reflectance
|
|
939
|
+
s += "transmittance = %8.5f\n" % self.transmittance
|
|
940
|
+
s += "unscattered trans = %8.5f\n" % self.unscattered_transmittance
|
|
941
|
+
return s
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
libiad.Spheres_Inverse_RT2.argtypes = (
|
|
945
|
+
ctypes.POINTER(ctypes.c_double), # sample
|
|
946
|
+
ctypes.POINTER(ctypes.c_double), # illumination
|
|
947
|
+
ctypes.POINTER(ctypes.c_double), # Refl Sphere
|
|
948
|
+
ctypes.POINTER(ctypes.c_double), # Trans Sphere
|
|
949
|
+
ctypes.POINTER(ctypes.c_double), # analysis
|
|
950
|
+
ctypes.POINTER(ctypes.c_double), # measurement
|
|
951
|
+
ctypes.POINTER(ctypes.c_double), # a
|
|
952
|
+
ctypes.POINTER(ctypes.c_double), # b
|
|
953
|
+
ctypes.POINTER(ctypes.c_double), # g
|
|
954
|
+
)
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
def _indent(s):
|
|
958
|
+
return " " + s.replace("\n", "\n ")
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
class Experiment:
|
|
962
|
+
"""Class for experiments."""
|
|
963
|
+
|
|
964
|
+
def __init__(self):
|
|
965
|
+
"""Initialize class."""
|
|
966
|
+
self.illumination = CIllumination()
|
|
967
|
+
self.sample = CSample()
|
|
968
|
+
self.r_sphere = CSphere()
|
|
969
|
+
self.t_sphere = CSphere()
|
|
970
|
+
self.analysis = CAnalysis()
|
|
971
|
+
self.measurement = CMeasurement()
|
|
972
|
+
|
|
973
|
+
def __str__(self):
|
|
974
|
+
"""Represent class as string."""
|
|
975
|
+
s = "e.sample.\n"
|
|
976
|
+
s += _indent(self.sample.__str__())
|
|
977
|
+
s += "\n\ne.illumination.\n"
|
|
978
|
+
s += _indent(self.illumination.__str__())
|
|
979
|
+
s += "\n\ne.r_sphere.\n"
|
|
980
|
+
s += _indent(self.r_sphere.__str__())
|
|
981
|
+
s += "\n\ne.t_sphere.\n"
|
|
982
|
+
s += _indent(self.t_sphere.__str__())
|
|
983
|
+
s += "\n\ne.analysis.\n"
|
|
984
|
+
s += _indent(self.analysis.__str__())
|
|
985
|
+
s += "\n\ne.measurement.\n"
|
|
986
|
+
s += _indent(self.measurement.__str__())
|
|
987
|
+
return s
|
|
988
|
+
|
|
989
|
+
def invert(self):
|
|
990
|
+
"""Calculate optical properties than match experiment."""
|
|
991
|
+
a_s = self.sample.as_c_array()
|
|
992
|
+
a_i = self.illumination.as_c_array()
|
|
993
|
+
a_r = self.r_sphere.as_c_array()
|
|
994
|
+
a_t = self.t_sphere.as_c_array()
|
|
995
|
+
a_a = self.analysis.as_c_array()
|
|
996
|
+
a_m = self.measurement.as_c_array()
|
|
997
|
+
|
|
998
|
+
a = ctypes.c_double()
|
|
999
|
+
b = ctypes.c_double()
|
|
1000
|
+
g = ctypes.c_double()
|
|
1001
|
+
|
|
1002
|
+
libiad.Spheres_Inverse_RT2(a_s, a_i, a_r, a_t, a_a, a_m, a, b, g)
|
|
1003
|
+
return [a.value, b.value, g.value]
|