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/iadcommand.py
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Command line for support for iadpython."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from enum import Enum
|
|
7
|
+
import argparse
|
|
8
|
+
from contextlib import redirect_stdout
|
|
9
|
+
import time
|
|
10
|
+
import numpy as np
|
|
11
|
+
import iadpython
|
|
12
|
+
|
|
13
|
+
# Global COUNTER to mimic static behavior in C
|
|
14
|
+
COUNTER = 0
|
|
15
|
+
ANY_ERROR = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SlidePosition(Enum):
|
|
19
|
+
"""All the combinations of glass and sample."""
|
|
20
|
+
|
|
21
|
+
NO_SLIDES = 0
|
|
22
|
+
ONE_SLIDE_ON_TOP = 1
|
|
23
|
+
TWO_IDENTICAL_SLIDES = 2
|
|
24
|
+
ONE_SLIDE_ON_BOTTOM = 3
|
|
25
|
+
ONE_SLIDE_NEAR_SPHERE = 4
|
|
26
|
+
ONE_SLIDE_NOT_NEAR_SPHERE = 5
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class LightCondition(Enum):
|
|
30
|
+
"""Options for interpreting illumination."""
|
|
31
|
+
|
|
32
|
+
MR_IS_ONLY_RD = 1
|
|
33
|
+
MT_IS_ONLY_TD = 2
|
|
34
|
+
NO_UNSCATTERED_LIGHT = 3
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class InputError(Enum):
|
|
38
|
+
"""Possible input errors."""
|
|
39
|
+
|
|
40
|
+
NO_ERROR = 0
|
|
41
|
+
TOO_MANY_ITERATIONS = 1
|
|
42
|
+
MR_TOO_BIG = 2
|
|
43
|
+
MR_TOO_SMALL = 3
|
|
44
|
+
MT_TOO_BIG = 4
|
|
45
|
+
MT_TOO_SMALL = 5
|
|
46
|
+
MU_TOO_BIG = 6
|
|
47
|
+
MU_TOO_SMALL = 7
|
|
48
|
+
TOO_MUCH_LIGHT = 8
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def print_error_legend():
|
|
52
|
+
"""Print error explanation and quit."""
|
|
53
|
+
print("----------------- Sorry, but ... errors encountered ---------------")
|
|
54
|
+
print(" * ==> Success ")
|
|
55
|
+
print(" 0-9 ==> Monte Carlo Iteration")
|
|
56
|
+
print(" R ==> M_R is too big ")
|
|
57
|
+
print(" r ==> M_R is too small")
|
|
58
|
+
print(" T ==> M_T is too big ")
|
|
59
|
+
print(" t ==> M_T is too small")
|
|
60
|
+
print(" U ==> M_U is too big ")
|
|
61
|
+
print(" u ==> M_U is too small")
|
|
62
|
+
print(" ! ==> M_R + M_T > 1 ")
|
|
63
|
+
print(" + ==> Did not converge\n")
|
|
64
|
+
sys.exit(0)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def what_char(err):
|
|
68
|
+
"""Return appropriate character for analysis of current datapoint."""
|
|
69
|
+
if err == InputError.NO_ERROR:
|
|
70
|
+
return "*"
|
|
71
|
+
if err == InputError.TOO_MANY_ITERATIONS:
|
|
72
|
+
return "+"
|
|
73
|
+
if err == InputError.MR_TOO_BIG:
|
|
74
|
+
return "R"
|
|
75
|
+
if err == InputError.MR_TOO_SMALL:
|
|
76
|
+
return "r"
|
|
77
|
+
if err == InputError.MT_TOO_BIG:
|
|
78
|
+
return "T"
|
|
79
|
+
if err == InputError.MT_TOO_SMALL:
|
|
80
|
+
return "t"
|
|
81
|
+
if err == InputError.MU_TOO_BIG:
|
|
82
|
+
return "U"
|
|
83
|
+
if err == InputError.MU_TOO_SMALL:
|
|
84
|
+
return "u"
|
|
85
|
+
if err == InputError.TOO_MUCH_LIGHT:
|
|
86
|
+
return "!"
|
|
87
|
+
return "?"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def print_dot(start_time, err, points, final, verbosity):
|
|
91
|
+
"""Print a character for each datapoint during analysis."""
|
|
92
|
+
global COUNTER # pylint: disable=global-statement
|
|
93
|
+
global ANY_ERROR # pylint: disable=global-statement
|
|
94
|
+
COUNTER += 1
|
|
95
|
+
|
|
96
|
+
if err != InputError.NO_ERROR:
|
|
97
|
+
ANY_ERROR = err
|
|
98
|
+
|
|
99
|
+
if verbosity == 0:
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
if final == 99:
|
|
103
|
+
print(what_char(err), end="")
|
|
104
|
+
else:
|
|
105
|
+
COUNTER -= 1
|
|
106
|
+
print(f"{final % 10}\b", end="")
|
|
107
|
+
|
|
108
|
+
if final == 99:
|
|
109
|
+
if COUNTER % 50 == 0:
|
|
110
|
+
rate = (time.time() - start_time) / points
|
|
111
|
+
print(f" {points} done ({rate:.2f} s/pt)")
|
|
112
|
+
elif COUNTER % 10 == 0:
|
|
113
|
+
print(" ", end="")
|
|
114
|
+
|
|
115
|
+
sys.stdout.flush()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def validator_01(value):
|
|
119
|
+
"""Is value between 0 and 1."""
|
|
120
|
+
try:
|
|
121
|
+
fvalue = float(value)
|
|
122
|
+
except ValueError as exc:
|
|
123
|
+
raise argparse.ArgumentTypeError(f"Commandline: {value} is not a valid number") from exc
|
|
124
|
+
if not 0 <= fvalue <= 1:
|
|
125
|
+
raise argparse.ArgumentTypeError(f"Commandline: {value} is not between 0 and 1")
|
|
126
|
+
return fvalue
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def validator_11(value):
|
|
130
|
+
"""Is value between -1 and 1."""
|
|
131
|
+
try:
|
|
132
|
+
fvalue = float(value)
|
|
133
|
+
except ValueError as exc:
|
|
134
|
+
raise argparse.ArgumentTypeError(f"{value} is not a valid number") from exc
|
|
135
|
+
if not -1 <= fvalue <= 1:
|
|
136
|
+
raise argparse.ArgumentTypeError(f"Commandline: {value} is not between -1 and 1")
|
|
137
|
+
return fvalue
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def validator_positive(value):
|
|
141
|
+
"""Is value non-negative."""
|
|
142
|
+
try:
|
|
143
|
+
fvalue = float(value)
|
|
144
|
+
except ValueError as exc:
|
|
145
|
+
raise argparse.ArgumentTypeError(f"Commandline: {value} is not a valid number") from exc
|
|
146
|
+
if fvalue < 0:
|
|
147
|
+
raise argparse.ArgumentTypeError(f"{value} is not positive")
|
|
148
|
+
return fvalue
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def validator_mc_iterations(value):
|
|
152
|
+
"""Is number of Monte Carlo iterations between 0 and 50."""
|
|
153
|
+
try:
|
|
154
|
+
ivalue = int(value)
|
|
155
|
+
except ValueError as exc:
|
|
156
|
+
raise argparse.ArgumentTypeError(f"Commandline: {value} is not a valid integer") from exc
|
|
157
|
+
if not 0 <= ivalue <= 50:
|
|
158
|
+
raise argparse.ArgumentTypeError(f"Commandline: {value} must be between 0 and 50")
|
|
159
|
+
return ivalue
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# Argument specifications
|
|
163
|
+
arg_specs = [
|
|
164
|
+
{
|
|
165
|
+
"flags": ["-1"],
|
|
166
|
+
"dest": "r_sphere",
|
|
167
|
+
"metavar": ("SPHERE_D", "SAMPLE_D", "ENTRANCE_D", "DETECTOR_D", "WALL_R"),
|
|
168
|
+
"nargs": 5,
|
|
169
|
+
"type": float,
|
|
170
|
+
"help": "Reflection sphere parameters",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"flags": ["-2"],
|
|
174
|
+
"dest": "t_sphere",
|
|
175
|
+
"metavar": ("SPHERE_D", "SAMPLE_D", "ENTRANCE_D", "DETECTOR_D", "WALL_R"),
|
|
176
|
+
"nargs": 5,
|
|
177
|
+
"type": float,
|
|
178
|
+
"help": "Transmission sphere parameters",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"flags": ["-a", "--albedo"],
|
|
182
|
+
"dest": "albedo",
|
|
183
|
+
"type": validator_01,
|
|
184
|
+
"help": "Use this albedo",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"flags": ["-A", "--mua"],
|
|
188
|
+
"dest": "mua",
|
|
189
|
+
"type": validator_positive,
|
|
190
|
+
"help": "Use this absorption coefficient",
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"flags": ["-b"],
|
|
194
|
+
"dest": "b",
|
|
195
|
+
"type": validator_positive,
|
|
196
|
+
"help": "Use this optical thickness",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"flags": ["-B", "--diameter"],
|
|
200
|
+
"dest": "diameter",
|
|
201
|
+
"type": validator_positive,
|
|
202
|
+
"help": "Beam diameter",
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"flags": ["-c"],
|
|
206
|
+
"dest": "f_r",
|
|
207
|
+
"type": validator_01,
|
|
208
|
+
"help": "Fraction of unscattered refl in MR",
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"flags": ["-C"],
|
|
212
|
+
"dest": "f_t",
|
|
213
|
+
"type": validator_01,
|
|
214
|
+
"help": "Fraction of unscattered trans in MT",
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"flags": ["-d"],
|
|
218
|
+
"dest": "thickness",
|
|
219
|
+
"type": validator_positive,
|
|
220
|
+
"help": "Thickness of sample",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"flags": ["-D"],
|
|
224
|
+
"dest": "slide_thickness",
|
|
225
|
+
"type": validator_positive,
|
|
226
|
+
"help": "Thickness of slide",
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"flags": ["-e"],
|
|
230
|
+
"dest": "error",
|
|
231
|
+
"type": validator_positive,
|
|
232
|
+
"help": "Error tolerance (default 0.0001)",
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"flags": ["-E"],
|
|
236
|
+
"dest": "E",
|
|
237
|
+
"type": validator_positive,
|
|
238
|
+
"help": "Optical depth (=mua*D) for slides",
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"flags": ["-f"],
|
|
242
|
+
"dest": "f_wall",
|
|
243
|
+
"type": validator_01,
|
|
244
|
+
"help": "Fraction 0.0-1.0 of light to hit sphere wall first",
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"flags": ["-F", "--mus"],
|
|
248
|
+
"dest": "mus",
|
|
249
|
+
"type": validator_positive,
|
|
250
|
+
"help": "Use this scattering coefficient",
|
|
251
|
+
},
|
|
252
|
+
{"flags": ["-g"], "type": validator_11, "help": "Scattering anisotropy"},
|
|
253
|
+
{
|
|
254
|
+
"flags": ["-G"],
|
|
255
|
+
"type": str,
|
|
256
|
+
"choices": ["0", "2", "t", "b", "n", "f"],
|
|
257
|
+
"help": "Type of boundary ",
|
|
258
|
+
},
|
|
259
|
+
{"flags": ["-i"], "type": float, "help": "Light incident at this angle in degrees"},
|
|
260
|
+
{"flags": ["-M"], "type": validator_mc_iterations, "help": "Number of Monte Carlo iterations"},
|
|
261
|
+
{
|
|
262
|
+
"flags": ["-n", "--nslab"],
|
|
263
|
+
"dest": "nslab",
|
|
264
|
+
"type": validator_positive,
|
|
265
|
+
"help": "Index of refraction of slab",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"flags": ["-N", "--nslide"],
|
|
269
|
+
"dest": "nslide",
|
|
270
|
+
"type": validator_positive,
|
|
271
|
+
"help": "Index of refraction of slides",
|
|
272
|
+
},
|
|
273
|
+
{"flags": ["-o"], "dest": "out_fname", "type": str, "help": "Filename for output"},
|
|
274
|
+
{"flags": ["-p"], "type": int, "help": "# of Monte Carlo photons (default 100000)"},
|
|
275
|
+
{
|
|
276
|
+
"flags": ["-q"],
|
|
277
|
+
"type": int,
|
|
278
|
+
"default": 8,
|
|
279
|
+
"help": "Number of quadrature points (default=8)",
|
|
280
|
+
},
|
|
281
|
+
{"flags": ["-r"], "type": validator_01, "help": "Total reflection measurement"},
|
|
282
|
+
{
|
|
283
|
+
"flags": ["-R"],
|
|
284
|
+
"type": validator_01,
|
|
285
|
+
"help": "Actual reflectance for 100%% measurement",
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"flags": ["-S"],
|
|
289
|
+
"type": int,
|
|
290
|
+
"choices": [0, 1, 2],
|
|
291
|
+
"help": "Number of spheres used",
|
|
292
|
+
},
|
|
293
|
+
{"flags": ["-t"], "type": validator_01, "help": "Total transmission measurement"},
|
|
294
|
+
{
|
|
295
|
+
"flags": ["-T"],
|
|
296
|
+
"type": validator_01,
|
|
297
|
+
"help": "Actual transmission for 100%% measurement",
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"flags": ["-u"],
|
|
301
|
+
"type": validator_01,
|
|
302
|
+
"help": "Unscattered transmission measurement",
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"flags": ["-v", "--version"],
|
|
306
|
+
"action": "version",
|
|
307
|
+
"version": "iadp " + iadpython.__version__,
|
|
308
|
+
"help": "short version",
|
|
309
|
+
},
|
|
310
|
+
{"flags": ["-V"], "action": "store_true", "help": "long version"},
|
|
311
|
+
{
|
|
312
|
+
"flags": ["--verbosity"],
|
|
313
|
+
"type": int,
|
|
314
|
+
"choices": [0, 1, 2],
|
|
315
|
+
"help": "Verbosity level",
|
|
316
|
+
},
|
|
317
|
+
{"flags": ["-x"], "type": int, "help": "Set debugging level"},
|
|
318
|
+
{"flags": ["-X"], "action": "store_true", "help": "Dual beam configuration"},
|
|
319
|
+
{"flags": ["-z"], "action": "store_true", "help": "Do forward calculation"},
|
|
320
|
+
{
|
|
321
|
+
"flags": ["filename"],
|
|
322
|
+
"nargs": "?",
|
|
323
|
+
"type": str,
|
|
324
|
+
"default": None,
|
|
325
|
+
"help": "Input filename",
|
|
326
|
+
},
|
|
327
|
+
]
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def print_long_version():
|
|
331
|
+
"""Print the version information and quit."""
|
|
332
|
+
s = ""
|
|
333
|
+
s += " version: " + iadpython.__version__ + "\n"
|
|
334
|
+
s += " Author: " + iadpython.__author__ + "\n"
|
|
335
|
+
s += " Copyright: " + iadpython.__copyright__ + "\n"
|
|
336
|
+
s += " License: " + iadpython.__license__ + "\n"
|
|
337
|
+
s += " URL:" + iadpython.__url__ + "\n"
|
|
338
|
+
s += "\n Forward and inverse adding-doubling radiative transport calculations.\n"
|
|
339
|
+
s += " Extensive documentation is at <https://iadpython.readthedocs.io>\n"
|
|
340
|
+
print(s)
|
|
341
|
+
sys.exit(0)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def example_text():
|
|
345
|
+
"""Return a string with some command-line examples."""
|
|
346
|
+
s = ""
|
|
347
|
+
s += "Examples:\n"
|
|
348
|
+
s += " iad file.rxt Results will be put in file.txt\n"
|
|
349
|
+
s += " iad file Same as above\n"
|
|
350
|
+
s += " iad -c 0.9 file.rxt Assume M_R includes 90%% of unscattered reflectance\n"
|
|
351
|
+
s += " iad -C 0.8 file.rxt Assume M_T includes 80%% of unscattered transmittance\n"
|
|
352
|
+
s += " iad -e 0.0001 file.rxt Better convergence to R & T values\n"
|
|
353
|
+
s += " iad -f 1.0 file.rxt All light hits reflectance sphere wall first\n"
|
|
354
|
+
s += " iad -o out file.rxt Calculated values in out\n"
|
|
355
|
+
s += " iad -r 0.3 R_total=0.\n"
|
|
356
|
+
return s
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def add_sample_constraints(exp, args):
|
|
360
|
+
"""Command-line constraints on sample."""
|
|
361
|
+
if args.thickness is not None:
|
|
362
|
+
exp.sample.d = args.thickness
|
|
363
|
+
|
|
364
|
+
if args.slide_thickness is not None:
|
|
365
|
+
exp.sample.d_above = args.slide_thickness
|
|
366
|
+
exp.sample.d_below = args.slide_thickness
|
|
367
|
+
|
|
368
|
+
if args.E is not None:
|
|
369
|
+
exp.sample.b_above = args.E
|
|
370
|
+
exp.sample.b_below = args.E
|
|
371
|
+
|
|
372
|
+
if args.nslab is not None:
|
|
373
|
+
exp.sample.n = args.nslab
|
|
374
|
+
|
|
375
|
+
if args.nslide is not None:
|
|
376
|
+
exp.sample.n_above = args.nslide
|
|
377
|
+
exp.sample.n_below = args.nslide
|
|
378
|
+
|
|
379
|
+
if args.G is not None:
|
|
380
|
+
if args.nslide is None:
|
|
381
|
+
raise argparse.ArgumentTypeError("Commandline: Cannot use -G without also specifing slide index with -N")
|
|
382
|
+
if args.G == "0":
|
|
383
|
+
exp.sample.n_above = 1
|
|
384
|
+
exp.sample.n_below = 1
|
|
385
|
+
elif args.G == "t":
|
|
386
|
+
exp.sample.n_above = args.nslide
|
|
387
|
+
exp.sample.n_below = 1
|
|
388
|
+
elif args.G == "b":
|
|
389
|
+
exp.sample.n_above = 1
|
|
390
|
+
exp.sample.n_below = args.nslide
|
|
391
|
+
elif args.G == "2":
|
|
392
|
+
exp.sample.n_above = args.nslide
|
|
393
|
+
exp.sample.n_below = args.nslide
|
|
394
|
+
elif args.G == "n":
|
|
395
|
+
exp.flip_sample = True
|
|
396
|
+
exp.sample.n_above = args.nslide
|
|
397
|
+
exp.sample.n_below = 1
|
|
398
|
+
else: # must be 'f'
|
|
399
|
+
exp.flip_sample = True
|
|
400
|
+
exp.sample.n_above = 1
|
|
401
|
+
exp.sample.n_below = args.nslide
|
|
402
|
+
|
|
403
|
+
if args.i is not None:
|
|
404
|
+
if abs(args.i) > 90:
|
|
405
|
+
raise argparse.ArgumentTypeError("Commandline: Bad argument to -i, value must be between -90 and 90")
|
|
406
|
+
exp.sample.nu_0 = np.cos(np.radians(args.i))
|
|
407
|
+
|
|
408
|
+
if args.q is not None:
|
|
409
|
+
if args.q % 4:
|
|
410
|
+
raise argparse.ArgumentTypeError("Commandline: Number of quadrature points must be a multiple of 4")
|
|
411
|
+
if exp.sample.nu_0 != 1 and args.q % 12:
|
|
412
|
+
raise argparse.ArgumentTypeError("Commandline: Quadrature must be 12, 24, 36,... for oblique incidence")
|
|
413
|
+
exp.sample.quad_pts = args.q
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def add_experiment_constraints(exp, args):
|
|
417
|
+
"""Command-line constraints on experiment."""
|
|
418
|
+
|
|
419
|
+
def cli_sphere_to_object(values, refl):
|
|
420
|
+
"""Convert `-1`/`-2` CLI sphere values to a Sphere object."""
|
|
421
|
+
sphere_d, sample_d, entrance_d, detector_d, wall_r = values
|
|
422
|
+
return iadpython.Sphere(
|
|
423
|
+
sphere_d,
|
|
424
|
+
sample_d,
|
|
425
|
+
d_third=entrance_d,
|
|
426
|
+
d_detector=detector_d,
|
|
427
|
+
r_wall=wall_r,
|
|
428
|
+
refl=refl,
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
if args.S is not None:
|
|
432
|
+
exp.num_spheres = args.S
|
|
433
|
+
|
|
434
|
+
if args.r_sphere is not None:
|
|
435
|
+
exp.r_sphere = cli_sphere_to_object(args.r_sphere, refl=True)
|
|
436
|
+
|
|
437
|
+
if args.t_sphere is not None:
|
|
438
|
+
exp.t_sphere = cli_sphere_to_object(args.t_sphere, refl=False)
|
|
439
|
+
|
|
440
|
+
if args.diameter is not None:
|
|
441
|
+
exp.d_beam = args.diameter
|
|
442
|
+
|
|
443
|
+
if args.r is not None:
|
|
444
|
+
exp.m_r = args.r
|
|
445
|
+
|
|
446
|
+
if args.t is not None:
|
|
447
|
+
exp.m_t = args.t
|
|
448
|
+
|
|
449
|
+
if args.u is not None:
|
|
450
|
+
exp.m_u = args.u
|
|
451
|
+
|
|
452
|
+
rstd_r = getattr(args, "R", None)
|
|
453
|
+
rstd_t = getattr(args, "T", None)
|
|
454
|
+
if rstd_r is not None:
|
|
455
|
+
if exp.r_sphere is not None:
|
|
456
|
+
exp.r_sphere.r_std = rstd_r
|
|
457
|
+
if exp.t_sphere is not None and rstd_t is None:
|
|
458
|
+
exp.t_sphere.r_std = rstd_r
|
|
459
|
+
|
|
460
|
+
if rstd_t is not None:
|
|
461
|
+
if exp.t_sphere is not None:
|
|
462
|
+
exp.t_sphere.r_std = rstd_t
|
|
463
|
+
if exp.r_sphere is not None and rstd_r is None:
|
|
464
|
+
exp.r_sphere.r_std = rstd_t
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def add_analysis_constraints(exp, args):
|
|
468
|
+
"""Add command line constraints on analysis."""
|
|
469
|
+
# constraints on analysis
|
|
470
|
+
if args.albedo is not None:
|
|
471
|
+
exp.default_a = args.albedo
|
|
472
|
+
|
|
473
|
+
if args.b is not None:
|
|
474
|
+
exp.default_b = args.b
|
|
475
|
+
|
|
476
|
+
if args.g is not None:
|
|
477
|
+
exp.default_g = args.g
|
|
478
|
+
|
|
479
|
+
if args.mua is not None:
|
|
480
|
+
exp.default_mua = args.mua
|
|
481
|
+
|
|
482
|
+
if args.mus is not None:
|
|
483
|
+
exp.default_mus = args.mus
|
|
484
|
+
|
|
485
|
+
if args.f_r is not None:
|
|
486
|
+
exp.fraction_of_rc_in_mr = args.f_r
|
|
487
|
+
|
|
488
|
+
if args.f_t is not None:
|
|
489
|
+
exp.fraction_of_tc_in_mt = args.f_t
|
|
490
|
+
|
|
491
|
+
if getattr(args, "f_wall", None) is not None:
|
|
492
|
+
exp.f_r = args.f_wall
|
|
493
|
+
|
|
494
|
+
if getattr(args, "error", None) is not None:
|
|
495
|
+
exp.tolerance = args.error
|
|
496
|
+
exp.MC_tolerance = args.error
|
|
497
|
+
|
|
498
|
+
if args.M is not None:
|
|
499
|
+
exp.max_mc_iterations = args.M
|
|
500
|
+
|
|
501
|
+
if args.p is not None:
|
|
502
|
+
exp.n_photons = args.p
|
|
503
|
+
|
|
504
|
+
if getattr(args, "x", None) is not None:
|
|
505
|
+
exp.debug_level = args.x
|
|
506
|
+
|
|
507
|
+
if getattr(args, "X", False):
|
|
508
|
+
exp.method = "comparison"
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def forward_calculation(exp):
|
|
512
|
+
"""Do a forward calculation."""
|
|
513
|
+
# set albedo
|
|
514
|
+
if exp.default_a is None:
|
|
515
|
+
if exp.default_mus is None:
|
|
516
|
+
exp.sample.a = 0
|
|
517
|
+
elif exp.default_mua is None:
|
|
518
|
+
exp.sample.a = 1
|
|
519
|
+
else:
|
|
520
|
+
exp.sample.a = exp.default_mus / (exp.default_mua + exp.default_mus)
|
|
521
|
+
else:
|
|
522
|
+
exp.sample.a = exp.default_a
|
|
523
|
+
|
|
524
|
+
# set optical thickness
|
|
525
|
+
if exp.default_b is None:
|
|
526
|
+
if exp.sample.d is None:
|
|
527
|
+
exp.sample.b = float("inf")
|
|
528
|
+
elif exp.sample.a == 0:
|
|
529
|
+
if exp.default_mua is None:
|
|
530
|
+
exp.sample.b = float("inf")
|
|
531
|
+
else:
|
|
532
|
+
exp.sample.b = exp.default_mua * exp.sample.d
|
|
533
|
+
elif exp.default_mus is None:
|
|
534
|
+
exp.sample.b = float("inf")
|
|
535
|
+
else:
|
|
536
|
+
exp.sample.b = exp.default_mus / exp.sample.a * exp.sample.d
|
|
537
|
+
else:
|
|
538
|
+
exp.sample.b = exp.default_b
|
|
539
|
+
|
|
540
|
+
# set scattering anisotropy
|
|
541
|
+
if exp.default_g is None:
|
|
542
|
+
exp.sample.g = 0
|
|
543
|
+
else:
|
|
544
|
+
exp.sample.g = exp.default_g
|
|
545
|
+
|
|
546
|
+
print(exp.sample)
|
|
547
|
+
|
|
548
|
+
ur1, ut1, _uru, _utu = exp.sample.rt()
|
|
549
|
+
ru, tu = exp.sample.unscattered_rt()
|
|
550
|
+
print("Calculated quantities")
|
|
551
|
+
print(" R total = %.3f" % ur1)
|
|
552
|
+
print(" R scattered = %.3f" % (ur1 - ru))
|
|
553
|
+
print(" R unscattered = %.3f" % ru)
|
|
554
|
+
print(" T total = %.3f" % ut1)
|
|
555
|
+
print(" T scattered = %.3f" % (ut1 - tu))
|
|
556
|
+
print(" T unscattered = %.3f" % tu)
|
|
557
|
+
sys.exit(0)
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def print_results_header(debug_lost_light=False):
|
|
561
|
+
"""Print the header for results to stdout."""
|
|
562
|
+
print(
|
|
563
|
+
"# \tMeasured \t M_R \tMeasured \t M_T \tEstimated\tEstimated\tEstimated",
|
|
564
|
+
end="",
|
|
565
|
+
)
|
|
566
|
+
if debug_lost_light:
|
|
567
|
+
print(
|
|
568
|
+
"\t Lost \t Lost \t Lost \t Lost \t MC \t IAD \t Error ",
|
|
569
|
+
end="",
|
|
570
|
+
)
|
|
571
|
+
print()
|
|
572
|
+
|
|
573
|
+
print(
|
|
574
|
+
"##wave\t M_R \t fit \t M_T \t fit \t mu_a \t mu_s' \t g ",
|
|
575
|
+
end="",
|
|
576
|
+
)
|
|
577
|
+
if debug_lost_light:
|
|
578
|
+
print(
|
|
579
|
+
"\t UR1 \t URU \t UT1 \t UTU \t # \t # \t State ",
|
|
580
|
+
end="",
|
|
581
|
+
)
|
|
582
|
+
print()
|
|
583
|
+
|
|
584
|
+
print(
|
|
585
|
+
"# [nm]\t [---] \t [---] \t [---] \t [---] \t 1/mm \t 1/mm \t [---] ",
|
|
586
|
+
end="",
|
|
587
|
+
)
|
|
588
|
+
if debug_lost_light:
|
|
589
|
+
print(
|
|
590
|
+
"\t [---] \t [---] \t [---] \t [---] \t [---] \t [---] \t [---] ",
|
|
591
|
+
end="",
|
|
592
|
+
)
|
|
593
|
+
print()
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def invert_file(exp, args):
|
|
597
|
+
"""Process an entire .rxt file."""
|
|
598
|
+
# determine output file name
|
|
599
|
+
if args.out_fname is None:
|
|
600
|
+
root, ext = os.path.splitext(args.filename)
|
|
601
|
+
if ext == ".rxt":
|
|
602
|
+
args.out_fname = root + ".txt"
|
|
603
|
+
else:
|
|
604
|
+
args.out_fname = args.filename + ".txt"
|
|
605
|
+
|
|
606
|
+
with open(args.out_fname, "w", encoding="utf-8") as output_stream, redirect_stdout(output_stream):
|
|
607
|
+
a, b, g = exp.invert_rt()
|
|
608
|
+
|
|
609
|
+
print_results_header()
|
|
610
|
+
for i, (a_i, b_i, g_i) in enumerate(zip(a, b, g)):
|
|
611
|
+
exp.sample.a = a_i
|
|
612
|
+
exp.sample.b = b_i
|
|
613
|
+
exp.sample.g = g_i
|
|
614
|
+
|
|
615
|
+
mr, mt = exp.measured_rt()
|
|
616
|
+
|
|
617
|
+
wavelength = None
|
|
618
|
+
if exp.lambda0 is not None:
|
|
619
|
+
wavelength = exp.lambda0 if np.isscalar(exp.lambda0) else exp.lambda0[i]
|
|
620
|
+
|
|
621
|
+
if wavelength not in (None, 0):
|
|
622
|
+
print("%6.1f" % wavelength, end="\t")
|
|
623
|
+
else:
|
|
624
|
+
print("%6d" % i, end="\t")
|
|
625
|
+
|
|
626
|
+
if exp.m_r is not None:
|
|
627
|
+
print("% 9.4f" % exp.m_r[i], end="\t")
|
|
628
|
+
else:
|
|
629
|
+
print("% 9.4f" % 0, end="\t")
|
|
630
|
+
print("% 9.4f" % mr, end="\t")
|
|
631
|
+
|
|
632
|
+
if exp.m_t is not None:
|
|
633
|
+
print("% 9.4f" % exp.m_t[i], end="\t")
|
|
634
|
+
else:
|
|
635
|
+
print("% 9.4f" % 0, end="\t")
|
|
636
|
+
|
|
637
|
+
print("% 9.4f" % mt, end="\t")
|
|
638
|
+
|
|
639
|
+
# print("% 9.4f" % exp.sample.a, end='\t')
|
|
640
|
+
# print("% 9.4f" % exp.sample.b, end='\t')
|
|
641
|
+
print("% 9.4f" % exp.sample.mu_a(), end="\t")
|
|
642
|
+
print("% 9.4f" % exp.sample.mu_sp(), end="\t")
|
|
643
|
+
print("% 9.4f" % exp.sample.g, end="\t")
|
|
644
|
+
print()
|
|
645
|
+
sys.exit(0)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def main():
|
|
649
|
+
"""Main command-line interface."""
|
|
650
|
+
parser = argparse.ArgumentParser(
|
|
651
|
+
description="iad command line program",
|
|
652
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
653
|
+
epilog=example_text(),
|
|
654
|
+
)
|
|
655
|
+
for spec in arg_specs:
|
|
656
|
+
flags = spec["flags"]
|
|
657
|
+
other_args = {k: v for k, v in spec.items() if k != "flags"}
|
|
658
|
+
parser.add_argument(*flags, **other_args)
|
|
659
|
+
|
|
660
|
+
try:
|
|
661
|
+
args = parser.parse_args()
|
|
662
|
+
|
|
663
|
+
if args.V:
|
|
664
|
+
print_long_version()
|
|
665
|
+
|
|
666
|
+
# If there is a file then read it, otherwise create a blank experiment
|
|
667
|
+
if args.filename:
|
|
668
|
+
exp = iadpython.read_rxt(args.filename)
|
|
669
|
+
else:
|
|
670
|
+
exp = iadpython.Experiment()
|
|
671
|
+
|
|
672
|
+
# update the search to include the command line constraints
|
|
673
|
+
add_sample_constraints(exp, args)
|
|
674
|
+
add_experiment_constraints(exp, args)
|
|
675
|
+
add_analysis_constraints(exp, args)
|
|
676
|
+
|
|
677
|
+
# follow iad behavior: sphere measurements default to substitution mode
|
|
678
|
+
if np.isscalar(exp.num_spheres) and exp.num_spheres > 0 and getattr(exp, "method", "unknown") == "unknown":
|
|
679
|
+
exp.method = "substitution"
|
|
680
|
+
|
|
681
|
+
if getattr(exp, "method", "unknown") in ("comparison", 1):
|
|
682
|
+
if np.isscalar(exp.num_spheres) and exp.num_spheres == 2:
|
|
683
|
+
raise argparse.ArgumentTypeError(
|
|
684
|
+
"Commandline: Dual beam (-X) cannot be used with double integrating spheres."
|
|
685
|
+
)
|
|
686
|
+
if getattr(exp, "f_r", 0.0) != 0:
|
|
687
|
+
raise argparse.ArgumentTypeError("Commandline: Dual beam (-X) cannot be used when -f is non-zero.")
|
|
688
|
+
|
|
689
|
+
if args.z:
|
|
690
|
+
forward_calculation(exp)
|
|
691
|
+
|
|
692
|
+
if args.filename:
|
|
693
|
+
invert_file(exp, args)
|
|
694
|
+
|
|
695
|
+
print(exp)
|
|
696
|
+
if (exp.m_r is None) and (exp.m_t is None) and (exp.m_u is None):
|
|
697
|
+
raise argparse.ArgumentTypeError("Commandline: One measurement needed or use '-z' for forward calc.")
|
|
698
|
+
|
|
699
|
+
# invert parameters specified on the commandline
|
|
700
|
+
a, b, g = exp.invert_rt()
|
|
701
|
+
print(" a = %.3f" % a)
|
|
702
|
+
print(" b = %.3f" % b)
|
|
703
|
+
print(" g = %.3f" % g)
|
|
704
|
+
sys.exit(0)
|
|
705
|
+
|
|
706
|
+
except (argparse.ArgumentTypeError, OSError, ValueError, RuntimeError, TypeError) as e:
|
|
707
|
+
print(f"Error: {e}")
|
|
708
|
+
sys.exit(1)
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
if __name__ == "__main__":
|
|
712
|
+
main()
|