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/start.py
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""Module for generating the starting layer for adding-doubling.
|
|
2
|
+
|
|
3
|
+
Two types of starting methods are possible IGI and Diamond.
|
|
4
|
+
|
|
5
|
+
Example::
|
|
6
|
+
|
|
7
|
+
>>> import iadpython as iad
|
|
8
|
+
>>> s = iad.Sample(a=0.9, b=10, g=0.9, n=1.5)
|
|
9
|
+
>>> r, t = iad.init_layer(s)
|
|
10
|
+
>>> print(r)
|
|
11
|
+
>>> print(t)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
import scipy.linalg
|
|
16
|
+
import iadpython as iad
|
|
17
|
+
|
|
18
|
+
__all__ = (
|
|
19
|
+
"zero_layer",
|
|
20
|
+
"starting_thickness",
|
|
21
|
+
"igi",
|
|
22
|
+
"diamond",
|
|
23
|
+
"thinnest_layer",
|
|
24
|
+
"boundary_layer",
|
|
25
|
+
"boundary_matrices",
|
|
26
|
+
"unscattered_rt",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def zero_layer(sample):
|
|
31
|
+
"""Create R and T matrices for thinnest_layer with zero thickness.
|
|
32
|
+
|
|
33
|
+
Need to include quadrature normalization so R+T=1.
|
|
34
|
+
"""
|
|
35
|
+
if sample.twonuw is None:
|
|
36
|
+
sample.update_quadrature()
|
|
37
|
+
t = np.diagflat(1 / sample.twonuw)
|
|
38
|
+
r = np.zeros_like(t)
|
|
39
|
+
return r, t
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def starting_thickness(sample):
|
|
43
|
+
"""Find the best starting thickness to start the doubling process.
|
|
44
|
+
|
|
45
|
+
The criterion is based on an assessment of the (1) round-off error,
|
|
46
|
+
(2) the angular initialization error, and (3) the thickness
|
|
47
|
+
initialization error. Wiscombe concluded that an optimal
|
|
48
|
+
starting thickness depends on the smallest quadrature slab.nu, and
|
|
49
|
+
recommends that when either the infinitesimal generator or diamond
|
|
50
|
+
initialization methods are used then the initial thickness is optimal
|
|
51
|
+
when type 2 and 3 errors are comparable, or when d ≈ µ.
|
|
52
|
+
|
|
53
|
+
Note that round-off is important when the starting thickness is less than
|
|
54
|
+
1e-4 for diamond initialization and less than 1e-8 for infinitesimal
|
|
55
|
+
generator initialization assuming about 14 significant digits of accuracy.
|
|
56
|
+
|
|
57
|
+
Since the final thickness is determined by repeated doubling, the
|
|
58
|
+
starting thickness is found by dividing by 2 until the starting thickness is
|
|
59
|
+
less than 'mu'. Also we make checks for a thinnest_layer with zero thickness
|
|
60
|
+
and one that infinitely thick.
|
|
61
|
+
"""
|
|
62
|
+
if sample.nu is None:
|
|
63
|
+
sample.update_quadrature()
|
|
64
|
+
|
|
65
|
+
if sample.b <= 0:
|
|
66
|
+
return 0.0
|
|
67
|
+
|
|
68
|
+
if sample.b == np.inf:
|
|
69
|
+
return sample.nu[0] / 2.0
|
|
70
|
+
|
|
71
|
+
dd = sample.b_delta_M()
|
|
72
|
+
while dd > sample.nu[0]:
|
|
73
|
+
dd /= 2
|
|
74
|
+
|
|
75
|
+
return dd
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def igi(sample):
|
|
79
|
+
r"""Infinitesmal Generator Initialization.
|
|
80
|
+
|
|
81
|
+
igi_start() generates the starting matrix with the inifinitesimal generator method.
|
|
82
|
+
The accuracy is O(d) and assumes that the average irradiance upwards is
|
|
83
|
+
equal to that travelling downwards at the top and the average radiance upwards
|
|
84
|
+
equals that moving upwards from the bottom.
|
|
85
|
+
|
|
86
|
+
Ultimately the formulas for the reflection matrix is
|
|
87
|
+
|
|
88
|
+
.. math:: R_{ij} = a d/(4\nu_i\nu_j) hpm_{ij}
|
|
89
|
+
|
|
90
|
+
and
|
|
91
|
+
|
|
92
|
+
.. math:: T_{ij} = a d/(4\nu_i\nu_j) hpp_{ij} + \delta_{ij}(1-d/\nu_i)/(2\nu_i w_i)
|
|
93
|
+
"""
|
|
94
|
+
if sample.nu is None:
|
|
95
|
+
sample.update_quadrature()
|
|
96
|
+
|
|
97
|
+
if sample.b_thinnest is None:
|
|
98
|
+
sample.b_thinnest = starting_thickness(sample)
|
|
99
|
+
|
|
100
|
+
n = sample.quad_pts
|
|
101
|
+
d = sample.b_thinnest
|
|
102
|
+
|
|
103
|
+
if sample.hp is None:
|
|
104
|
+
sample.hp, sample.hm = iad.phase_legendre(sample)
|
|
105
|
+
|
|
106
|
+
temp = sample.a_delta_M() * d / 4 / sample.nu
|
|
107
|
+
R = temp * (sample.hm / sample.nu).T
|
|
108
|
+
T = temp * (sample.hp / sample.nu).T
|
|
109
|
+
T += (1 - d / sample.nu) / sample.twonuw * np.identity(n)
|
|
110
|
+
|
|
111
|
+
return R, T
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def diamond(sample):
|
|
115
|
+
"""Diamond initialization.
|
|
116
|
+
|
|
117
|
+
The diamond method uses the same `r_hat` and `t_hat` as was used for infinitesimal
|
|
118
|
+
generator method. Division by `2*nu_j*w_j` is not needed until the
|
|
119
|
+
final values for `R` and `T` are formed.
|
|
120
|
+
"""
|
|
121
|
+
if sample.nu is None:
|
|
122
|
+
sample.update_quadrature()
|
|
123
|
+
|
|
124
|
+
if sample.b_thinnest is None:
|
|
125
|
+
sample.b_thinnest = starting_thickness(sample)
|
|
126
|
+
|
|
127
|
+
n = sample.quad_pts
|
|
128
|
+
d = sample.b_thinnest
|
|
129
|
+
II = np.identity(n)
|
|
130
|
+
|
|
131
|
+
if sample.hp is None:
|
|
132
|
+
sample.hp, sample.hm = iad.phase_legendre(sample)
|
|
133
|
+
|
|
134
|
+
w = sample.twonuw / sample.nu / 2
|
|
135
|
+
temp = sample.a_delta_M() * d * w / 4
|
|
136
|
+
r_hat = temp * (sample.hm / sample.nu).T
|
|
137
|
+
t_hat = d / (2 * sample.nu) * II - temp * (sample.hp / sample.nu).T
|
|
138
|
+
|
|
139
|
+
C = np.linalg.solve((II + t_hat).T, r_hat.T)
|
|
140
|
+
G = 0.5 * (II + t_hat - C.T @ r_hat).T
|
|
141
|
+
lu, piv = scipy.linalg.lu_factor(G)
|
|
142
|
+
|
|
143
|
+
D = 1 / sample.twonuw * (C * sample.twonuw).T
|
|
144
|
+
R = scipy.linalg.lu_solve((lu, piv), D).T / sample.twonuw
|
|
145
|
+
T = scipy.linalg.lu_solve((lu, piv), II).T / sample.twonuw
|
|
146
|
+
T -= II.T / sample.twonuw
|
|
147
|
+
return R, T
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def thinnest_layer(sample):
|
|
151
|
+
"""Reflection and transmission matrices for a thin thinnest_layer.
|
|
152
|
+
|
|
153
|
+
The optimal method for calculating a thin thinnest_layer depends on the
|
|
154
|
+
thinness of the thinnest_layer and the quadrature angles. This chooses
|
|
155
|
+
the best method using Wiscombe's criteria.
|
|
156
|
+
"""
|
|
157
|
+
if sample.b <= 0:
|
|
158
|
+
return zero_layer(sample)
|
|
159
|
+
|
|
160
|
+
sample.b_thinnest = starting_thickness(sample)
|
|
161
|
+
|
|
162
|
+
if sample.b_thinnest < 1e-4 or sample.b_thinnest < 0.09 * sample.nu[0]:
|
|
163
|
+
return igi(sample)
|
|
164
|
+
|
|
165
|
+
return diamond(sample)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _boundary(sample, n_i, n_g, n_t, b, nu_i):
|
|
169
|
+
"""Find matrix for R and T for air/glass/slab interface.
|
|
170
|
+
|
|
171
|
+
The resulting matrix is a diagonal matrix that is represented as an
|
|
172
|
+
array.
|
|
173
|
+
|
|
174
|
+
The reflection matrix is the same entering or exiting the slab. The
|
|
175
|
+
transmission matrices should differ by a factor of
|
|
176
|
+
(n_slab/n_outside)**4, due to n**2 law of radiance, but there is some
|
|
177
|
+
inconsistency in the program and if I use this principle then regular
|
|
178
|
+
calculations for R and T don't work and the fluence calculations still
|
|
179
|
+
don't work. So punted and took all that code out.
|
|
180
|
+
|
|
181
|
+
"""
|
|
182
|
+
r, t = iad.absorbing_glass_RT(n_i, n_g, n_t, nu_i, b)
|
|
183
|
+
r *= sample.twonuw
|
|
184
|
+
return r, t
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def boundary_layer(s, top=True):
|
|
188
|
+
"""Create reflection and transmission arrays for a boundary_layer.
|
|
189
|
+
|
|
190
|
+
If 'boundary_layer=="top"' then the arrays returned are for the top
|
|
191
|
+
surface and the labels are as expected i.e. 'T01' is the reflection for
|
|
192
|
+
light from air passing to the slab. Otherwise the calculations are made
|
|
193
|
+
for the bottom surface and the labels are backwards i.e. 'T01 == T32'
|
|
194
|
+
and 'T10 == T23', where 0 is the first air slide surface, 1 is the
|
|
195
|
+
slide/slab surface, 2 is the second slide/slab surface, and 3 is the
|
|
196
|
+
bottom slide/air surface.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
s: slab
|
|
200
|
+
top: True if this is the top slide
|
|
201
|
+
Returns:
|
|
202
|
+
R01: reflection array from air to slab
|
|
203
|
+
R10: reflection array from slab to air
|
|
204
|
+
T01: transmission array from air to slab
|
|
205
|
+
T10: transmission array from slab to air
|
|
206
|
+
"""
|
|
207
|
+
if s.nu is None:
|
|
208
|
+
s.update_quadrature()
|
|
209
|
+
|
|
210
|
+
n_slab_transport = s.n
|
|
211
|
+
n_slab_boundary = getattr(s, "n_sample_boundary", s.n)
|
|
212
|
+
|
|
213
|
+
if top:
|
|
214
|
+
nu_01 = iad.cos_snell(n_slab_transport, s.nu, iad.fresnel._transport_index(s.n_outer_above))
|
|
215
|
+
R01, T01 = _boundary(s, s.n_outer_above, s.n_above, n_slab_boundary, s.b_above, nu_01)
|
|
216
|
+
R10, T10 = _boundary(s, n_slab_boundary, s.n_above, s.n_outer_above, s.b_above, s.nu)
|
|
217
|
+
else:
|
|
218
|
+
nu_23 = iad.cos_snell(n_slab_transport, s.nu, iad.fresnel._transport_index(s.n_outer_below))
|
|
219
|
+
R10, T10 = _boundary(s, s.n_outer_below, s.n_below, n_slab_boundary, s.b_below, nu_23)
|
|
220
|
+
R01, T01 = _boundary(s, n_slab_boundary, s.n_below, s.n_outer_below, s.b_below, s.nu)
|
|
221
|
+
return R01, R10, T01, T10
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def boundary_matrices(s, top=True):
|
|
225
|
+
"""Create reflection and transmission matrices for a boundary_layer.
|
|
226
|
+
|
|
227
|
+
These can be treated like any other layer in the adding-doubling
|
|
228
|
+
formalism.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
s: slab
|
|
232
|
+
top: True if this is the top boundary
|
|
233
|
+
Returns:
|
|
234
|
+
R01: reflection array from air to slab
|
|
235
|
+
R10: reflection array from slab to air
|
|
236
|
+
T01: transmission array from air to slab
|
|
237
|
+
T10: transmission array from slab to air
|
|
238
|
+
"""
|
|
239
|
+
R01, R10, T01, T10 = boundary_layer(s, top=top)
|
|
240
|
+
rr01 = np.diagflat(R01 / s.twonuw**2)
|
|
241
|
+
rr10 = np.diagflat(R10 / s.twonuw**2)
|
|
242
|
+
tt01 = np.diagflat(T01 / s.twonuw)
|
|
243
|
+
tt10 = np.diagflat(T10 / s.twonuw)
|
|
244
|
+
|
|
245
|
+
return rr01, rr10, tt01, tt10
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def unscattered_rt(s):
|
|
249
|
+
"""Unscattered reflection and transmission for a slide-slab-slide sandwich.
|
|
250
|
+
|
|
251
|
+
The sample is characterized by the record 'slab'. The total unscattered_layer
|
|
252
|
+
reflection and transmission for oblique irradiance ('urx' and 'utx')
|
|
253
|
+
together with their companions 'uru' and 'utu' for diffuse irradiance.
|
|
254
|
+
The cosine of the incident angle is specified by 'slab.cos_angle'.
|
|
255
|
+
|
|
256
|
+
The way that this routine calculates the diffuse unscattered_layer quantities
|
|
257
|
+
based on the global quadrature angles previously set-up. Consequently,
|
|
258
|
+
these estimates are not exact. In fact if 'n=4' then only two
|
|
259
|
+
quadrature points will actually be used to figure out the diffuse
|
|
260
|
+
reflection and transmission (assuming mismatched boundaries).
|
|
261
|
+
|
|
262
|
+
This algorithm is pretty simple. Since the quadrature angles are all
|
|
263
|
+
chosen assuming points inside the medium, I must calculate the
|
|
264
|
+
corresponding angle for light entering from the outside. If the the
|
|
265
|
+
cosine of this angle is greater than zero then the angle does not
|
|
266
|
+
correspond to a direction in which light is totally internally
|
|
267
|
+
reflected. For this ray, I find the unscattered_layer that would be reflected
|
|
268
|
+
or transmitted from the slab. I multiply this by the quadrature angle
|
|
269
|
+
and weight 'twoaw[i]' to get the total diffuse reflectance and
|
|
270
|
+
transmittance.
|
|
271
|
+
|
|
272
|
+
Oh, yes. The mysterious multiplication by a factor of 'n_slab*n_slab'
|
|
273
|
+
is required to account for the n**2-law of radiance.
|
|
274
|
+
"""
|
|
275
|
+
r01, t01 = iad.zero_layer(s)
|
|
276
|
+
r10, t10 = iad.zero_layer(s)
|
|
277
|
+
|
|
278
|
+
nu_out = iad.cos_snell(s.n, s.nu, iad.fresnel._transport_index(s.n_outer_above))
|
|
279
|
+
r01, t01 = iad.specular_rt(
|
|
280
|
+
s.n_above,
|
|
281
|
+
getattr(s, "n_sample_boundary", s.n),
|
|
282
|
+
s.n_below,
|
|
283
|
+
s.b,
|
|
284
|
+
nu_out,
|
|
285
|
+
s.b_above,
|
|
286
|
+
s.b_below,
|
|
287
|
+
n_outer_top=s.n_outer_above,
|
|
288
|
+
n_outer_bot=s.n_outer_below,
|
|
289
|
+
n_slab_transport=s.n,
|
|
290
|
+
)
|
|
291
|
+
r10, t10 = iad.specular_rt(
|
|
292
|
+
s.n_below,
|
|
293
|
+
getattr(s, "n_sample_boundary", s.n),
|
|
294
|
+
s.n_above,
|
|
295
|
+
s.b,
|
|
296
|
+
iad.cos_snell(s.n, s.nu, iad.fresnel._transport_index(s.n_outer_below)),
|
|
297
|
+
s.b_below,
|
|
298
|
+
s.b_above,
|
|
299
|
+
n_outer_top=s.n_outer_below,
|
|
300
|
+
n_outer_bot=s.n_outer_above,
|
|
301
|
+
n_slab_transport=s.n,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
rr01 = np.diagflat(r01) / s.twonuw
|
|
305
|
+
rr10 = np.diagflat(r10) / s.twonuw
|
|
306
|
+
tt01 = np.diagflat(t01) / s.twonuw
|
|
307
|
+
tt10 = np.diagflat(t10) / s.twonuw
|
|
308
|
+
|
|
309
|
+
return rr01, rr10, tt01, tt10
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def unscattered(s):
|
|
313
|
+
"""Unscattered reflection and transmission for a slide-slab-slide sandwich.
|
|
314
|
+
|
|
315
|
+
The sample is characterized by the record 'slab'. The total unscattered_layer
|
|
316
|
+
reflection and transmission for oblique irradiance ('urx' and 'utx')
|
|
317
|
+
together with their companions 'uru' and 'utu' for diffuse irradiance.
|
|
318
|
+
The cosine of the incident angle is specified by 'slab.cos_angle'.
|
|
319
|
+
|
|
320
|
+
The way that this routine calculates the diffuse unscattered_layer quantities
|
|
321
|
+
based on the global quadrature angles previously set-up. Consequently,
|
|
322
|
+
these estimates are not exact. In fact if 'n=4' then only two
|
|
323
|
+
quadrature points will actually be used to figure out the diffuse
|
|
324
|
+
reflection and transmission (assuming mismatched boundaries).
|
|
325
|
+
|
|
326
|
+
This algorithm is pretty simple. Since the quadrature angles are all
|
|
327
|
+
chosen assuming points inside the medium, I must calculate the
|
|
328
|
+
corresponding angle for light entering from the outside. If the the
|
|
329
|
+
cosine of this angle is greater than zero then the angle does not
|
|
330
|
+
correspond to a direction in which light is totally internally
|
|
331
|
+
reflected. For this ray, I find the unscattered_layer that would be reflected
|
|
332
|
+
or transmitted from the slab. I multiply this by the quadrature angle
|
|
333
|
+
and weight 'twoaw[i]' to get the total diffuse reflectance and
|
|
334
|
+
transmittance.
|
|
335
|
+
|
|
336
|
+
Oh, yes. The mysterious multiplication by a factor of 'n_slab*n_slab'
|
|
337
|
+
is required to account for the n**2-law of radiance.
|
|
338
|
+
"""
|
|
339
|
+
n = s.quad_pts
|
|
340
|
+
|
|
341
|
+
uru = 0
|
|
342
|
+
utu = 0
|
|
343
|
+
|
|
344
|
+
for i in range(n):
|
|
345
|
+
nu_outside = iad.cos_snell(s.n, s.nu[i], iad.fresnel._transport_index(s.n_outer_above))
|
|
346
|
+
if nu_outside > 0:
|
|
347
|
+
r, t = iad.specular_rt(
|
|
348
|
+
s.n_above,
|
|
349
|
+
getattr(s, "n_sample_boundary", s.n),
|
|
350
|
+
s.n_below,
|
|
351
|
+
s.b,
|
|
352
|
+
nu_outside,
|
|
353
|
+
s.b_above,
|
|
354
|
+
s.b_below,
|
|
355
|
+
n_outer_top=s.n_outer_above,
|
|
356
|
+
n_outer_bot=s.n_outer_below,
|
|
357
|
+
n_slab_transport=s.n,
|
|
358
|
+
)
|
|
359
|
+
uru += s.twonuw[i] * r[i, i]
|
|
360
|
+
utu += s.twonuw[i] * t[i, i]
|
|
361
|
+
|
|
362
|
+
ur1, ut1 = iad.specular_rt(
|
|
363
|
+
s.n_above,
|
|
364
|
+
getattr(s, "n_sample_boundary", s.n),
|
|
365
|
+
s.n_below,
|
|
366
|
+
s.b,
|
|
367
|
+
s.nu_0,
|
|
368
|
+
s.b_above,
|
|
369
|
+
s.b_below,
|
|
370
|
+
n_outer_top=s.n_outer_above,
|
|
371
|
+
n_outer_bot=s.n_outer_below,
|
|
372
|
+
n_slab_transport=s.n,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
uru *= s.n**2
|
|
376
|
+
utu *= s.n**2
|
|
377
|
+
return ur1, ut1, uru, utu
|
iadpython/txt.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""Module for reading txt files.
|
|
2
|
+
|
|
3
|
+
This reads an output txt file and saves the parameters into an
|
|
4
|
+
Experiment object.
|
|
5
|
+
|
|
6
|
+
Example::
|
|
7
|
+
|
|
8
|
+
>>> import numpy
|
|
9
|
+
>>> import matplotlib.pyplot as plt
|
|
10
|
+
>>> import iadpython
|
|
11
|
+
>>>
|
|
12
|
+
>>> filename = 'ink.txt'
|
|
13
|
+
>>> exp, data = iadpython.read_txt(filename)
|
|
14
|
+
>>> plt.plot(data.lam, data.mr)
|
|
15
|
+
>>> plt.plot(data.lam, data.rr)
|
|
16
|
+
>>> plt.xlabel("Wavelength")
|
|
17
|
+
>>> plt.ylabel("measured reflectance")
|
|
18
|
+
>>> plt.title(filename)
|
|
19
|
+
>>> plt.show()
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import os
|
|
23
|
+
import re
|
|
24
|
+
import numpy as np
|
|
25
|
+
import iadpython
|
|
26
|
+
|
|
27
|
+
__all__ = ("read_txt", "IADResult")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class IADResult:
|
|
31
|
+
"""Container class results in an iad output file."""
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
"""Initialization."""
|
|
35
|
+
self.lam = np.array([0], dtype=float)
|
|
36
|
+
self.mr = np.array([0], dtype=float)
|
|
37
|
+
self.cr = np.array([0], dtype=float)
|
|
38
|
+
self.mt = np.array([0], dtype=float)
|
|
39
|
+
self.ct = np.array([0], dtype=float)
|
|
40
|
+
self.mua = np.array([0], dtype=float)
|
|
41
|
+
self.musp = np.array([0], dtype=float)
|
|
42
|
+
self.g = np.array([0], dtype=float)
|
|
43
|
+
self.success = np.array([0], dtype=bool)
|
|
44
|
+
self.mus = np.array([0], dtype=float)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def verify_magic(fp, magic):
|
|
48
|
+
"""Verify that the file's initial bytes match the string 'magic'."""
|
|
49
|
+
fp.seek(0)
|
|
50
|
+
chunk = fp.read(len(magic))
|
|
51
|
+
fp.seek(0)
|
|
52
|
+
return chunk == magic
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_number_from_line(fp):
|
|
56
|
+
"""Read the number on the next line after an = sign."""
|
|
57
|
+
s = fp.readline()
|
|
58
|
+
# print("starting=%s" % s)
|
|
59
|
+
if s[0] != "#":
|
|
60
|
+
print("line in file should start with `#`")
|
|
61
|
+
return 0
|
|
62
|
+
|
|
63
|
+
s = re.sub(r".*= *", "", s)
|
|
64
|
+
s = re.sub(r" .*", "", s)
|
|
65
|
+
|
|
66
|
+
# print("finish x=%10.5f" % float(s))
|
|
67
|
+
return float(s)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def read_sphere(fp):
|
|
71
|
+
"""Read the information for a sphere."""
|
|
72
|
+
line = fp.readline()
|
|
73
|
+
d_sphere = get_number_from_line(fp)
|
|
74
|
+
d_sample = get_number_from_line(fp)
|
|
75
|
+
sphere = iadpython.Sphere(d_sphere, d_sample)
|
|
76
|
+
sphere.baffle = "a baffle" in line
|
|
77
|
+
sphere.third.d = get_number_from_line(fp)
|
|
78
|
+
sphere.detector.d = get_number_from_line(fp)
|
|
79
|
+
sphere.detector.uru = get_number_from_line(fp) / 100
|
|
80
|
+
sphere.r_wall = get_number_from_line(fp) / 100
|
|
81
|
+
sphere.r_std = get_number_from_line(fp) / 100
|
|
82
|
+
return sphere
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def read_misc(fp, _exp):
|
|
86
|
+
"""Read info after sphere data but before data."""
|
|
87
|
+
for _ in range(14):
|
|
88
|
+
fp.readline()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def read_txt(filename):
|
|
92
|
+
"""Read an IAD output file in .rxt format."""
|
|
93
|
+
if not os.path.exists(filename):
|
|
94
|
+
raise ValueError('input file "%s" must end in ".txt"' % filename)
|
|
95
|
+
|
|
96
|
+
# verify that file is an output file
|
|
97
|
+
with open(filename, encoding="utf-8") as fp:
|
|
98
|
+
if not verify_magic(fp, "# Inverse Adding-Doubling"):
|
|
99
|
+
raise ValueError('"%s" does not start with "# Inverse Adding-Doubling"' % filename)
|
|
100
|
+
|
|
101
|
+
# create experiment object
|
|
102
|
+
exp = iadpython.Experiment()
|
|
103
|
+
|
|
104
|
+
# now read the header
|
|
105
|
+
fp.readline()
|
|
106
|
+
fp.readline()
|
|
107
|
+
exp.d_beam = get_number_from_line(fp)
|
|
108
|
+
exp.sample.d = get_number_from_line(fp)
|
|
109
|
+
exp.sample.d_above = get_number_from_line(fp)
|
|
110
|
+
exp.sample.d_below = get_number_from_line(fp)
|
|
111
|
+
exp.sample.n = get_number_from_line(fp)
|
|
112
|
+
exp.sample.n_above = get_number_from_line(fp)
|
|
113
|
+
exp.sample.n_below = get_number_from_line(fp)
|
|
114
|
+
fp.readline()
|
|
115
|
+
exp.fraction_of_rc_in_mr = get_number_from_line(fp) / 100
|
|
116
|
+
exp.fraction_of_tc_in_mt = get_number_from_line(fp) / 100
|
|
117
|
+
fp.readline()
|
|
118
|
+
exp.r_sphere = read_sphere(fp)
|
|
119
|
+
fp.readline()
|
|
120
|
+
exp.t_sphere = read_sphere(fp)
|
|
121
|
+
read_misc(fp, exp)
|
|
122
|
+
|
|
123
|
+
data = IADResult()
|
|
124
|
+
position = fp.tell()
|
|
125
|
+
|
|
126
|
+
result = np.loadtxt(fp, usecols=range(8), delimiter="\t")
|
|
127
|
+
lam, mr, cr, mt, ct, mua, musp, g = result.T
|
|
128
|
+
data.lam = np.atleast_1d(lam)
|
|
129
|
+
data.mr = np.atleast_1d(mr)
|
|
130
|
+
data.cr = np.atleast_1d(cr)
|
|
131
|
+
data.mt = np.atleast_1d(mt)
|
|
132
|
+
data.ct = np.atleast_1d(ct)
|
|
133
|
+
data.mua = np.atleast_1d(mua)
|
|
134
|
+
data.musp = np.atleast_1d(musp)
|
|
135
|
+
data.g = np.atleast_1d(g)
|
|
136
|
+
data.mus = data.musp / (1 - g)
|
|
137
|
+
exp.m_r = data.mr
|
|
138
|
+
exp.m_t = data.mt
|
|
139
|
+
exp.lambda0 = data.lam
|
|
140
|
+
|
|
141
|
+
fp.seek(position)
|
|
142
|
+
converters = {8: lambda s: s.lstrip("#").strip()}
|
|
143
|
+
status = np.loadtxt(fp, usecols=[8], dtype=str, converters=converters)
|
|
144
|
+
status = np.atleast_1d(status)
|
|
145
|
+
data.success = status == "*"
|
|
146
|
+
|
|
147
|
+
return exp, data
|