skinoptics 0.0.1b8__py3-none-any.whl → 0.0.2__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.
- skinoptics/__init__.py +3 -3
- skinoptics/absorption_coefficient.py +0 -1
- skinoptics/anisotropy_factor.py +954 -955
- skinoptics/colors.py +1543 -1403
- skinoptics/dataframes.py +98 -98
- skinoptics/datasets/colors/Sharma2004_TableI.txt +69 -0
- skinoptics/datasets/optical_properties/ext_and_molarext_oxy_and_deo_Prahl.txt +376 -376
- skinoptics/datasets/optical_properties/molarext_bil_Li.txt +1842 -1842
- skinoptics/datasets/optical_properties/mua_wat_Hale.txt +231 -231
- skinoptics/refractive_index.py +413 -414
- skinoptics/scattering_coefficient.py +0 -1
- skinoptics/utils.py +419 -420
- {skinoptics-0.0.1b8.dist-info → skinoptics-0.0.2.dist-info}/METADATA +31 -31
- {skinoptics-0.0.1b8.dist-info → skinoptics-0.0.2.dist-info}/RECORD +17 -16
- {skinoptics-0.0.1b8.dist-info → skinoptics-0.0.2.dist-info}/WHEEL +1 -1
- {skinoptics-0.0.1b8.dist-info → skinoptics-0.0.2.dist-info}/licenses/LICENSE.txt +674 -674
- {skinoptics-0.0.1b8.dist-info → skinoptics-0.0.2.dist-info}/top_level.txt +0 -0
skinoptics/anisotropy_factor.py
CHANGED
|
@@ -1,955 +1,954 @@
|
|
|
1
|
-
'''
|
|
2
|
-
| SkinOptics
|
|
3
|
-
| Copyright (C) 2024-2025 Victor Lima
|
|
4
|
-
|
|
5
|
-
| This program is free software: you can redistribute it and/or modify
|
|
6
|
-
| it under the terms of the GNU General Public License as published by
|
|
7
|
-
| the Free Software Foundation, either version 3 of the License, or
|
|
8
|
-
| (at your option) any later version.
|
|
9
|
-
|
|
10
|
-
| This program is distributed in the hope that it will be useful,
|
|
11
|
-
| but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
-
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
-
| GNU General Public License for more details.
|
|
14
|
-
|
|
15
|
-
| You should have received a copy of the GNU General Public License
|
|
16
|
-
| along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
|
|
18
|
-
| Victor Lima
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
from skinoptics.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
| For :math:`\alpha = 1
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
:
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
:
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
:
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
:
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
| For :math:`\alpha =
|
|
313
|
-
| For :math:`\alpha =
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
:
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
:
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
:
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
:
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
:
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
:
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
:
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
:
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
|
401
|
-
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
|
405
|
-
|
|
|
406
|
-
| :math:`
|
|
407
|
-
| :math
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
:
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
:
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
|
431
|
-
|
|
|
432
|
-
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
\
|
|
437
|
-
\mbox{arccos}
|
|
438
|
-
\
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
:
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
:
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
:
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
|
473
|
-
|
|
|
474
|
-
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
:
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
:
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
:
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
|
503
|
-
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
:
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
:
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
msg
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
msg
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
msg
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
msg
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
|
546
|
-
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
:
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
:
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
:
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
msg
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
msg
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
msg
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
msg
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
msg
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
msg
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
|
608
|
-
| Reynolds
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
|
612
|
-
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
:
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
:
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
msg
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
msg
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
msg
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
msg
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
|
681
|
-
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
:
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
:
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
:
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
:
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
:
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
msg
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
msg
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
msg
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
msg
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
msg
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
msg
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
msg
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
msg
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
msg
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
msg
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
|
773
|
-
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
:
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
msg
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
msg
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
|
801
|
-
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
:
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
:
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
msg
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
msg
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
|
832
|
-
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
:
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
msg
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
msg
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
|
860
|
-
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
:
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
:
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
:
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
msg
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
msg
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
msg
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
msg
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
msg
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
msg
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
|
922
|
-
|
|
|
923
|
-
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
:
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
:
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
np.array(oxy_and_deo_Bosschaart_dataframe)[-1,4]))(lambda0)
|
|
1
|
+
'''
|
|
2
|
+
| SkinOptics
|
|
3
|
+
| Copyright (C) 2024-2025 Victor Lima
|
|
4
|
+
|
|
5
|
+
| This program is free software: you can redistribute it and/or modify
|
|
6
|
+
| it under the terms of the GNU General Public License as published by
|
|
7
|
+
| the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
| (at your option) any later version.
|
|
9
|
+
|
|
10
|
+
| This program is distributed in the hope that it will be useful,
|
|
11
|
+
| but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
| GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
| You should have received a copy of the GNU General Public License
|
|
16
|
+
| along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
| Victor Lima
|
|
19
|
+
| victor.lima\@ufscar.br
|
|
20
|
+
| victorportog.github.io
|
|
21
|
+
|
|
22
|
+
| Release date:
|
|
23
|
+
| October 2024
|
|
24
|
+
| Last modification:
|
|
25
|
+
| March 2025
|
|
26
|
+
|
|
27
|
+
| References:
|
|
28
|
+
|
|
29
|
+
| [HG41] Henyey & Greenstein 1941.
|
|
30
|
+
| Diffuse radiation in the Galaxy.
|
|
31
|
+
| https://doi.org/10.1086/144246
|
|
32
|
+
|
|
33
|
+
| [RM80] Reynolds & McCormick 1980.
|
|
34
|
+
| Approximate two-parameter phase function for light scattering.
|
|
35
|
+
| https://doi.org/10.1364/JOSA.70.001206
|
|
36
|
+
|
|
37
|
+
| [Bv84] Bruls & van der Leun 1984.
|
|
38
|
+
| Forward scattering properties of human epidermal layers.
|
|
39
|
+
| https://doi.org/10.1111/j.1751-1097.1984.tb04581.x
|
|
40
|
+
|
|
41
|
+
| [JAP87] Jacques, Alter & Prahl 1987.
|
|
42
|
+
| Angular Dependence of HeNe Laser Light Scattering by Human Dermis.
|
|
43
|
+
| https://omlc.org/~prahl/pubs/pdfx/jacques87b.pdf
|
|
44
|
+
|
|
45
|
+
| [Y*87] Yoon, Welch, Motamedi & van Gemert 1987.
|
|
46
|
+
| Development and Application of Three-Dimensional Light Distribution Model for Laser Irradiated Tissue.
|
|
47
|
+
| https://doi.org/10.1109/JQE.1987.1073224
|
|
48
|
+
|
|
49
|
+
| [v*89] van Gemert, Jacques, Sterenborg & Star 1989.
|
|
50
|
+
| Skin Optics.
|
|
51
|
+
| https://doi.org/10.1109/10.42108
|
|
52
|
+
|
|
53
|
+
| [CS92] Cornette & Shanks 1992.
|
|
54
|
+
| Physically reasonable analytic expression for the single-scattering phase function
|
|
55
|
+
| https://doi.org/10.1364/AO.31.003152
|
|
56
|
+
|
|
57
|
+
| [WJ92] Wang & Jacques 1992.
|
|
58
|
+
| Monte Carlo Modeling of Light Transport in Multi-layered Tissues in Standard C.
|
|
59
|
+
| https://omlc.org/software/mc/mcml/MCman.pdf
|
|
60
|
+
|
|
61
|
+
| [D03] Draine 2003.
|
|
62
|
+
| Scattering by Interstellar Dust Grains. I. Optical and Ultraviolet.
|
|
63
|
+
| https://doi.org/10.1086/379118
|
|
64
|
+
|
|
65
|
+
| [F11] Frisvad 2011.
|
|
66
|
+
| Importance sampling the Rayleigh phase function.
|
|
67
|
+
| https://doi.org/10.1364/JOSAA.28.002436
|
|
68
|
+
|
|
69
|
+
| [B*14] Bosschaart, Edelman, Aalders, van Leeuwen & Faber 2014.
|
|
70
|
+
| A literature review and novel theoretical approach on the optical properties of whole blood.
|
|
71
|
+
| https://doi.org/10.1007/s10103-013-1446-7
|
|
72
|
+
|
|
73
|
+
| [BCK22] Baes, Camps & Kapoor 2022.
|
|
74
|
+
| A new analytical scattering phase function for interstellar dust.
|
|
75
|
+
| https://doi.org/10.1051/0004-6361/202142437
|
|
76
|
+
|
|
77
|
+
| [JM23] Jacques & McCormick 2023.
|
|
78
|
+
| Two-term scattering phase function for photon transport to model subdiffuse reflectance
|
|
79
|
+
| in superficial tissues.
|
|
80
|
+
| https://doi.org/10.1364/BOE.476461
|
|
81
|
+
'''
|
|
82
|
+
|
|
83
|
+
import numpy as np
|
|
84
|
+
from scipy.interpolate import interp1d
|
|
85
|
+
|
|
86
|
+
from skinoptics.utils import *
|
|
87
|
+
from skinoptics.dataframes import *
|
|
88
|
+
|
|
89
|
+
def ptheta_R(theta):
|
|
90
|
+
r'''
|
|
91
|
+
| The Rayleigh scattering phase function.
|
|
92
|
+
| For details please check Frisvad 2011 [F11].
|
|
93
|
+
|
|
94
|
+
:math:`p_{R}(\theta) = \frac{3}{8}(1 + \cos^2\theta)`
|
|
95
|
+
|
|
96
|
+
:param theta: scattering angle [degrees]
|
|
97
|
+
:type theta: float or np.ndarray
|
|
98
|
+
|
|
99
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
100
|
+
'''
|
|
101
|
+
|
|
102
|
+
return (3/8)*(1 + np.cos(theta*np.pi/180)**2)
|
|
103
|
+
|
|
104
|
+
def ptheta_HG(theta, g):
|
|
105
|
+
r'''
|
|
106
|
+
| The Henyey-Greenstein scattering phase function.
|
|
107
|
+
| For details please check Henyey & Greenstein 1941 [HG41].
|
|
108
|
+
|
|
109
|
+
:math:`p_{HG}(\theta, g) = \frac{1}{2}\frac{1 - g^2}{(1 + g^2 - 2g \cos \theta )^{3/2}}`
|
|
110
|
+
|
|
111
|
+
In this particular model :math:`g` is the anisotropy factor.
|
|
112
|
+
|
|
113
|
+
:param theta: scattering angle [degrees]
|
|
114
|
+
:type theta: float or np.ndarray
|
|
115
|
+
|
|
116
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
117
|
+
:type g: float
|
|
118
|
+
|
|
119
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
120
|
+
'''
|
|
121
|
+
|
|
122
|
+
if g < -1 or g > 1:
|
|
123
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
124
|
+
raise Exception(msg)
|
|
125
|
+
|
|
126
|
+
return (1/2)*(1 - g**2)/(1 + g**2 - 2*g*np.cos(theta*np.pi/180))**(3/2)
|
|
127
|
+
|
|
128
|
+
def ptheta_HGIT(theta, g, gamma):
|
|
129
|
+
r'''
|
|
130
|
+
| The Henyey-Greenstein scattering phase function with an isotropic term.
|
|
131
|
+
| For details please check Jacques, Alter & Prahl 1987 [JAP87] and Yoon et al. [Y*87].
|
|
132
|
+
|
|
133
|
+
:math:`p_{HGIT}(\theta, g, \gamma) = \frac{1}{2}\gamma + (1-\gamma) \mbox{ } p_{HG}(\theta, g)`
|
|
134
|
+
|
|
135
|
+
In this model :math:`g` is NOT the anisotropy factor.
|
|
136
|
+
|
|
137
|
+
:param theta: scattering angle [degrees]
|
|
138
|
+
:type theta: float or np.ndarray
|
|
139
|
+
|
|
140
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
141
|
+
:type g: float
|
|
142
|
+
|
|
143
|
+
:param gamma: relative weight of the isotropic term component [-] (must be in the range [0, 1])
|
|
144
|
+
:type gamma: float
|
|
145
|
+
|
|
146
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
147
|
+
'''
|
|
148
|
+
|
|
149
|
+
if g < -1 or g > 1:
|
|
150
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
151
|
+
raise Exception(msg)
|
|
152
|
+
if gamma < 0 or gamma > 1:
|
|
153
|
+
msg = 'The input gamma = {} is out of the range [0, 1].'.format(gamma)
|
|
154
|
+
raise Exception(msg)
|
|
155
|
+
|
|
156
|
+
return (1/2)*gamma + (1 - gamma)*ptheta_HG(theta = theta, g = g)
|
|
157
|
+
|
|
158
|
+
def ptheta_TTHG(theta, g1, g2, gamma):
|
|
159
|
+
r'''
|
|
160
|
+
| The two-term Henyey-Greenstein scattering phase function.
|
|
161
|
+
| For details please check Baes, Camps & Kapoor 2022 [BCK22].
|
|
162
|
+
|
|
163
|
+
:math:`p_{TTHG}(\theta, g_1, g_2, \gamma) = \gamma \mbox{ } p_{HG}(\theta, g_1) + (1 - \gamma) \mbox{ } p_{HG}(\theta, g_2)`
|
|
164
|
+
|
|
165
|
+
:math:`g_1` characterizes the shape and the strength of the forward scattering peak
|
|
166
|
+
|
|
167
|
+
:math:`g_2` characterizes the shape and the strength of the backward scattering peak
|
|
168
|
+
|
|
169
|
+
:param theta: scattering angle [degrees]
|
|
170
|
+
:type theta: float or np.ndarray
|
|
171
|
+
|
|
172
|
+
:param g1: parameter :math:`g_1` [-] (must be in the range [0, 1])
|
|
173
|
+
:type g1: float
|
|
174
|
+
|
|
175
|
+
:param g2: parameter :math:`g_2` [-] (must be in the range [-1, 0])
|
|
176
|
+
:type g2: float
|
|
177
|
+
|
|
178
|
+
:param gamma: relative weight of the forward scattering component [-] (must be in the range [0, 1])
|
|
179
|
+
:type gamma: float
|
|
180
|
+
|
|
181
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
182
|
+
'''
|
|
183
|
+
|
|
184
|
+
if g1 < 0 or g1 > 1:
|
|
185
|
+
msg = 'The input g1 = {} is out of the range [0, 1].'.format(g1)
|
|
186
|
+
raise Exception(msg)
|
|
187
|
+
if g2 < -1 or g2 > 0:
|
|
188
|
+
msg = 'The input g2 = {} is out of the range [-1, 0].'.format(g2)
|
|
189
|
+
raise Exception(msg)
|
|
190
|
+
if gamma < 0 or gamma > 1:
|
|
191
|
+
msg = 'The input gamma = {} is out of the range [0, 1].'.format(gamma)
|
|
192
|
+
raise Exception(msg)
|
|
193
|
+
|
|
194
|
+
return gamma*ptheta_HG(theta = theta, g = g1) + (1 - gamma)*ptheta_HG(theta = theta, g = g2)
|
|
195
|
+
|
|
196
|
+
def ptheta_RM(theta, g, alpha):
|
|
197
|
+
r'''
|
|
198
|
+
| The Reynolds-McCormick scattering phase function.
|
|
199
|
+
| For details please check Reynolds & McCormick 1980 [RM80] and Jacques & McCormick 2023 [JM23].
|
|
200
|
+
|
|
201
|
+
:math:`p_{RM}(\theta, g, \alpha) = 2 \frac{\alpha g}{(1 + g)^{2\alpha} - (1 - g)^{2\alpha}}\frac{(1 - g^2)^{2\alpha}}{(1 + g^2 - 2g\cos\theta)^{\alpha + 1}}`
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
| For :math:`\alpha = 1/2` it reduces to the Henyey-Greenstein scattering phase function.
|
|
205
|
+
| For :math:`\alpha = 1` it reduces to the Ultraspherical-2 scattering phase function.
|
|
206
|
+
|
|
207
|
+
| In this model :math:`g` is the anisotropy factor only when :math:`\alpha = 1/2`.
|
|
208
|
+
|
|
209
|
+
:param theta: scattering angle [degrees]
|
|
210
|
+
:type theta: float or np.ndarray
|
|
211
|
+
|
|
212
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
213
|
+
:type g: float
|
|
214
|
+
|
|
215
|
+
:param alpha: parameter :math:`\alpha` [-] (must be greater than -0.5)
|
|
216
|
+
:type alpha: float
|
|
217
|
+
|
|
218
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
219
|
+
'''
|
|
220
|
+
|
|
221
|
+
if g < -1 or g > 1:
|
|
222
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
223
|
+
raise Exception(msg)
|
|
224
|
+
if alpha <= -1/2:
|
|
225
|
+
msg = 'The input alpha = {} is not greater than -1/2.'.format(alpha)
|
|
226
|
+
raise Exception(msg)
|
|
227
|
+
|
|
228
|
+
return 2*(alpha*g)/((1 + g)**(2*alpha) - (1 - g)**(2*alpha))*((1 - g**2)**(2*alpha))/((1 + g**2 - 2*g*np.cos(theta*np.pi/180))**(alpha + 1))
|
|
229
|
+
|
|
230
|
+
def ptheta_TTRM(theta, g1, g2, alpha1, alpha2, gamma):
|
|
231
|
+
r'''
|
|
232
|
+
| The two-term Reynolds-McCormick scattering phase function.
|
|
233
|
+
| For details please check Reynolds & McCormick 1980 [RM80] and Jacques & McCormick 2023 [JM23].
|
|
234
|
+
|
|
235
|
+
:math:`p_{TTRM}(\theta, g_1, g_2, \alpha_1, \alpha_2, \gamma) = \gamma \mbox{ } p_{RM}(\theta, g_1, \alpha_1) + (1 - \gamma) \mbox{ } p_{RM}(\theta, g_2, \alpha_2)`
|
|
236
|
+
|
|
237
|
+
:math:`g_1` characterizes the shape and the strength of the forward scattering peak
|
|
238
|
+
|
|
239
|
+
:math:`g_2` characterizes the shape and the strength of the backward scattering peak
|
|
240
|
+
|
|
241
|
+
:param theta: scattering angle [degrees]
|
|
242
|
+
:type theta: float or np.ndarray
|
|
243
|
+
|
|
244
|
+
:param g1: parameter :math:`g_1` [-] (must be in the range [0, 1])
|
|
245
|
+
:type g1: float
|
|
246
|
+
|
|
247
|
+
:param g2: parameter :math:`g_2` [-] (must be in the range [-1, 0])
|
|
248
|
+
:type g2: float
|
|
249
|
+
|
|
250
|
+
:param alpha1: parameter :math:`\alpha_1` [-] (must be greater than -1/2)
|
|
251
|
+
:type alpha1: float
|
|
252
|
+
|
|
253
|
+
:param alpha2: parameter :math:`\alpha_2` [-] (must be greater than -1/2)
|
|
254
|
+
:type alpha2: float
|
|
255
|
+
|
|
256
|
+
:param gamma: relative weight of the forward scattering component [-] (must be in the range [0, 1])
|
|
257
|
+
:type gamma: float
|
|
258
|
+
|
|
259
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
260
|
+
'''
|
|
261
|
+
|
|
262
|
+
if g1 < 0 or g1 > 1:
|
|
263
|
+
msg = 'The input g1 = {} is out of the range [0, 1].'.format(g1)
|
|
264
|
+
raise Exception(msg)
|
|
265
|
+
if g2 < -1 or g2 > 0:
|
|
266
|
+
msg = 'The input g2 = {} is out of the range [-1, 0].'.format(g2)
|
|
267
|
+
raise Exception(msg)
|
|
268
|
+
if alpha1 <= -1/2:
|
|
269
|
+
msg = 'The input alpha1 = {} is not greater than -1/2.'.format(alpha1)
|
|
270
|
+
raise Exception(msg)
|
|
271
|
+
if alpha2 <= -1/2:
|
|
272
|
+
msg = 'The input alpha2 = {} is not greater than -1/2.'.format(alpha2)
|
|
273
|
+
raise Exception(msg)
|
|
274
|
+
if gamma < 0 or gamma > 1:
|
|
275
|
+
msg = 'The input gamma = {} is out of the range [0, 1].'.format(gamma)
|
|
276
|
+
raise Exception(msg)
|
|
277
|
+
|
|
278
|
+
return gamma*ptheta_RM(theta = theta, g = g1, alpha = alpha1) + (1 - gamma)*ptheta_RM(theta = theta, g = g2, alpha = alpha2)
|
|
279
|
+
|
|
280
|
+
def ptheta_CS(theta, g):
|
|
281
|
+
r'''
|
|
282
|
+
| The Cornette-Shanks scattering phase function.
|
|
283
|
+
| For details please check Cornette & Shanks 1992 [CS92].
|
|
284
|
+
|
|
285
|
+
:math:`p_{CS}(\theta, g) = \frac{3}{2}\frac{1 + \cos^2\theta}{2 + g^2} \mbox{ } p_{HG}(\theta, g)`
|
|
286
|
+
|
|
287
|
+
| In this model :math:`g` is NOT the anisotropy factor.
|
|
288
|
+
|
|
289
|
+
:param theta: scattering angle [degrees]
|
|
290
|
+
:type theta: float or np.ndarray
|
|
291
|
+
|
|
292
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
293
|
+
:type g: float
|
|
294
|
+
|
|
295
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
296
|
+
'''
|
|
297
|
+
|
|
298
|
+
if g < -1 or g > 1:
|
|
299
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
300
|
+
raise Exception(msg)
|
|
301
|
+
|
|
302
|
+
return (3/2)*(1 + np.cos(theta*np.pi/180.)**2)/(2 + g**2)*ptheta_HG(theta = theta, g = g)
|
|
303
|
+
|
|
304
|
+
def ptheta_D(theta, g, alpha):
|
|
305
|
+
r'''
|
|
306
|
+
| The Draine scattering phase function.
|
|
307
|
+
| For details please check Draine 2003 [D03].
|
|
308
|
+
|
|
309
|
+
:math:`p_{D}(\theta, g, \alpha) = 3\frac{1 + \alpha \cos^2\theta}{3 + \alpha (1 + 2g^2)} \mbox{ } p_{HG}(\theta, g)`
|
|
310
|
+
|
|
311
|
+
| For :math:`\alpha = 1` and :math:`g = 0` it reduces to the Rayleigh scattering phase function.
|
|
312
|
+
| For :math:`\alpha = 0` it reduces to the Henyey-Greenstein scattering phase function.
|
|
313
|
+
| For :math:`\alpha = 1` it reduces to the Cornette-Shanks scattering phase function
|
|
314
|
+
|
|
315
|
+
| In this model :math:`g` is NOT the anisotropy factor.
|
|
316
|
+
|
|
317
|
+
:param theta: scattering angle [degrees]
|
|
318
|
+
:type theta: float or np.ndarray
|
|
319
|
+
|
|
320
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
321
|
+
:type g: float
|
|
322
|
+
|
|
323
|
+
:param alpha: parameter :math:`\alpha` [-]
|
|
324
|
+
:type alpha: float
|
|
325
|
+
|
|
326
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
327
|
+
'''
|
|
328
|
+
|
|
329
|
+
if g < -1 or g > 1:
|
|
330
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
331
|
+
raise Exception(msg)
|
|
332
|
+
|
|
333
|
+
return 3*(1 + alpha*np.cos(theta*np.pi/180.)**2)/(3 + alpha*(1 + 2*g**2))*ptheta_HG(theta = theta, g = g)
|
|
334
|
+
|
|
335
|
+
def ptheta_U2(theta, g):
|
|
336
|
+
r'''
|
|
337
|
+
| The Ultraspherical-2 scattering phase function.
|
|
338
|
+
| For details please check Baes, Camps & Kapoor 2022 [BCK22].
|
|
339
|
+
|
|
340
|
+
:math:`p_{U2}(\theta, g) = \frac{1}{2}\frac{(1 - g^2)^2}{(1 + g^2 - 2g \cos \theta)^2}`
|
|
341
|
+
|
|
342
|
+
| In this model :math:`g` is NOT the anisotropy factor.
|
|
343
|
+
|
|
344
|
+
:param theta: scattering angle [degrees]
|
|
345
|
+
:type theta: float or np.ndarray
|
|
346
|
+
|
|
347
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
348
|
+
:type g: float
|
|
349
|
+
|
|
350
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
351
|
+
'''
|
|
352
|
+
|
|
353
|
+
if g < -1 or g > 1:
|
|
354
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
355
|
+
raise Exception(msg)
|
|
356
|
+
|
|
357
|
+
return (1/2)*(1 - g**2)**2/(1 + g**2 - 2*g*np.cos(theta*np.pi/180.))**2
|
|
358
|
+
|
|
359
|
+
def ptheta_TTU2(theta, g1, g2, gamma):
|
|
360
|
+
r'''
|
|
361
|
+
| The two-term Ultraspherical-2 scattering phase function.
|
|
362
|
+
| For details please check Baes, Camps & Kapoor 2022 [BCK22].
|
|
363
|
+
|
|
364
|
+
:math:`p_{TTU2}(\theta, g_1, g_2, \gamma) = \gamma \mbox{ } p_{U2}(\theta, g_1) + (1 - \gamma) \mbox{ } p_{U2}(\theta, g_2)`
|
|
365
|
+
|
|
366
|
+
:math:`g_1` characterizes the shape and the strength of the forward scattering peak
|
|
367
|
+
|
|
368
|
+
:math:`g_2` characterizes the shape and the strength of the backward scattering peak
|
|
369
|
+
|
|
370
|
+
:param theta: scattering angle [degrees]
|
|
371
|
+
:type theta: float or np.ndarray
|
|
372
|
+
|
|
373
|
+
:param g1: parameter :math:`g_1` [-] (must be in the range [0, 1])
|
|
374
|
+
:type g1: float
|
|
375
|
+
|
|
376
|
+
:param g2: parameter :math:`g_2` [-] (must be in the range [-1, 0])
|
|
377
|
+
:type g2: float
|
|
378
|
+
|
|
379
|
+
:param gamma: relative weight of the forward scattering component [-] (must be in the range [0, 1])
|
|
380
|
+
:type gamma: float
|
|
381
|
+
|
|
382
|
+
:return: - **ptheta** (*float or np.ndarray*) – scattering phase function [-]
|
|
383
|
+
'''
|
|
384
|
+
|
|
385
|
+
if g1 < 0 or g1 > 1:
|
|
386
|
+
msg = 'The input g1 = {} is out of the range [0, 1].'.format(g1)
|
|
387
|
+
raise Exception(msg)
|
|
388
|
+
if g2 < -1 or g2 > 0:
|
|
389
|
+
msg = 'The input g2 = {} is out of the range [-1, 0].'.format(g2)
|
|
390
|
+
raise Exception(msg)
|
|
391
|
+
if gamma < 0 or gamma > 1:
|
|
392
|
+
msg = 'The input gamma = {} is out of the range [0, 1].'.format(gamma)
|
|
393
|
+
raise Exception(msg)
|
|
394
|
+
|
|
395
|
+
return gamma*ptheta_U2(theta = theta, g = g1) + (1 - gamma)*ptheta_U2(theta = theta, g = g2)
|
|
396
|
+
|
|
397
|
+
def theta_R_from_RND(n_RND = int(1E6), seed_RND = None):
|
|
398
|
+
r'''
|
|
399
|
+
| The scattering angle distribution as a function of a set of random numbers uniformly
|
|
400
|
+
| distributed over the interval [0, 1), assuming the Rayleigh scattering phase function.
|
|
401
|
+
| For details please check section 3.B from Frisvad 2011 [F11].
|
|
402
|
+
|
|
403
|
+
| :math:`\theta_{R} = \mbox{arccos}(\sqrt[3]{u + v} + \sqrt[3]{u - v})`
|
|
404
|
+
| with
|
|
405
|
+
| :math:`u = -2(2 \xi - 1)`
|
|
406
|
+
| :math:`v = \sqrt{4(2 \xi - 1)^2 + 1}`
|
|
407
|
+
| in which :math:`\xi` is a random number in the interval [0, 1)
|
|
408
|
+
|
|
409
|
+
:param n_RND: number of random numbers [-] (default to int(1E6))
|
|
410
|
+
:type n_RND: int
|
|
411
|
+
|
|
412
|
+
:param seed_RND: seed for the random number generator np.random.default_rng() [-] (default to None)
|
|
413
|
+
:type seed_RND: int
|
|
414
|
+
|
|
415
|
+
:return: - **theta** (*np.ndarray*) – scattering angle [degrees]
|
|
416
|
+
'''
|
|
417
|
+
|
|
418
|
+
rng = np.random.default_rng(seed = seed_RND)
|
|
419
|
+
xi = rng.uniform(0., 1., size = n_RND)
|
|
420
|
+
two_times_xi_minus_one = 2*xi - 1
|
|
421
|
+
term1 = -2*two_times_xi_minus_one
|
|
422
|
+
term2 = np.sqrt(4*two_times_xi_minus_one**2 + 1)
|
|
423
|
+
|
|
424
|
+
return np.arccos(np.cbrt(term1 + term2) + np.cbrt(term1 - term2))*180/np.pi
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def theta_HG_from_RND(g, n_RND = int(1E6), seed_RND = None):
|
|
428
|
+
r'''
|
|
429
|
+
| The scattering angle distribution as a function of the anisotropy factor and a set of random
|
|
430
|
+
| numbers uniformly distributed over the interval [0, 1), assuming the Henyey-Greenstein
|
|
431
|
+
| scattering phase function.
|
|
432
|
+
| For details please check section 3.5 from Wang & Jacques 1992 [WJ92].
|
|
433
|
+
|
|
434
|
+
:math:`\theta_{HG} =
|
|
435
|
+
\left \{ \begin{matrix}
|
|
436
|
+
\mbox{arccos}(2 \xi - 1) , & \mbox{if } g = 0 \\
|
|
437
|
+
\mbox{arccos}\left\{\frac{1}{2g} \left[1 + g^2 - \left(\frac{1 - g^2}{1 - g + 2g \xi}\right)^2\right]\right\}, & \mbox{if } g \ne 0
|
|
438
|
+
\end{matrix} \right.`
|
|
439
|
+
|
|
440
|
+
in which :math:`\xi` is a random number in the interval [0, 1)
|
|
441
|
+
|
|
442
|
+
In this particular model :math:`g` is the anisotropy factor.
|
|
443
|
+
|
|
444
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
445
|
+
:type g: float
|
|
446
|
+
|
|
447
|
+
:param n_RND: number of random numbers [-] (default to int(1E6))
|
|
448
|
+
:type n_RND: int
|
|
449
|
+
|
|
450
|
+
:param seed_RND: seed for the random number generator np.random.default_rng() [-] (default to None)
|
|
451
|
+
:type seed_RND: int
|
|
452
|
+
|
|
453
|
+
:return: - **theta** (*np.ndarray*) – scattering angle [degrees]
|
|
454
|
+
'''
|
|
455
|
+
|
|
456
|
+
if g < -1 or g > 1:
|
|
457
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
458
|
+
raise Exception(msg)
|
|
459
|
+
|
|
460
|
+
rng = np.random.default_rng(seed = seed_RND)
|
|
461
|
+
xi = rng.uniform(0., 1., size = n_RND)
|
|
462
|
+
if g == 0:
|
|
463
|
+
theta_HG = np.arccos(2*xi - 1)*180./np.pi
|
|
464
|
+
else:
|
|
465
|
+
theta_HG = np.arccos(1/(2*g)*(1 + g**2 - ((1 - g**2)/(1 - g + 2*g*xi))**2))*180./np.pi
|
|
466
|
+
|
|
467
|
+
return theta_HG
|
|
468
|
+
|
|
469
|
+
def theta_U2_from_RND(g, n_RND = int(1E6), seed_RND = None):
|
|
470
|
+
r'''
|
|
471
|
+
| The scattering angle distribution as a function of the g parameter and a set of random
|
|
472
|
+
| numbers uniformly distributed over the interval [0, 1), assuming the Ultraspherical-2
|
|
473
|
+
| scattering phase function.
|
|
474
|
+
| For details please check section 4.4.2 from Baes, Camps & Kapoor 2022 [BCK22].
|
|
475
|
+
|
|
476
|
+
:math:`\theta_{U2} = arccos\left[\frac{(1 + g)^2 - 2 \xi (1 + g^2)}{(1 + g)^2 - 4g \xi}\right]`
|
|
477
|
+
|
|
478
|
+
in which :math:`\xi` is a random number in the interval [0, 1)
|
|
479
|
+
|
|
480
|
+
| In this model :math:`g` is NOT the anisotropy factor.
|
|
481
|
+
|
|
482
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
483
|
+
:type g: float
|
|
484
|
+
|
|
485
|
+
:param n_RND: number of random numbers [-] (default to int(1E6))
|
|
486
|
+
:type n_RND: int
|
|
487
|
+
|
|
488
|
+
:param seed_RND: seed for the random number generator np.random.default_rng() [-] (default to None)
|
|
489
|
+
:type seed_RND: int
|
|
490
|
+
|
|
491
|
+
:return: - **theta** (*np.ndarray*) – scattering angle [degrees]
|
|
492
|
+
'''
|
|
493
|
+
|
|
494
|
+
rng = np.random.default_rng(seed = seed_RND)
|
|
495
|
+
xi = rng.uniform(0., 1., size = n_RND)
|
|
496
|
+
|
|
497
|
+
return np.arccos(((1 + g)**2 - 2*xi*(1 + g**2))/((1 + g)**2 - 4*g*xi))*180./np.pi
|
|
498
|
+
|
|
499
|
+
def costheta_HGIT(g, gamma):
|
|
500
|
+
r'''
|
|
501
|
+
| The anisotropy factor as a function of the parameters g and gamma, assuming the
|
|
502
|
+
| Henyey-Greenstein scattering phase function with an isotropic term.
|
|
503
|
+
| For details please check Jacques, Alter & Prahl 1987 [JAP87] and Yoon et al. [Y*87].
|
|
504
|
+
|
|
505
|
+
:math:`\langle \cos\theta \rangle_{HGIT}(g, \gamma) = (1 - \gamma) \mbox{ } g`
|
|
506
|
+
|
|
507
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
508
|
+
:type g: float
|
|
509
|
+
|
|
510
|
+
:param gamma: fraction of isotropic term contribution [-] (must be in the range [0, 1])
|
|
511
|
+
:type gamma: float
|
|
512
|
+
|
|
513
|
+
:return: - **costheta** (*np.ndarray*) – anisotropy factor [-]
|
|
514
|
+
'''
|
|
515
|
+
|
|
516
|
+
if isinstance(g, np.ndarray) == True:
|
|
517
|
+
if np.any(g < -1) or np.any(g > 1):
|
|
518
|
+
msg = 'At least one element in the input g is out of the range [-1, 1].'
|
|
519
|
+
raise Exception(msg)
|
|
520
|
+
elif isinstance(g, (int, float)) == True:
|
|
521
|
+
if g < -1 or g > 1:
|
|
522
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
523
|
+
raise Exception(msg)
|
|
524
|
+
else:
|
|
525
|
+
msg = 'The input g must be int, float or np.ndarray.'
|
|
526
|
+
raise Exception(msg)
|
|
527
|
+
|
|
528
|
+
if isinstance(gamma, np.ndarray) == True:
|
|
529
|
+
if np.any(gamma < 0) or np.any(gamma > 1):
|
|
530
|
+
msg = 'At least one element in the input gamma is out of the range [0, 1].'
|
|
531
|
+
raise Exception(msg)
|
|
532
|
+
elif isinstance(gamma, (int, float)) == True:
|
|
533
|
+
if gamma < 0 or gamma > 1:
|
|
534
|
+
msg = 'The input gamma = {} is out of the range [0, 1].'.format(gamma)
|
|
535
|
+
raise Exception(msg)
|
|
536
|
+
else:
|
|
537
|
+
msg = 'The input gamma must be int, float or np.ndarray.'
|
|
538
|
+
raise Exception(msg)
|
|
539
|
+
|
|
540
|
+
return (1 - gamma)*g
|
|
541
|
+
|
|
542
|
+
def costheta_TTHG(g1, g2, gamma):
|
|
543
|
+
r'''
|
|
544
|
+
| The anisotropy factor as a function of the parameters g1, g2 and gamma, assuming the
|
|
545
|
+
| two-term Henyey-Greenstein scattering phase function.
|
|
546
|
+
| For details please check Baes, Camps & Kapoor 2022 [BCK22].
|
|
547
|
+
|
|
548
|
+
:math:`\langle \cos\theta \rangle_{TTHG}(g_1, g_2, \gamma) = \gamma \mbox{ } g_1 + (1 - \gamma) \mbox { } g_2`
|
|
549
|
+
|
|
550
|
+
:math:`g_1` characterizes the shape and the strength of the forward scattering peak
|
|
551
|
+
|
|
552
|
+
:math:`g_2` characterizes the shape and the strength of the backward scattering peak
|
|
553
|
+
|
|
554
|
+
:param g1: parameter :math:`g_1` [-] (must be in the range [0, 1])
|
|
555
|
+
:type g1: float
|
|
556
|
+
|
|
557
|
+
:param g2: parameter :math:`g_2` [-] (must be in the range [-1, 0])
|
|
558
|
+
:type g2: float
|
|
559
|
+
|
|
560
|
+
:param gamma: relative weight of the forward scattering component [-] (must be in the range [0, 1])
|
|
561
|
+
:type gamma: float
|
|
562
|
+
|
|
563
|
+
:return: - **costheta** (*np.ndarray*) – anisotropy factor [-]
|
|
564
|
+
'''
|
|
565
|
+
|
|
566
|
+
if isinstance(g1, np.ndarray) == True:
|
|
567
|
+
if np.any(g1 < 0) or np.any(g1 > 1):
|
|
568
|
+
msg = 'At least one element in the input g1 is out of the range [0, 1].'
|
|
569
|
+
raise Exception(msg)
|
|
570
|
+
elif isinstance(g1, (int, float)) == True:
|
|
571
|
+
if g1 < 0 or g1 > 1:
|
|
572
|
+
msg = 'The input g1 = {} is out of the range [0, 1].'.format(g1)
|
|
573
|
+
raise Exception(msg)
|
|
574
|
+
else:
|
|
575
|
+
msg = 'The input g1 must be int, float or np.ndarray.'
|
|
576
|
+
raise Exception(msg)
|
|
577
|
+
|
|
578
|
+
if isinstance(g2, np.ndarray) == True:
|
|
579
|
+
if np.any(g2 < -1) or np.any(g2 > 0):
|
|
580
|
+
msg = 'At least one element in the input g2 is out of the range [-1, 0].'
|
|
581
|
+
raise Exception(msg)
|
|
582
|
+
elif isinstance(g2, (int, float)) == True:
|
|
583
|
+
if g2 < -1 or g2 > 0:
|
|
584
|
+
msg = 'The input g2 = {} is out of the range [-1, 0].'.format(g2)
|
|
585
|
+
raise Exception(msg)
|
|
586
|
+
else:
|
|
587
|
+
msg = 'The input g2 must be int, float or np.ndarray.'
|
|
588
|
+
raise Exception(msg)
|
|
589
|
+
|
|
590
|
+
if isinstance(gamma, np.ndarray) == True:
|
|
591
|
+
if np.any(gamma < 0) or np.any(gamma > 1):
|
|
592
|
+
msg = 'At least one element in the input gamma is out of the range [0, 1].'
|
|
593
|
+
raise Exception(msg)
|
|
594
|
+
elif isinstance(gamma, (int, float)) == True:
|
|
595
|
+
if gamma < 0 or gamma > 1:
|
|
596
|
+
msg = 'The input gamma = {} is out of the range [0, 1].'.format(gamma)
|
|
597
|
+
raise Exception(msg)
|
|
598
|
+
else:
|
|
599
|
+
msg = 'The input gamma must be int, float or np.ndarray.'
|
|
600
|
+
raise Exception(msg)
|
|
601
|
+
|
|
602
|
+
return gamma*g1 + (1 - gamma)*g2
|
|
603
|
+
|
|
604
|
+
def costheta_RM(g, alpha):
|
|
605
|
+
r'''
|
|
606
|
+
| The anisotropy factor as a function of the parameters g and alpha, assuming the
|
|
607
|
+
| Reynolds-McCormick scattering phase function.
|
|
608
|
+
| For details please check Reynolds & McCormick 1980 [RM80] and Jacques & McCormick 2023 [JM23].
|
|
609
|
+
|
|
610
|
+
| :math:`\langle \cos\theta \rangle_{RM}(g, \alpha) = \frac{2 \alpha g L - (1+g^2)}{2g(\alpha - 1)}`
|
|
611
|
+
| with
|
|
612
|
+
| :math:`L = \frac{(1+g)^{2\alpha} + (1-g)^{2\alpha}}{(1+g)^{2\alpha} - (1-g)^{2\alpha}}`
|
|
613
|
+
|
|
614
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
615
|
+
:type g: float
|
|
616
|
+
|
|
617
|
+
:param alpha: parameter :math:`\alpha` [-] (must be greater than -0.5)
|
|
618
|
+
:float alpha: float or np.ndarray
|
|
619
|
+
|
|
620
|
+
:return: - **costheta** (*np.ndarray*) – anisotropy factor [-]
|
|
621
|
+
'''
|
|
622
|
+
|
|
623
|
+
if isinstance(g, np.ndarray) == True:
|
|
624
|
+
if np.any(g < -1) or np.any(g > 1):
|
|
625
|
+
msg = 'At least one element in the input g is out of the range [-1, 1].'
|
|
626
|
+
raise Exception(msg)
|
|
627
|
+
elif isinstance(g, (int, float)) == True:
|
|
628
|
+
if g < -1 or g > 1:
|
|
629
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
630
|
+
raise Exception(msg)
|
|
631
|
+
else:
|
|
632
|
+
msg = 'The input g must be int, float or np.ndarray.'
|
|
633
|
+
raise Exception(msg)
|
|
634
|
+
|
|
635
|
+
if isinstance(alpha, np.ndarray) == True:
|
|
636
|
+
if np.any(alpha <= -1/2):
|
|
637
|
+
msg = 'At least one element in the input alpha is not greater than -1/2.'
|
|
638
|
+
raise Exception(msg)
|
|
639
|
+
costheta = np.zeros((len(alpha), 5))
|
|
640
|
+
for i in range(len(alpha)):
|
|
641
|
+
if i == 1:
|
|
642
|
+
if isinstance(g, np.ndarray) == True:
|
|
643
|
+
costheta[i] = costheta_U2(g[i])
|
|
644
|
+
else:
|
|
645
|
+
costheta[i] = costheta_U2(g)
|
|
646
|
+
else:
|
|
647
|
+
if isinstance(g, np.ndarray) == True:
|
|
648
|
+
one_plus_g = 1 + g[i]
|
|
649
|
+
one_minus_g = 1 - g[i]
|
|
650
|
+
two_times_alpha = 2*alpha[i]
|
|
651
|
+
L = (one_plus_g**two_times_alpha + one_minus_g**two_times_alpha)/(one_plus_g**two_times_alpha - one_minus_g**two_times_alpha)
|
|
652
|
+
costheta[i] = (2*alpha[i]*g[i]*L - (1 + g[i]**2))/(2*g[i]*(alpha[i] - 1))
|
|
653
|
+
else:
|
|
654
|
+
one_plus_g = 1 + g
|
|
655
|
+
one_minus_g = 1 - g
|
|
656
|
+
two_times_alpha = 2*alpha
|
|
657
|
+
L = (one_plus_g**two_times_alpha + one_minus_g**two_times_alpha)/(one_plus_g**two_times_alpha - one_minus_g**two_times_alpha)
|
|
658
|
+
costheta[i] = (2*alpha[i]*g*L - (1 + g**2))/(2*g*(alpha[i] - 1))
|
|
659
|
+
elif isinstance(alpha, (int, float)) == True:
|
|
660
|
+
if alpha <= -1/2:
|
|
661
|
+
msg = 'The input alpha = {} is not greater than -1/2.'.format(alpha)
|
|
662
|
+
raise Exception(msg)
|
|
663
|
+
if alpha == 1:
|
|
664
|
+
costheta = costheta_U2(g)
|
|
665
|
+
else:
|
|
666
|
+
one_plus_g = 1 + g
|
|
667
|
+
one_minus_g = 1 - g
|
|
668
|
+
two_times_alpha = 2*alpha
|
|
669
|
+
L = (one_plus_g**two_times_alpha + one_minus_g**two_times_alpha)/(one_plus_g**two_times_alpha - one_minus_g**two_times_alpha)
|
|
670
|
+
costheta = (2*alpha*g*L - (1 + g**2))/(2*g*(alpha - 1))
|
|
671
|
+
else:
|
|
672
|
+
msg = 'The input alpha must be int, float or np.ndarray.'
|
|
673
|
+
raise Exception(msg)
|
|
674
|
+
|
|
675
|
+
return costheta
|
|
676
|
+
|
|
677
|
+
def costheta_TTRM(g1, g2, alpha1, alpha2, gamma):
|
|
678
|
+
r'''
|
|
679
|
+
| The anisotropy factor as a function of the parameters g1, g2, alpha1, alpha2 and gamma,
|
|
680
|
+
| assuming the two-term Reynolds-McCormick scattering phase function.
|
|
681
|
+
| For details please check Reynolds & McCormick 1980 [RM80] and Jacques & McCormick 2023 [JM23].
|
|
682
|
+
|
|
683
|
+
:math:`\langle \cos\theta \rangle_{TTRM}(g_1, g_2, \alpha_1, \alpha_2, \gamma) = \gamma \mbox{ } \langle \cos\theta \rangle_{RM}(g_1, \alpha_1) + (1 - \gamma) \mbox{ } \langle \cos\theta \rangle_{RM}(g_2, \alpha_2)`
|
|
684
|
+
|
|
685
|
+
:math:`g_1` characterizes the shape and the strength of the forward scattering peak
|
|
686
|
+
|
|
687
|
+
:math:`g_2` characterizes the shape and the strength of the backward scattering peak
|
|
688
|
+
|
|
689
|
+
:param g1: parameter :math:`g_1` [-] (must be in the range [0, 1])
|
|
690
|
+
:type g1: float
|
|
691
|
+
|
|
692
|
+
:param g2: parameter :math:`g_2` [-] (must be in the range [-1, 0])
|
|
693
|
+
:type g2: float
|
|
694
|
+
|
|
695
|
+
:param alpha1: parameter :math:`\alpha_1` [-] (must be greater than -0.5)
|
|
696
|
+
:type alpha1: float
|
|
697
|
+
|
|
698
|
+
:param alpha2: parameter :math:`\alpha_2` [-] (must be greater than -0.5)
|
|
699
|
+
:type alpha2: float
|
|
700
|
+
|
|
701
|
+
:param gamma: relative weight of the forward scattering component [-] (must be in the range [0, 1])
|
|
702
|
+
:type gamma: float
|
|
703
|
+
|
|
704
|
+
:return: - **costheta** (*np.ndarray*) – anisotropy factor [-]
|
|
705
|
+
'''
|
|
706
|
+
|
|
707
|
+
if isinstance(g1, np.ndarray) == True:
|
|
708
|
+
if np.any(g1 < 0) or np.any(g1 > 1):
|
|
709
|
+
msg = 'At least one element in the input g1 is out of the range [0, 1].'
|
|
710
|
+
raise Exception(msg)
|
|
711
|
+
elif isinstance(g1, (int, float)) == True:
|
|
712
|
+
if g1 < 0 or g1 > 1:
|
|
713
|
+
msg = 'The input g1 = {} is out of the range [0, 1].'.format(g1)
|
|
714
|
+
raise Exception(msg)
|
|
715
|
+
else:
|
|
716
|
+
msg = 'The input g1 must be int, float or np.ndarray.'
|
|
717
|
+
raise Exception(msg)
|
|
718
|
+
|
|
719
|
+
if isinstance(g2, np.ndarray) == True:
|
|
720
|
+
if np.any(g2 < -1) or np.any(g2 > 0):
|
|
721
|
+
msg = 'At least one element in the input g2 is out of the range [-1, 0].'
|
|
722
|
+
raise Exception(msg)
|
|
723
|
+
elif isinstance(g2, (int, float)) == True:
|
|
724
|
+
if g2 < -1 or g2 > 0:
|
|
725
|
+
msg = 'The input g2 = {} is out of the range [-1, 0].'.format(g2)
|
|
726
|
+
raise Exception(msg)
|
|
727
|
+
else:
|
|
728
|
+
msg = 'The input g2 must be int, float or np.ndarray.'
|
|
729
|
+
raise Exception(msg)
|
|
730
|
+
|
|
731
|
+
if isinstance(alpha1, np.ndarray) == True:
|
|
732
|
+
if np.any(alpha1 <= 1/2):
|
|
733
|
+
msg = 'At least one element in the input alpha1 is not greater than 1/2.'
|
|
734
|
+
raise Exception(msg)
|
|
735
|
+
elif isinstance(alpha1, (int, float)) == True:
|
|
736
|
+
if alpha1 <= 1/2:
|
|
737
|
+
msg = 'The input alpha1 = {} is not greater than 1/2.'.format(alpha1)
|
|
738
|
+
raise Exception(msg)
|
|
739
|
+
else:
|
|
740
|
+
msg = 'The input alpha1 must be int, float or np.ndarray.'
|
|
741
|
+
raise Exception(msg)
|
|
742
|
+
|
|
743
|
+
if isinstance(alpha2, np.ndarray) == True:
|
|
744
|
+
if np.any(alpha2 <= 1/2):
|
|
745
|
+
msg = 'At least one element in the input alpha2 is not greater than 1/2.'
|
|
746
|
+
raise Exception(msg)
|
|
747
|
+
elif isinstance(alpha2, (int, float)) == True:
|
|
748
|
+
if alpha2 <= 1/2:
|
|
749
|
+
msg = 'The input alpha2 = {} is not greater than 1/2.'.format(alpha2)
|
|
750
|
+
raise Exception(msg)
|
|
751
|
+
else:
|
|
752
|
+
msg = 'The input alpha2 must be int, float or np.ndarray.'
|
|
753
|
+
raise Exception(msg)
|
|
754
|
+
|
|
755
|
+
if isinstance(gamma, np.ndarray) == True:
|
|
756
|
+
if np.any(gamma < 0) or np.any(gamma > 1):
|
|
757
|
+
msg = 'At least one element in the input gamma is out of the range [0, 1].'
|
|
758
|
+
raise Exception(msg)
|
|
759
|
+
elif isinstance(gamma, (int, float)) == True:
|
|
760
|
+
if gamma < 0 or gamma > 1:
|
|
761
|
+
msg = 'The input gamma = {} is out of the range [0, 1].'.format(gamma)
|
|
762
|
+
raise Exception(msg)
|
|
763
|
+
else:
|
|
764
|
+
msg = 'The input gamma must be int, float or np.ndarray.'
|
|
765
|
+
raise Exception(msg)
|
|
766
|
+
|
|
767
|
+
return gamma*costheta_RM(g = g1, alpha = alpha1) + (1 - gamma)*costheta_RM(g = g2, alpha = alpha2)
|
|
768
|
+
|
|
769
|
+
def costheta_CS(g):
|
|
770
|
+
r'''
|
|
771
|
+
| The anisotropy factor as a function of the parameter g, assuming the Cornette-Shanks
|
|
772
|
+
| scattering phase function.
|
|
773
|
+
| For details please check Cornette & Shanks 1992 [CS92].
|
|
774
|
+
|
|
775
|
+
:math:`\langle \cos\theta \rangle_{CS}(g) = \frac{3(4 + g^2)}{5(2 + g^2)} \mbox{ } g`
|
|
776
|
+
|
|
777
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
778
|
+
:type g: float
|
|
779
|
+
|
|
780
|
+
:return: - **costheta** (*np.ndarray*) – anisotropy factor [-]
|
|
781
|
+
'''
|
|
782
|
+
|
|
783
|
+
if isinstance(g, np.ndarray) == True:
|
|
784
|
+
if np.any(g < -1) or np.any(g > 1):
|
|
785
|
+
msg = 'At least one element in the input g is out of the range [-1, 1].'
|
|
786
|
+
raise Exception(msg)
|
|
787
|
+
elif isinstance(g, (int, float)) == True:
|
|
788
|
+
if g < -1 or g > 1:
|
|
789
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
790
|
+
raise Exception(msg)
|
|
791
|
+
else:
|
|
792
|
+
msg = 'The input g must be int, float or np.ndarray.'
|
|
793
|
+
raise Exception(msg)
|
|
794
|
+
|
|
795
|
+
return g*(3*(4 + g**2))/(5*(2 + g**2))
|
|
796
|
+
|
|
797
|
+
def costheta_D(g, alpha):
|
|
798
|
+
r'''
|
|
799
|
+
| The anisotropy factor as a function of the parameters g and alpha, assuming the Draine
|
|
800
|
+
| scattering phase function.
|
|
801
|
+
| For details please check Draine 2003 [D03].
|
|
802
|
+
|
|
803
|
+
:math:`\langle \cos\theta \rangle_{D}(g, \alpha) = \frac{1 + \alpha(3 + 2g^2)/5}{1 + \alpha(1 + 2g^2)/3} \mbox{ } g`
|
|
804
|
+
|
|
805
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
806
|
+
:type g: float
|
|
807
|
+
|
|
808
|
+
:param alpha: parameter :math:`\alpha` [-]
|
|
809
|
+
:float alpha: float or np.ndarray
|
|
810
|
+
|
|
811
|
+
:return: - **costheta** (*np.ndarray*) – anisotropy factor [-]
|
|
812
|
+
'''
|
|
813
|
+
|
|
814
|
+
if isinstance(g, np.ndarray) == True:
|
|
815
|
+
if np.any(g < -1) or np.any(g > 1):
|
|
816
|
+
msg = 'At least one element in the input g is out of the range [-1, 1].'
|
|
817
|
+
raise Exception(msg)
|
|
818
|
+
elif isinstance(g, (int, float)) == True:
|
|
819
|
+
if g < -1 or g > 1:
|
|
820
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
821
|
+
raise Exception(msg)
|
|
822
|
+
else:
|
|
823
|
+
msg = 'The input g must be int, float or np.ndarray.'
|
|
824
|
+
raise Exception(msg)
|
|
825
|
+
|
|
826
|
+
return g*(1 + alpha*(3 + 2*g**2)/5)/(1 + alpha*(1 + 2*g**2)/3)
|
|
827
|
+
|
|
828
|
+
def costheta_U2(g):
|
|
829
|
+
r'''
|
|
830
|
+
| The anisotropy factor as a function of the parameter g, assuming the Ultraspherical-2
|
|
831
|
+
| scattering phase function.
|
|
832
|
+
| For details please check Baes, Camps & Kapoor 2022 [BCK22].
|
|
833
|
+
|
|
834
|
+
:math:`\langle \cos\theta \rangle_{U2}(g) = \frac{1+g^2}{2g} + \left(\frac{1-g^2}{2g}\right)^2 \mbox{ ln} \left(\frac{1-g}{1+g}\right)`
|
|
835
|
+
|
|
836
|
+
:param g: parameter :math:`g` [-] (must be in the range [-1, 1])
|
|
837
|
+
:type g: float
|
|
838
|
+
|
|
839
|
+
:return: - **costheta** (*np.ndarray*) – anisotropy factor [-]
|
|
840
|
+
'''
|
|
841
|
+
|
|
842
|
+
if isinstance(g, np.ndarray) == True:
|
|
843
|
+
if np.any(g < -1) or np.any(g > 1):
|
|
844
|
+
msg = 'At least one element in the input g is out of the range [-1, 1].'
|
|
845
|
+
raise Exception(msg)
|
|
846
|
+
elif isinstance(g, (int, float)) == True:
|
|
847
|
+
if g < -1 or g > 1:
|
|
848
|
+
msg = 'The input g = {} is out of the range [-1, 1].'.format(g)
|
|
849
|
+
raise Exception(msg)
|
|
850
|
+
else:
|
|
851
|
+
msg = 'The input g must be int, float or np.ndarray.'
|
|
852
|
+
raise Exception(msg)
|
|
853
|
+
|
|
854
|
+
return (1 + g**2)/(2*g) + ((1 - g**2)/(2*g))**2*np.log((1 - g)/(1 + g))
|
|
855
|
+
|
|
856
|
+
def costheta_TTU2(g1, g2, gamma):
|
|
857
|
+
r'''
|
|
858
|
+
| The anisotropy factor as a function of the parameters g1, g2 and gamma, assuming the
|
|
859
|
+
| two-term Ultraspherical-2 scattering phase function.
|
|
860
|
+
| For details please check Baes, Camps & Kapoor 2022 [BCK22].
|
|
861
|
+
|
|
862
|
+
:math:`\langle \cos\theta \rangle_{TTU2}(g_1, g_2, \gamma) = \gamma \mbox{ } \langle \cos\theta \rangle_{U2}(g_1) + (1 - \gamma) \mbox{ } \langle \cos\theta \rangle_{U2}(g_2)`
|
|
863
|
+
|
|
864
|
+
:math:`g_1` characterizes the shape and the strength of the forward scattering peak
|
|
865
|
+
|
|
866
|
+
:math:`g_2` characterizes the shape and the strength of the backward scattering peak
|
|
867
|
+
|
|
868
|
+
:param g1: parameter :math:`g_1` [-] (must be in the range [0, 1])
|
|
869
|
+
:type g1: float
|
|
870
|
+
|
|
871
|
+
:param g2: parameter :math:`g_2` [-] (must be in the range [-1, 0])
|
|
872
|
+
:type g2: float
|
|
873
|
+
|
|
874
|
+
:param gamma: relative weight of the forward scattering component [-] (must be in the range [0, 1])
|
|
875
|
+
:type gamma: float
|
|
876
|
+
|
|
877
|
+
:return: - **costheta** (*np.ndarray*) – anisotropy factor [-]
|
|
878
|
+
'''
|
|
879
|
+
|
|
880
|
+
if isinstance(g1, np.ndarray) == True:
|
|
881
|
+
if np.any(g1 < 0) or np.any(g1 > 1):
|
|
882
|
+
msg = 'At least one element in the input g1 is out of the range [0, 1].'
|
|
883
|
+
raise Exception(msg)
|
|
884
|
+
elif isinstance(g1, (int, float)) == True:
|
|
885
|
+
if g1 < 0 or g1 > 1:
|
|
886
|
+
msg = 'The input g1 = {} is out of the range [0, 1].'.format(g1)
|
|
887
|
+
raise Exception(msg)
|
|
888
|
+
else:
|
|
889
|
+
msg = 'The input g1 must be int, float or np.ndarray.'
|
|
890
|
+
raise Exception(msg)
|
|
891
|
+
|
|
892
|
+
if isinstance(g2, np.ndarray) == True:
|
|
893
|
+
if np.any(g2 < -1) or np.any(g2 > 0):
|
|
894
|
+
msg = 'At least one element in the input g2 is out of the range [-1, 0].'
|
|
895
|
+
raise Exception(msg)
|
|
896
|
+
elif isinstance(g2, (int, float)) == True:
|
|
897
|
+
if g2 < -1 or g2 > 0:
|
|
898
|
+
msg = 'The input g2 = {} is out of the range [-1, 0].'.format(g2)
|
|
899
|
+
raise Exception(msg)
|
|
900
|
+
else:
|
|
901
|
+
msg = 'The input g2 must be int, float or np.ndarray.'
|
|
902
|
+
raise Exception(msg)
|
|
903
|
+
|
|
904
|
+
if isinstance(gamma, np.ndarray) == True:
|
|
905
|
+
if np.any(gamma < 0) or np.any(gamma > 1):
|
|
906
|
+
msg = 'At least one element in the input gamma is out of the range [0, 1].'
|
|
907
|
+
raise Exception(msg)
|
|
908
|
+
elif isinstance(gamma, (int, float)) == True:
|
|
909
|
+
if gamma < 0 or gamma > 1:
|
|
910
|
+
msg = 'The input gamma = {} is out of the range [0, 1].'.format(gamma)
|
|
911
|
+
raise Exception(msg)
|
|
912
|
+
else:
|
|
913
|
+
msg = 'The input gamma must be int, float or np.ndarray.'
|
|
914
|
+
raise Exception(msg)
|
|
915
|
+
|
|
916
|
+
return gamma*costheta_U2(g = g1) + (1 - gamma)*costheta_U2(g = g2)
|
|
917
|
+
|
|
918
|
+
def g_vanGemert(lambda0):
|
|
919
|
+
r'''
|
|
920
|
+
| The anisotropy factor of human EPIDERMIS or DERMIS as a function of wavelength.
|
|
921
|
+
| van Gemert et al. 1989 [v*89]'s fit for experimental data from Bruls & van der Leun 1984
|
|
922
|
+
| [Bv84] (epidermis, 302, 365, 436 and 546 nm) and Jacques, Alter & Prahl 1987 [JAP87]
|
|
923
|
+
| (dermis, 633 nm).
|
|
924
|
+
|
|
925
|
+
:math:`g(\lambda) = 0.29 \times 10^{-3} \lambda + 0.62`
|
|
926
|
+
|
|
927
|
+
| wavelength range: [302 nm, 633 nm]
|
|
928
|
+
|
|
929
|
+
:param lambda0: wavelength [nm]
|
|
930
|
+
:type lambda0: float or np.ndarray
|
|
931
|
+
|
|
932
|
+
:return: - **g** (*float or np.ndarray*) – anisotropy factor [-]
|
|
933
|
+
'''
|
|
934
|
+
|
|
935
|
+
return linear(lambda0, 0.29E-3, 0.62)
|
|
936
|
+
|
|
937
|
+
def g_Bosschaart(lambda0):
|
|
938
|
+
r'''
|
|
939
|
+
| The anisotropy factor of OXYGENATED BLOOD as a function of wavelength.
|
|
940
|
+
| Linear interpolation of experimental data compiled by Bosschaart et al. 2014 [B*14].
|
|
941
|
+
|
|
942
|
+
wavelength range: [251 nm, 1000 nm]
|
|
943
|
+
|
|
944
|
+
:param lambda0: wavelength [nm]
|
|
945
|
+
:type lambda0: float or np.ndarray
|
|
946
|
+
|
|
947
|
+
:return: - **g** (*float or np.ndarray*) – anisotropy factor [-]
|
|
948
|
+
'''
|
|
949
|
+
|
|
950
|
+
return interp1d(np.array(oxy_and_deo_Bosschaart_dataframe)[:,0],
|
|
951
|
+
np.array(oxy_and_deo_Bosschaart_dataframe)[:,4],
|
|
952
|
+
bounds_error = False,
|
|
953
|
+
fill_value = (np.array(oxy_and_deo_Bosschaart_dataframe)[0,4],
|
|
954
|
+
np.array(oxy_and_deo_Bosschaart_dataframe)[-1,4]))(lambda0)
|