OSCR

Quantification of dual-state 5-ALA-induced PpIX fluorescence: methodology and validation in tissue-mimicking phantoms.

Code ↔ Paper

2 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 2 matches
  1. [1] § Materials & methods › Optical properties extraction ↔ src/iad_main.c, lines 52–156 · score 0.67 · reduced scattering coefficient, scattering anisotropy, absorption coefficient, IAD, optical properties, transmittance
  2. [2] § Materials & methods › Optical properties extraction ↔ src/iad_main.c, lines 52–156 · score 0.52 · dual beam spectrophotometer, Optical properties, transmittance, wavelength, sphere, port

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

C · 3,065 lines · 118 KB · MIT · 2 matches

  1. /* Autogenerated v4-0-0 from https://github.com/scottprahl/iad */
  2. #define _CRT_SECURE_NO_WARNINGS
  3. #define _CRT_NONSTDC_NO_WARNINGS
  4. #define NO_SLIDES 0
  5. #define ONE_SLIDE_ON_TOP 1
  6. #define TWO_IDENTICAL_SLIDES 2
  7. #define ONE_SLIDE_ON_BOTTOM 3
  8. #define ONE_SLIDE_NEAR_SPHERE 4
  9. #define ONE_SLIDE_NOT_NEAR_SPHERE 5
  10. #define MR_IS_ONLY_RD 1
  11. #define MT_IS_ONLY_TD 2
  12. #define NO_UNSCATTERED_LIGHT 3
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <stdlib.h>
  16. #include <unistd.h>
  17. #include <time.h>
  18. #include <math.h>
  19. #include <ctype.h>
  20. #include <errno.h>
  21. #include "ad_globl.h"
  22. #include "ad_prime.h"
  23. #include "iad_type.h"
  24. #include "iad_pub.h"
  25. #include "iad_io.h"
  26. #include "iad_calc.h"
  27. #include "iad_util.h"
  28. #include "version.h"
  29. #include "mc_lost.h"
  30. #include "ad_frsnl.h"
  31. static void print_version(int verbosity)
  32. {
  33. if (verbosity == 0) {
  34. fprintf(stdout, "%s", VersionShort);
  35. }
  36. else {
  37. fprintf(stdout, "iad %s\n", Version);
  38. fprintf(stdout, "Copyright 1993-2026 Scott Prahl, [email hidden]\n");
  39. fprintf(stdout, " (see Applied Optics, 32:559-568, 1993)\n\n");
  40. fprintf(stdout, "This is free software; see the source for copying conditions.\n");
  41. fprintf(stdout, "There is no warranty; not even for MERCHANTABILITY or FITNESS.\n");
  42. fprintf(stdout, "FOR A PARTICULAR PURPOSE.\n");
  43. }
  44. }
  45. static void print_usage(void)
  46. {
  47. fprintf(stdout, "iad %s\n\n", Version);
  48. fprintf(stdout, "iad finds optical properties from measurements\n\n");
  49. fprintf(stdout, "Usage: iad [options] input\n\n");
  50. fprintf(stdout, "Options:\n");
  51. fprintf(stdout, " -1 '# # # # #' reflection sphere parameters \n");
  52. fprintf(stdout, " 'd_sphere d d_sample_port d_entrance_port d_detector_port r_wall'\n");
  53. fprintf(stdout, " -2 '# # # # #' transmission sphere parameters \n");
  54. fprintf(stdout, " 'd_sphere d d_sample_port d_third_port d_detector_port r_wall'\n");
  55. fprintf(stdout, " -a # use this albedo \n");
  56. fprintf(stdout, " -A # use this absorption coefficient \n");
  57. fprintf(stdout, " -b # use this optical thickness \n");
  58. fprintf(stdout, " -B # beam diameter \n");
  59. fprintf(stdout, " -c # fraction of unscattered refl in MR\n");
  60. fprintf(stdout, " -C # fraction of unscattered trans in MT\n");
  61. fprintf(stdout, " -d # thickness of sample \n");
  62. fprintf(stdout, " -D # thickness of slide \n");
  63. fprintf(stdout, " -e # error tolerance (default 0.0001) \n");
  64. fprintf(stdout, " -E # optical depth (=mua*D) for slides\n");
  65. fprintf(stdout, " -f # allow a fraction 0.0-1.0 of light to hit sphere wall first\n");
  66. fprintf(stdout, " -F # constrain scattering coefficient \n");
  67. fprintf(stdout, " # = constant: use constant scattering coefficient \n");
  68. fprintf(stdout, " # = 'P lambda0 mus0 gamma' then mus=mus0*(lambda/lambda0)^gamma\n");
  69. fprintf(stdout, " -g # scattering anisotropy (default 0) \n");
  70. fprintf(stdout, " -G # type of boundary '0', '2', 't', 'b', 'n', 'f' \n");
  71. fprintf(stdout, " '0' or '2' --- number of slides\n");
  72. fprintf(stdout, " 't' (top) or 'b' (bottom) \
  73. --- one slide that is hit by light first\n");
  74. fprintf(stdout, " 'n' (near) or 'f' (far) \
  75. --- one slide position relative to sphere\n");
  76. fprintf(stdout, " -h display help\n");
  77. fprintf(stdout, " -H # # = 0, no baffles for R or T spheres\n");
  78. fprintf(stdout, " # = 1, baffle for R but not for T sphere\n");
  79. fprintf(stdout, " # = 2, baffle for T but not for R sphere\n");
  80. fprintf(stdout, " # = 3, baffle for both R and T spheres (default)\n");
  81. fprintf(stdout, " -i # incident angle in degrees\n");
  82. fprintf(stdout, " -j # constrain reduced scattering coefficient \n");
  83. fprintf(stdout, " -J generate grid after inverse calculation\n");
  84. fprintf(stdout, " -l # wavelength limits\n");
  85. fprintf(stdout, " -L # specify the wavelength lambda\n");
  86. fprintf(stdout, " -M # limit number of Monte Carlo iterations\n");
  87. fprintf(stdout, " -n # specify index of refraction of slab\n");
  88. fprintf(stdout, " -N # specify index of refraction of slides\n");
  89. fprintf(stdout, " -o filename explicitly specify filename for output\n");
  90. fprintf(stdout, " -p # # of Monte Carlo photons (default 100000)\n");
  91. fprintf(stdout, " a negative number is max MC time in milliseconds\n");
  92. fprintf(stdout, " -q # number of quadrature points (default=8)\n");
  93. fprintf(stdout, " -r # total reflection measurement\n");
  94. fprintf(stdout, " -R # actual reflectance for 100%% measurement \n");
  95. fprintf(stdout, " -s # specify type of search to do\n");
  96. fprintf(stdout, " -S # number of spheres used\n");
  97. fprintf(stdout, " -t # total transmission measurement\n");
  98. fprintf(stdout, " -T # actual transmission for 100%% measurement \n");
  99. fprintf(stdout, " -u # unscattered transmission measurement\n");
  100. fprintf(stdout, " -v version information\n");
  101. fprintf(stdout, " -V 0 verbosity low --- no output to stdout\n");
  102. fprintf(stdout, " -V 1 verbosity moderate \n");
  103. fprintf(stdout, " -V 2 verbosity high\n");
  104. fprintf(stdout, " -w # wall reflectivity for reflection sphere\n");
  105. fprintf(stdout, " -W # wall reflectivity for transmission sphere\n");
  106. fprintf(stdout, " -x # set debugging level\n");
  107. fprintf(stdout, " -X dual beam configuration\n");
  108. fprintf(stdout, " -Y ignore the diffuse lost-light correction (temporary)\n");
  109. fprintf(stdout, " -z do forward calculation\n");
  110. fprintf(stdout, "Examples:\n");
  111. fprintf(stdout, " iad file.rxt Results will be put in file.txt\n");
  112. fprintf(stdout, " iad file Same as above\n");
  113. fprintf(stdout, " iad -c 0.9 file.rxt \
  114. Assume M_R includes 90%% of unscattered reflectance\n");
  115. fprintf(stdout, " iad -C 0.8 file.rxt \
  116. Assume M_T includes 80%% of unscattered transmittance\n");
  117. fprintf(stdout, " iad -e 0.0001 file.rxt Better convergence to R & T values\n");
  118. fprintf(stdout, " iad -f 1.0 file.rxt All light hits reflectance sphere wall first\n");
  119. fprintf(stdout, " iad -l '500 600' file.rxt Only do wavelengths between 500 and 600\n");
  120. fprintf(stdout, " iad -o out file.rxt Calculated values in out\n");
  121. fprintf(stdout, " iad -r 0.3 R_total=0.3, b=inf, find albedo\n");
  122. fprintf(stdout, " iad -r 0.3 -t 0.4 R_total=0.3, T_total=0.4, find a,b,g\n");
  123. fprintf(stdout, " iad -r 0.3 -t 0.4 -n 1.5 R_total=0.3, T_total=0.4, n=1.5, find a,b\n");
  124. fprintf(stdout, " iad -r 0.3 -t 0.4 R_total=0.3, T_total=0.4, find a,b\n");
  125. fprintf(stdout, " iad -p 1000 file.rxt Only 1000 photons\n");
  126. fprintf(stdout, " iad -p -100 file.rxt Allow only 100ms per iteration\n");
  127. fprintf(stdout, " iad -q 4 file.rxt Four quadrature points\n");
  128. fprintf(stdout, " iad -M 0 file.rxt No MC (iad)\n");
  129. fprintf(stdout, " iad -M 1 file.rxt MC once (iad -> MC -> iad)\n");
  130. fprintf(stdout, " iad -M 2 file.rxt MC twice (iad -> MC -> iad -> MC -> iad)\n");
  131. fprintf(stdout, " iad -M 0 -q 4 file.rxt Fast and crude conversion\n");
  132. fprintf(stdout, " iad -G t file.rxt One top slide with properties from file.rxt\n");
  133. fprintf(stdout, " iad -G b -N 1.5 -D 1 file Use 1 bottom slide with n=1.5 and thickness=1\n");
  134. fprintf(stdout, " iad -x 1 file.rxt Show sphere and MC effects\n");
  135. fprintf(stdout, " iad -x 2 file.rxt Show grid decisions\n");
  136. fprintf(stdout, " iad -x 4 file.rxt Show iterations\n");
  137. fprintf(stdout, " iad -x 8 file.rxt Show lost light effects\n");
  138. fprintf(stdout, " iad -x 16 file.rxt Show best grid points\n");
  139. fprintf(stdout, " iad -x 32 file.rxt Show decisions for type of search\n");
  140. fprintf(stdout, " iad -x 64 file.rxt Show all grid calculations\n");
  141. fprintf(stdout, " iad -x 128 file.rxt Show sphere calculations\n");
  142. fprintf(stdout, " iad -x 256 file.rxt DEBUG_EVERY_CALC\n");
  143. fprintf(stdout, " iad -x 511 file.rxt Show all debugging output\n");
  144. fprintf(stdout, " iad -X -i 8 file.rxt Dual beam spectrometer with 8 degree incidence\n\n");
  145. fprintf(stdout, " iad -z -a 0.9 -b 1 -i 45 Forward calc assuming 45 degree incidence\n\n");
  146. fprintf(stdout, " iad * Process all .rxt files in current directory\n");
  147. fprintf(stdout, " iad x.rxt y.rxt Process multiple files\n\n");
  148. fprintf(stdout, "Report bugs to <[email hidden]>\n\n");
  149. }
  150. static char *strdup_together(char *s, char *t)
  151. {
  152. char *both;
  153. if (s == NULL) {
  154. if (t == NULL)
  155. return NULL;
  156. return strdup(t);
  157. }
  158. if (t == NULL)
  159. return strdup(s);
  160. both = malloc(strlen(s) + strlen(t) + 1);
  161. if (both == NULL)
  162. fprintf(stderr, "Could not allocate memory for both strings.\n");
  163. strcpy(both, s);
  164. strcat(both, t);
  165. return both;
  166. }
  167. static double my_strtod(const char *str)
  168. {
  169. char *endptr;
  170. errno = 0;
  171. double val = strtod(str, &endptr);
  172. if (endptr == str) {
  173. fprintf(stderr, "Error in the command line\n");
  174. fprintf(stderr, " No conversion could be performed for `%s`.\n", str);
  175. exit(EXIT_FAILURE);
  176. }
  177. if (*endptr != '\0') {
  178. fprintf(stderr, "Error in the command line\n");
  179. fprintf(stderr, " Partial conversion of string = '%s'\n", str);
  180. exit(EXIT_FAILURE);
  181. }
  182. if (errno == ERANGE) {
  183. fprintf(stderr, "Error in the command line\n");
  184. printf(" The value '%s' is out of range of double.\n", str);
  185. exit(EXIT_FAILURE);
  186. }
  187. return val;
  188. }
  189. static double seconds_elapsed(clock_t start_time)
  190. {
  191. clock_t finish_time = clock();
  192. return (double) (finish_time - start_time) / CLOCKS_PER_SEC;
  193. }
  194. static void print_error_legend(void)
  195. {
  196. if (Debug(DEBUG_ANY))
  197. return;
  198. fprintf(stderr, "----------------- Sorry, but ... errors encountered ---------------\n");
  199. fprintf(stderr, " * ==> Success ");
  200. fprintf(stderr, " 0-9 ==> Monte Carlo Iteration\n");
  201. fprintf(stderr, " R ==> M_R is too big ");
  202. fprintf(stderr, " r ==> M_R is too small\n");
  203. fprintf(stderr, " T ==> M_T is too big ");
  204. fprintf(stderr, " t ==> M_T is too small\n");
  205. fprintf(stderr, " U ==> M_U is too big ");
  206. fprintf(stderr, " u ==> M_U is too small\n");
  207. fprintf(stderr, " ! ==> M_R + M_T > 1 ");
  208. fprintf(stderr, " + ==> Hit iteration limit\n");
  209. fprintf(stderr, " x ==> No solution found");
  210. fprintf(stderr, " m ==> Lost-light correction failed\n");
  211. fprintf(stderr, " P ==> Port geometry is wrong");
  212. fprintf(stderr, " L ==> Lost light puts data out of reach\n\n");
  213. }
  214. static char what_char(int err)
  215. {
  216. if (err == IAD_NO_ERROR)
  217. return '*';
  218. if (err == IAD_TOO_MANY_ITERATIONS)
  219. return '+';
  220. if (err == IAD_MR_TOO_BIG)
  221. return 'R';
  222. if (err == IAD_MR_TOO_SMALL)
  223. return 'r';
  224. if (err == IAD_MT_TOO_BIG)
  225. return 'T';
  226. if (err == IAD_MT_TOO_SMALL)
  227. return 't';
  228. if (err == IAD_MU_TOO_BIG)
  229. return 'U';
  230. if (err == IAD_MU_TOO_SMALL)
  231. return 'u';
  232. if (err == IAD_TOO_MUCH_LIGHT)
  233. return '!';
  234. if (err == IAD_SEARCH_STALLED)
  235. return 'x';
  236. if (err == IAD_MC_DID_NOT_CONVERGE)
  237. return 'm';
  238. if (err == IAD_UNREACHABLE_WITH_LOST_LIGHT)
  239. return 'L';
  240. if (err == IAD_BEAM_TOO_BIG_FOR_SAMPLE_PORT)
  241. return 'P';
  242. if (err == IAD_BEAM_TOO_BIG_FOR_ENTRANCE_PORT)
  243. return 'P';
  244. if (err == IAD_SAMPLE_PORTS_DIFFER)
  245. return 'P';
  246. if (err == IAD_BEAM_TOO_BIG_FOR_EXIT_PORT)
  247. return 'P';
  248. if (err == IAD_BEAM_NOT_VALID)
  249. return 'P';
  250. return '?';
  251. }
  252. static void print_long_error(int err)
  253. {
  254. switch (err) {
  255. case IAD_NO_ERROR:
  256. fprintf(stderr, "Successful Search\n");
  257. break;
  258. case IAD_TOO_MANY_ITERATIONS:
  259. fprintf(stderr, "Failed Search, hit the limit of %d iterations\n", IAD_MAX_ITERATIONS);
  260. break;
  261. case IAD_SEARCH_STALLED:
  262. fprintf(stderr, "Failed Search, stopped early without matching the measurements\n");
  263. fprintf(stderr, " no combination of a, b, and g reproduced M_R and M_T;\n");
  264. fprintf(stderr, " compare the measured and fitted values above\n");
  265. break;
  266. case IAD_BEAM_TOO_BIG_FOR_SAMPLE_PORT:
  267. fprintf(stderr, "Failed Search, beam is wider than the sample port\n");
  268. fprintf(stderr, " part of the beam missed the sample entirely;\n");
  269. fprintf(stderr, " check the beam diameter and the sample port size\n");
  270. break;
  271. case IAD_BEAM_TOO_BIG_FOR_ENTRANCE_PORT:
  272. fprintf(stderr, "Failed Search, beam is wider than the entrance port\n");
  273. fprintf(stderr, " the beam was clipped entering the sphere;\n");
  274. fprintf(stderr, " check the beam diameter and the entrance port size\n");
  275. break;
  276. case IAD_BEAM_NOT_VALID:
  277. fprintf(stderr, "Failed Search, the beam has no width\n");
  278. fprintf(stderr, " a beam diameter must be positive; the default is 1 mm\n");
  279. fprintf(stderr, " and -B sets it explicitly\n");
  280. break;
  281. case IAD_BEAM_TOO_BIG_FOR_EXIT_PORT:
  282. fprintf(stderr, "Failed Search, beam is wider than the exit port\n");
  283. fprintf(stderr, " only part of the direct beam would reach the standard;\n");
  284. fprintf(stderr, " use an exit port of zero to close it off entirely\n");
  285. break;
  286. case IAD_SAMPLE_PORTS_DIFFER:
  287. fprintf(stderr, "Failed Search, the two spheres disagree about the sample port\n");
  288. fprintf(stderr, " the sample sits in one hole, so both sphere blocks\n");
  289. fprintf(stderr, " must give it the same diameter\n");
  290. break;
  291. case IAD_MC_DID_NOT_CONVERGE:
  292. fprintf(stderr, "Failed Search, lost-light correction did not settle\n");
  293. fprintf(stderr, " the Monte Carlo re-inversion stopped converging;\n");
  294. fprintf(stderr, " try -M 0 to skip the lost-light correction\n");
  295. break;
  296. case IAD_UNREACHABLE_WITH_LOST_LIGHT:
  297. fprintf(stderr, "Failed Search, lost light puts the data out of reach\n");
  298. fprintf(stderr, " once the estimated lost light is subtracted, no sample\n");
  299. fprintf(stderr, " reproduces these measurements: even one that absorbs\n");
  300. fprintf(stderr, " nothing is too dim. Either the measurements or the\n");
  301. fprintf(stderr, " sphere and port description do not describe the same\n");
  302. fprintf(stderr, " experiment. Use -x 8 to see the shortfall, or -M 0 to\n");
  303. fprintf(stderr, " invert without the lost-light correction\n");
  304. break;
  305. case IAD_MR_TOO_BIG:
  306. fprintf(stderr, "Failed Search, M_R is too big\n");
  307. break;
  308. case IAD_MR_TOO_SMALL:
  309. fprintf(stderr, "Failed Search, M_R is too small\n");
  310. break;
  311. case IAD_MT_TOO_BIG:
  312. fprintf(stderr, "Failed Search, M_T is too big\n");
  313. break;
  314. case IAD_MT_TOO_SMALL:
  315. fprintf(stderr, "Failed Search, M_T is too small\n");
  316. break;
  317. case IAD_MU_TOO_BIG:
  318. fprintf(stderr, "Failed Search, M_U is too big\n");
  319. break;
  320. case IAD_MU_TOO_SMALL:
  321. fprintf(stderr, "Failed Search, M_U is too small\n");
  322. break;
  323. case IAD_TOO_MUCH_LIGHT:
  324. fprintf(stderr, "Failed Search, M_R + M_T exceeds 1\n");
  325. break;
  326. case IAD_RT_LT_MINIMUM:
  327. fprintf(stderr, "Failed Search, M_R + M_T is below the minimum possible\n");
  328. break;
  329. case IAD_EXCESSIVE_LIGHT_LOSS:
  330. fprintf(stderr, "Failed Search, too much light lost out the sides\n");
  331. break;
  332. case IAD_AS_NOT_VALID:
  333. fprintf(stderr, "Failed Search, sample port is too big for the sphere\n");
  334. break;
  335. case IAD_AE_NOT_VALID:
  336. fprintf(stderr, "Failed Search, entrance port is too big for the sphere\n");
  337. break;
  338. case IAD_AD_NOT_VALID:
  339. fprintf(stderr, "Failed Search, detector port is too big for the sphere\n");
  340. break;
  341. case IAD_RW_NOT_VALID:
  342. fprintf(stderr, "Failed Search, sphere wall reflectance is not between 0 and 1\n");
  343. break;
  344. case IAD_RD_NOT_VALID:
  345. fprintf(stderr, "Failed Search, detector reflectance is not between 0 and 1\n");
  346. break;
  347. case IAD_RSTD_NOT_VALID:
  348. fprintf(stderr, "Failed Search, reflectance standard is not between 0 and 1\n");
  349. break;
  350. case IAD_TSTD_NOT_VALID:
  351. fprintf(stderr, "Failed Search, transmittance standard is not between 0 and 1\n");
  352. break;
  353. case IAD_QUAD_PTS_NOT_VALID:
  354. fprintf(stderr, "Failed Search, number of quadrature points is not valid\n");
  355. break;
  356. case IAD_BAD_G_VALUE:
  357. fprintf(stderr, "Failed Search, anisotropy is not between -1 and 1\n");
  358. break;
  359. case IAD_BAD_PHASE_FUNCTION:
  360. fprintf(stderr, "Failed Search, unknown phase function\n");
  361. break;
  362. case IAD_GAMMA_NOT_VALID:
  363. fprintf(stderr, "Failed Search, gamma is not valid\n");
  364. break;
  365. case IAD_F_NOT_VALID:
  366. fprintf(stderr, "Failed Search, sphere wall fraction is not valid\n");
  367. break;
  368. case IAD_TOO_MANY_LAYERS:
  369. fprintf(stderr, "Failed Search, too many layers\n");
  370. break;
  371. case IAD_MEMORY_ERROR:
  372. fprintf(stderr, "Failed Search, out of memory\n");
  373. break;
  374. case IAD_FILE_ERROR:
  375. fprintf(stderr, "Failed Search, error reading the input file\n");
  376. break;
  377. default:
  378. fprintf(stderr, "Failed Search, error %d\n", err);
  379. break;
  380. }
  381. fprintf(stderr, "\n");
  382. }
  383. static void print_dot(clock_t start_time, int err, int points, int final, int verbosity)
  384. {
  385. static int counter = 0;
  386. counter++;
  387. if (verbosity == 0 || Debug(DEBUG_ANY))
  388. return;
  389. if (final)
  390. fprintf(stderr, "%c", what_char(err));
  391. else {
  392. counter--;
  393. fprintf(stderr, "%1d\b", points % 10);
  394. }
  395. if (final) {
  396. if (counter % 50 == 0) {
  397. double rate = (seconds_elapsed(start_time) / counter);
  398. fprintf(stderr, " %3d done (%5.2f s/pt)\n", counter, rate);
  399. }
  400. else if (counter % 10 == 0)
  401. fprintf(stderr, " ");
  402. }
  403. fflush(stderr);
  404. }
  405. static void bright_mr_mt(struct measure_type m, struct invert_type r, double b, double *m_r, double *m_t)
  406. {
  407. r.slab.a = 1.0;
  408. r.slab.b = b;
  409. r.a = 1.0;
  410. r.b = b;
  411. Calculate_MR_MT(m, r, MC_USE_EXISTING, TRUE, m_r, m_t);
  412. }
  413. static double solve_for_b_from_mt(struct measure_type m, struct invert_type r, double target)
  414. {
  415. double lo = 1e-6, hi = 1e4, mid, m_r, m_t;
  416. int i;
  417. for (i = 0; i < 40; i++) {
  418. mid = sqrt(lo * hi);
  419. bright_mr_mt(m, r, mid, &m_r, &m_t);
  420. if (m_t > target)
  421. lo = mid;
  422. else
  423. hi = mid;
  424. }
  425. return sqrt(lo * hi);
  426. }
  427. static void calculate_coefficients(struct measure_type m,
  428. struct invert_type r, double *LR, double *LT, double *musp, double *mua)
  429. {
  430. double mus;
  431. *LR = 0;
  432. *LT = 0;
  433. if (r.error == IAD_NO_ERROR || r.error == IAD_TOO_MANY_ITERATIONS ||
  434. r.error == IAD_SEARCH_STALLED || r.error == IAD_MC_DID_NOT_CONVERGE ||
  435. r.error == IAD_UNREACHABLE_WITH_LOST_LIGHT) {
  436. Calculate_MR_MT(m, r, MC_USE_EXISTING, TRUE, LR, LT);
  437. Calculate_Mua_Musp(m, r, &mus, musp, mua);
  438. }
  439. else {
  440. *musp = 0;
  441. *mua = 0;
  442. }
  443. }
  444. static int parse_string_into_array(char *s, double *a, int n)
  445. {
  446. char *t, *last, *r;
  447. int i = 0;
  448. t = s;
  449. last = s + strlen(s);
  450. while (t < last) {
  451. r = t;
  452. while (*r != ' ' && *r != '\0')
  453. r++;
  454. *r = '\0';
  455. if (sscanf(t, "%lf", &(a[i])) == 0)
  456. return 1;
  457. i++;
  458. if (i == n) {
  459. if (i == 5) {
  460. if (a[i - 1] <= 0 || a[i - 1] > 1) {
  461. fprintf(stderr, "Sphere wall reflectivity (r_w=%g) must be a fraction less than one.\n", a[i - 1]);
  462. exit(EXIT_FAILURE);
  463. }
  464. }
  465. return 0;
  466. }
  467. t = r + 1;
  468. }
  469. return 1;
  470. }
  471. static int has_rxt_extension(char *s)
  472. {
  473. size_t len;
  474. char *extension = ".rxt";
  475. size_t extension_len = strlen(extension);
  476. if (s == NULL)
  477. return 0;
  478. len = strlen(s);
  479. if (len < extension_len)
  480. return 0;
  481. return strcmp(s + len - extension_len, extension) == 0;
  482. }
  483. static void print_results_header(FILE *fp)
  484. {
  485. if (Debug(DEBUG_LOST_LIGHT)) {
  486. fprintf(fp, "# | Meas M_R | Meas M_T | calc calc calc |");
  487. fprintf(fp, " Lost Lost Lost Lost | MC AD Error\n");
  488. fprintf(fp, "# wave | M_R fit | M_T fit | mu_a mu_s' g | ");
  489. fprintf(fp, " UR1 URU UT1 UTU | # # Type\n");
  490. fprintf(fp, "# nm | --- --- | --- --- | 1/mm 1/mm --- |");
  491. fprintf(fp, " --- --- --- --- | --- --- ---\n");
  492. fprintf(fp, "#---------------------------------------------------------");
  493. fprintf(fp, "--------------------------------------------------------\n");
  494. }
  495. else {
  496. fprintf(fp, "# \t Meas \t Fit \t Meas \t Fit \t \t \t \t");
  497. fprintf(fp, "\n");
  498. fprintf(fp, "##wave\t M_R \t M_R \t M_T \t M_T \t mu_a \t mu_s'\t g \t");
  499. fprintf(fp, "\n");
  500. fprintf(fp, "# [nm]\t[---] \t[---] \t[---] \t[---] \t[1/mm]\t[1/mm]\t[---] \t");
  501. fprintf(fp, "\n");
  502. }
  503. }
  504. void print_optical_property_result(FILE *fp,
  505. struct measure_type m, struct invert_type r, double LR, double LT, double mu_a, double mu_sp, int line)
  506. {
  507. int display_error = (!r.found && r.error == IAD_NO_ERROR) ? IAD_SEARCH_STALLED : r.error;
  508. if (Debug(DEBUG_LOST_LIGHT)) {
  509. if (m.lambda != 0)
  510. fprintf(fp, "%6.1f ", m.lambda);
  511. else
  512. fprintf(fp, "%6d ", line);
  513. if (mu_a >= 200)
  514. mu_a = 199.9999;
  515. if (mu_sp >= 1000)
  516. mu_sp = 999.9999;
  517. fprintf(fp, "%6.4f % 6.4f | ", m.m_r, LR);
  518. fprintf(fp, "%6.4f % 6.4f | ", m.m_t, LT);
  519. fprintf(fp, "%6.3f ", mu_a);
  520. fprintf(fp, "%6.3f ", mu_sp);
  521. fprintf(fp, "%6.3f |", r.g);
  522. fprintf(fp, " %6.4f %6.4f ", m.lost_r.direct, m.lost_r.diffuse);
  523. fprintf(fp, "%6.4f %6.4f | ", m.lost_t.direct, m.utu_lost);
  524. fprintf(fp, "%2d ", r.MC_iterations);
  525. fprintf(fp, "%3d", r.AD_iterations);
  526. fprintf(fp, " %c \n", what_char(display_error));
  527. }
  528. else {
  529. if (m.lambda != 0)
  530. fprintf(fp, "%6.1f\t", m.lambda);
  531. else
  532. fprintf(fp, "%6d\t", line);
  533. if (mu_a >= 200)
  534. mu_a = 199.9999;
  535. if (mu_sp >= 1000)
  536. mu_sp = 999.9999;
  537. fprintf(fp, "%6.4f\t%6.4f\t", m.m_r, LR);
  538. fprintf(fp, "%6.4f\t%6.4f\t", m.m_t, LT);
  539. fprintf(fp, "%6.4f\t", mu_a);
  540. fprintf(fp, "%6.4f\t", mu_sp);
  541. fprintf(fp, "%6.4f\t", r.g);
  542. fprintf(fp, " %c \n", what_char(display_error));
  543. }
  544. fflush(fp);
  545. }
  546. int main(int argc, char **argv)
  547. {
  548. struct measure_type m;
  549. struct invert_type r;
  550. char *g_out_name = NULL;
  551. char *g_grid_name = NULL;
  552. int c;
  553. long n_photons = 100000;
  554. int MAX_MC_iterations = 19;
  555. int any_error = 0;
  556. int last_error = IAD_NO_ERROR;
  557. int process_command_line = 0;
  558. int params = 0;
  559. int rt_total = 0;
  560. int mc_total = 0;
  561. int file_index = 0;
  562. int file_count = 1;
  563. int cl_quadrature_points = UNINITIALIZED;
  564. int cl_verbosity = 2;
  565. double cl_forward_calc = UNINITIALIZED;
  566. double cl_grid_calc = UNINITIALIZED;
  567. double cl_default_a = UNINITIALIZED;
  568. double cl_default_g = UNINITIALIZED;
  569. double cl_default_b = UNINITIALIZED;
  570. double cl_default_mua = UNINITIALIZED;
  571. double cl_default_mus = UNINITIALIZED;
  572. double cl_default_musp = UNINITIALIZED;
  573. double cl_tolerance = UNINITIALIZED;
  574. double cl_slide_OD = UNINITIALIZED;
  575. double cl_cos_angle = UNINITIALIZED;
  576. double cl_beam_d = UNINITIALIZED;
  577. double cl_sample_d = UNINITIALIZED;
  578. double cl_sample_n = UNINITIALIZED;
  579. double cl_slide_d = UNINITIALIZED;
  580. double cl_slide_n = UNINITIALIZED;
  581. double cl_slides = UNINITIALIZED;
  582. double cl_default_fr = UNINITIALIZED;
  583. double cl_rstd_t = UNINITIALIZED;
  584. double cl_rstd_r = UNINITIALIZED;
  585. double cl_baffle_r = UNINITIALIZED;
  586. double cl_baffle_t = UNINITIALIZED;
  587. double cl_ru_fraction = UNINITIALIZED;
  588. double cl_tu_fraction = UNINITIALIZED;
  589. double cl_lambda = UNINITIALIZED;
  590. double cl_rwall_r = UNINITIALIZED;
  591. double cl_rwall_t = UNINITIALIZED;
  592. double cl_search = UNINITIALIZED;
  593. double cl_mus0 = UNINITIALIZED;
  594. double cl_mus0_pwr = UNINITIALIZED;
  595. double cl_mus0_lambda = UNINITIALIZED;
  596. double cl_UR1 = UNINITIALIZED;
  597. double cl_UT1 = UNINITIALIZED;
  598. double cl_Tc = UNINITIALIZED;
  599. double cl_method = UNINITIALIZED;
  600. int cl_num_spheres = UNINITIALIZED;
  601. double cl_sphere_one[5] = { UNINITIALIZED, UNINITIALIZED, UNINITIALIZED,
  602. UNINITIALIZED, UNINITIALIZED
  603. };
  604. double cl_sphere_two[5] = { UNINITIALIZED, UNINITIALIZED, UNINITIALIZED,
  605. UNINITIALIZED, UNINITIALIZED
  606. };
  607. double cl_wave_limit[2] = { UNINITIALIZED, UNINITIALIZED };
  608. clock_t start_time = clock();
  609. char command_line_options[] = "1:2:a:A:b:B:c:C:d:D:e:E:f:F:g:G:hH:i:j:Jl:L:M:n:N:o:p:q:r:R:s:S:t:T:u:vV:w:W:x:XYz";
  610. char *command_line = NULL;
  611. {
  612. size_t command_line_length = 0;
  613. for (int i = 0; i < argc; ++i) {
  614. command_line_length += strlen(argv[i]) + 3;
  615. }
  616. command_line = (char *) malloc(command_line_length);
  617. if (command_line == NULL) {
  618. fprintf(stderr, "Memory allocation failed\n");
  619. return 1;
  620. }
  621. strcpy(command_line, "");
  622. for (int i = 0; i < argc; ++i) {
  623. if (strchr(argv[i], ' ') != NULL) {
  624. strcat(command_line, "'");
  625. strcat(command_line, argv[i]);
  626. strcat(command_line, "' ");
  627. }
  628. else {
  629. strcat(command_line, argv[i]);
  630. strcat(command_line, " ");
  631. }
  632. }
  633. optind = 1;
  634. }
  635. while ((c = getopt(argc, argv, command_line_options)) != EOF) {
  636. int n;
  637. char cc;
  638. char *tmp_str = NULL;
  639. switch (c) {
  640. case '1':
  641. tmp_str = strdup(optarg);
  642. parse_string_into_array(optarg, cl_sphere_one, 5);
  643. if (cl_sphere_one[4] == UNINITIALIZED) {
  644. fprintf(stderr, "Error in the command-line argument for -1\n");
  645. fprintf(stderr, " the current argument is '%s' but it must have 5 terms: ", tmp_str);
  646. fprintf(stderr, "'d_sphere d_sample d_entrance d_detector r_wall'\n");
  647. exit(EXIT_FAILURE);
  648. }
  649. break;
  650. case '2':
  651. tmp_str = strdup(optarg);
  652. parse_string_into_array(optarg, cl_sphere_two, 5);
  653. if (cl_sphere_two[4] == UNINITIALIZED) {
  654. fprintf(stderr, "Error in the command-line argument for -2\n");
  655. fprintf(stderr, " the current argument is '%s' but it must have 5 terms: ", tmp_str);
  656. fprintf(stderr, "'d_sphere d_sample d_third d_detector r_wall'\n");
  657. exit(EXIT_FAILURE);
  658. }
  659. break;
  660. case 'a':
  661. cl_default_a = my_strtod(optarg);
  662. if (cl_default_a < 0 || cl_default_a > 1) {
  663. fprintf(stderr, "Error in the command line\n");
  664. fprintf(stderr, " albedo '-a %s'\n", optarg);
  665. exit(EXIT_FAILURE);
  666. }
  667. break;
  668. case 'A':
  669. cl_default_mua = my_strtod(optarg);
  670. if (cl_default_mua < 0) {
  671. fprintf(stderr, "Error in the command line\n");
  672. fprintf(stderr, " absorption '-A %s'\n", optarg);
  673. exit(EXIT_FAILURE);
  674. }
  675. break;
  676. case 'b':
  677. cl_default_b = my_strtod(optarg);
  678. if (cl_default_b < 0) {
  679. fprintf(stderr, "Error in the command line\n");
  680. fprintf(stderr, " optical thickness '-b %s'\n", optarg);
  681. exit(EXIT_FAILURE);
  682. }
  683. break;
  684. case 'B':
  685. cl_beam_d = my_strtod(optarg);
  686. if (cl_beam_d < 0) {
  687. fprintf(stderr, "Error in the command line\n");
  688. fprintf(stderr, " beam diameter '-B %s'\n", optarg);
  689. exit(EXIT_FAILURE);
  690. }
  691. break;
  692. case 'c':
  693. cl_ru_fraction = my_strtod(optarg);
  694. if (cl_ru_fraction < 0.0 || cl_ru_fraction > 1.0) {
  695. fprintf(stderr, "Error in the command line\n");
  696. fprintf(stderr, " unscattered refl fraction '-c %s'\n", optarg);
  697. fprintf(stderr, " must be between 0 and 1\n");
  698. exit(EXIT_SUCCESS);
  699. }
  700. break;
  701. case 'C':
  702. cl_tu_fraction = my_strtod(optarg);
  703. if (cl_tu_fraction < 0.0 || cl_tu_fraction > 1.0) {
  704. fprintf(stderr, "Error in the command line\n");
  705. fprintf(stderr, " unscattered trans fraction '-C %s'\n", optarg);
  706. fprintf(stderr, " must be between 0 and 1\n");
  707. exit(EXIT_SUCCESS);
  708. }
  709. break;
  710. case 'd':
  711. cl_sample_d = my_strtod(optarg);
  712. if (cl_sample_d < 0) {
  713. fprintf(stderr, "Error in the command line\n");
  714. fprintf(stderr, " sample thickness '-d %s'\n", optarg);
  715. exit(EXIT_FAILURE);
  716. }
  717. break;
  718. case 'D':
  719. cl_slide_d = my_strtod(optarg);
  720. if (cl_slide_d < 0) {
  721. fprintf(stderr, "Error in the command line\n");
  722. fprintf(stderr, " slide thickness '-D %s'\n", optarg);
  723. exit(EXIT_FAILURE);
  724. }
  725. break;
  726. case 'e':
  727. cl_tolerance = my_strtod(optarg);
  728. if (cl_tolerance < 0) {
  729. fprintf(stderr, "Error in the command line\n");
  730. fprintf(stderr, " error tolerance '-e %s'\n", optarg);
  731. exit(EXIT_FAILURE);
  732. }
  733. break;
  734. case 'E':
  735. cl_slide_OD = my_strtod(optarg);
  736. if (cl_slide_OD < 0) {
  737. fprintf(stderr, "Error in the command line\n");
  738. fprintf(stderr, " slide optical depth '-E %s'\n", optarg);
  739. exit(EXIT_FAILURE);
  740. }
  741. break;
  742. case 'f':
  743. cl_default_fr = my_strtod(optarg);
  744. if (cl_default_fr < 0.0 || cl_default_fr > 1.0) {
  745. fprintf(stderr, "Error in the command-line argument: ");
  746. fprintf(stderr, "'-f %s' The argument must be between 0 and 1.\n", optarg);
  747. exit(EXIT_SUCCESS);
  748. }
  749. break;
  750. case 'F':
  751. if (isdigit(optarg[0])) {
  752. cl_default_mus = my_strtod(optarg);
  753. if (cl_default_mus < 0) {
  754. fprintf(stderr, "Error in the command line\n");
  755. fprintf(stderr, " mus '-F %s'\n", optarg);
  756. exit(EXIT_FAILURE);
  757. }
  758. break;
  759. }
  760. n = sscanf(optarg, "%c %lf %lf %lf", &cc, &cl_mus0_lambda, &cl_mus0, &cl_mus0_pwr);
  761. if (n != 4 || (cc != 'P' && cc != 'p')) {
  762. fprintf(stderr, "Error in the command line\n");
  763. fprintf(stderr, " bad -F option. '-F %s'\n", optarg);
  764. fprintf(stderr, " -F 1.0 for mus =1.0\n");
  765. fprintf(stderr, " -F 'P 500 1.0 -1.3' for mus =1.0*(lambda/500)^(-1.3)\n");
  766. exit(EXIT_FAILURE);
  767. }
  768. break;
  769. case 'g':
  770. cl_default_g = my_strtod(optarg);
  771. if (cl_default_g < -1 || cl_default_g > 1) {
  772. fprintf(stderr, "Error in the command line\n");
  773. fprintf(stderr, " anisotropy '-g %s'\n", optarg);
  774. exit(EXIT_FAILURE);
  775. }
  776. if (cl_default_g == -1)
  777. cl_default_g = -MAX_ABS_G;
  778. if (cl_default_g == 1)
  779. cl_default_g = MAX_ABS_G;
  780. break;
  781. case 'G':
  782. if (optarg[0] == '0')
  783. cl_slides = NO_SLIDES;
  784. else if (optarg[0] == '2')
  785. cl_slides = TWO_IDENTICAL_SLIDES;
  786. else if (optarg[0] == 't' || optarg[0] == 'T')
  787. cl_slides = ONE_SLIDE_ON_TOP;
  788. else if (optarg[0] == 'b' || optarg[0] == 'B')
  789. cl_slides = ONE_SLIDE_ON_BOTTOM;
  790. else if (optarg[0] == 'n' || optarg[0] == 'N')
  791. cl_slides = ONE_SLIDE_NEAR_SPHERE;
  792. else if (optarg[0] == 'f' || optarg[0] == 'F')
  793. cl_slides = ONE_SLIDE_NOT_NEAR_SPHERE;
  794. else {
  795. fprintf(stderr, "Error in the command line\n");
  796. fprintf(stderr, " Argument for '-G %s' must be \n", optarg);
  797. fprintf(stderr, " 't' --- light always hits top slide first\n");
  798. fprintf(stderr, " 'b' --- light always hits bottom slide first\n");
  799. fprintf(stderr, " 'n' --- slide always closest to sphere\n");
  800. fprintf(stderr, " 'f' --- slide always farthest from sphere\n");
  801. exit(EXIT_FAILURE);
  802. }
  803. break;
  804. case 'H':
  805. if (optarg[0] == '0') {
  806. cl_baffle_r = 0;
  807. cl_baffle_t = 0;
  808. }
  809. else if (optarg[0] == '1') {
  810. cl_baffle_r = 1;
  811. cl_baffle_t = 0;
  812. }
  813. else if (optarg[0] == '2') {
  814. cl_baffle_r = 0;
  815. cl_baffle_t = 1;
  816. }
  817. else if (optarg[0] == '3') {
  818. cl_baffle_r = 1;
  819. cl_baffle_t = 1;
  820. }
  821. else {
  822. fprintf(stderr, "Error in the command-line -H argument\n");
  823. fprintf(stderr, " argument is '%s', but ", optarg);
  824. fprintf(stderr, "must be 0, 1, 2, or 3\n");
  825. exit(EXIT_FAILURE);
  826. }
  827. break;
  828. case 'i':
  829. cl_cos_angle = my_strtod(optarg);
  830. if (cl_cos_angle < 0 || cl_cos_angle > 90) {
  831. fprintf(stderr, "Error in the command line\n");
  832. fprintf(stderr, " incident angle '-i %s'\n", optarg);
  833. fprintf(stderr, " must be between 0 and 90 degrees\n");
  834. exit(EXIT_FAILURE);
  835. }
  836. cl_cos_angle = cos(cl_cos_angle * M_PI / 180.0);
  837. break;
  838. case 'j':
  839. cl_default_musp = my_strtod(optarg);
  840. if (cl_default_musp < 0) {
  841. fprintf(stderr, "Error in the command line\n");
  842. fprintf(stderr, " musp '-j %s'\n", optarg);
  843. exit(EXIT_FAILURE);
  844. }
  845. break;
  846. case 'J':
  847. cl_grid_calc = 1;
  848. break;
  849. case 'l':
  850. tmp_str = strdup(optarg);
  851. parse_string_into_array(optarg, cl_wave_limit, 2);
  852. break;
  853. case 'L':
  854. cl_lambda = my_strtod(optarg);
  855. break;
  856. case 'M':
  857. MAX_MC_iterations = (int) my_strtod(optarg);
  858. if (MAX_MC_iterations < 0 || MAX_MC_iterations > 50) {
  859. fprintf(stderr, "Error in the command line\n");
  860. fprintf(stderr, " MC iterations '-M %s'\n", optarg);
  861. exit(EXIT_FAILURE);
  862. }
  863. break;
  864. case 'n':
  865. cl_sample_n = my_strtod(optarg);
  866. if (cl_sample_n < 0.1 || cl_sample_n > 10) {
  867. fprintf(stderr, "Error in the command line\n");
  868. fprintf(stderr, " slab index '-n %s'\n", optarg);
  869. exit(EXIT_FAILURE);
  870. }
  871. break;
  872. case 'N':
  873. cl_slide_n = my_strtod(optarg);
  874. if (cl_slide_n < 0.1 || cl_slide_n > 10) {
  875. fprintf(stderr, "Error in the command line\n");
  876. fprintf(stderr, " slide index '-N %s'\n", optarg);
  877. exit(EXIT_FAILURE);
  878. }
  879. break;
  880. case 'o':
  881. g_out_name = strdup(optarg);
  882. break;
  883. case 'p':
  884. n_photons = (long) my_strtod(optarg);
  885. break;
  886. case 'q':
  887. cl_quadrature_points = (int) my_strtod(optarg);
  888. if (cl_quadrature_points % 4 != 0) {
  889. fprintf(stderr, "Error in the command line\n");
  890. fprintf(stderr, " '-q %s'\n", optarg);
  891. fprintf(stderr, " Quadrature points must be a multiple of 4\n");
  892. exit(EXIT_FAILURE);
  893. }
  894. if ((cl_cos_angle != UNINITIALIZED) && (cl_quadrature_points % 12 != 0)) {
  895. fprintf(stderr, "Error in the command line\n");
  896. fprintf(stderr, " '-q %s'\n", optarg);
  897. fprintf(stderr, " Quadrature points must be multiple of 12 for oblique incidence\n");
  898. exit(EXIT_FAILURE);
  899. }
  900. break;
  901. case 'r':
  902. cl_UR1 = my_strtod(optarg);
  903. process_command_line = 1;
  904. if (cl_UR1 < 0 || cl_UR1 > 1) {
  905. fprintf(stderr, "Error in the command line\n");
  906. fprintf(stderr, " UR1 value '-r %s'\n", optarg);
  907. fprintf(stderr, " must be between 0 and 1\n");
  908. exit(EXIT_FAILURE);
  909. }
  910. break;
  911. case 'R':
  912. cl_rstd_r = my_strtod(optarg);
  913. if (cl_rstd_r < 0 || cl_rstd_r > 1) {
  914. fprintf(stderr, "Error in the command line\n");
  915. fprintf(stderr, " Rstd value '-R %s'\n", optarg);
  916. fprintf(stderr, " must be between 0 and 1\n");
  917. exit(EXIT_FAILURE);
  918. }
  919. break;
  920. case 's':
  921. cl_search = (int) my_strtod(optarg);
  922. break;
  923. case 'S':
  924. cl_num_spheres = (int) my_strtod(optarg);
  925. if (cl_num_spheres != 0 && cl_num_spheres != 1 && cl_num_spheres != 2) {
  926. fprintf(stderr, "Error in the command line\n");
  927. fprintf(stderr, " sphere number '-S %s'\n", optarg);
  928. fprintf(stderr, " must be 0, 1, or 2\n");
  929. exit(EXIT_FAILURE);
  930. }
  931. break;
  932. case 't':
  933. cl_UT1 = my_strtod(optarg);
  934. if (cl_UT1 < 0 || cl_UT1 > 1) {
  935. fprintf(stderr, "Error in the command line\n");
  936. fprintf(stderr, " UT1 value '-t %s'\n", optarg);
  937. fprintf(stderr, " must be between 0 and 1\n");
  938. exit(EXIT_FAILURE);
  939. }
  940. process_command_line = 1;
  941. break;
  942. case 'T':
  943. cl_rstd_t = my_strtod(optarg);
  944. if (cl_rstd_t < 0 || cl_rstd_t > 1) {
  945. fprintf(stderr, "Error in the command line\n");
  946. fprintf(stderr, " transmission standard '-T %s'\n", optarg);
  947. fprintf(stderr, " must be between 0 and 1\n");
  948. exit(EXIT_FAILURE);
  949. }
  950. break;
  951. case 'u':
  952. cl_Tc = my_strtod(optarg);
  953. if (cl_Tc < 0 || cl_Tc > 1) {
  954. fprintf(stderr, "Error in the command line\n");
  955. fprintf(stderr, " unscattered transmission '-u %s'\n", optarg);
  956. fprintf(stderr, " must be between 0 and 1\n");
  957. exit(EXIT_FAILURE);
  958. }
  959. process_command_line = 1;
  960. break;
  961. case 'v':
  962. print_version(cl_verbosity);
  963. exit(EXIT_SUCCESS);
  964. break;
  965. case 'V':
  966. cl_verbosity = my_strtod(optarg);
  967. break;
  968. case 'w':
  969. cl_rwall_r = my_strtod(optarg);
  970. if (cl_rwall_r < 0 || cl_rwall_r > 1) {
  971. fprintf(stderr, "Error in the command line\n");
  972. fprintf(stderr, " refl sphere wall '-w %s'\n", optarg);
  973. fprintf(stderr, " must be between 0 and 1\n");
  974. exit(EXIT_FAILURE);
  975. }
  976. break;
  977. case 'W':
  978. cl_rwall_t = my_strtod(optarg);
  979. if (cl_rwall_t < 0 || cl_rwall_r > 1) {
  980. fprintf(stderr, "Error in the command line\n");
  981. fprintf(stderr, " trans sphere wall '-w %s'\n", optarg);
  982. fprintf(stderr, " must be between 0 and 1\n");
  983. exit(EXIT_FAILURE);
  984. }
  985. break;
  986. case 'x':
  987. Set_Debugging((int) my_strtod(optarg));
  988. break;
  989. case 'X':
  990. cl_method = COMPARISON;
  991. break;
  992. case 'Y':
  993. MC_Include_Diffuse_Loss(0);
  994. break;
  995. case 'z':
  996. cl_forward_calc = 1;
  997. process_command_line = 1;
  998. break;
  999. default:
  1000. fprintf(stderr, "unknown option '%c'\n", c);
  1001. case 'h':
  1002. print_usage();
  1003. exit(EXIT_SUCCESS);
  1004. }
  1005. }
  1006. argc -= optind;
  1007. argv += optind;
  1008. Initialize_Measure(&m);
  1009. if (cl_cos_angle != UNINITIALIZED) {
  1010. m.slab_cos_angle = cl_cos_angle;
  1011. if (cl_quadrature_points == UNINITIALIZED)
  1012. cl_quadrature_points = 12;
  1013. if (cl_quadrature_points != 12 * (cl_quadrature_points / 12)) {
  1014. fprintf(stderr, "If you use the -i option to specify an oblique incidence angle, then\n");
  1015. fprintf(stderr, "the number of quadrature points must be a multiple of 12\n");
  1016. exit(EXIT_SUCCESS);
  1017. }
  1018. }
  1019. if (cl_sample_n != UNINITIALIZED)
  1020. m.slab_index = cl_sample_n;
  1021. if (cl_slide_n != UNINITIALIZED) {
  1022. m.slab_bottom_slide_index = cl_slide_n;
  1023. m.slab_top_slide_index = cl_slide_n;
  1024. if (cl_slide_d == UNINITIALIZED && !Column_Label_Present('D')) {
  1025. if (m.slab_top_slide_thickness == 0)
  1026. m.slab_top_slide_thickness = 1.0;
  1027. if (m.slab_bottom_slide_thickness == 0)
  1028. m.slab_bottom_slide_thickness = 1.0;
  1029. }
  1030. }
  1031. if (cl_slide_OD != UNINITIALIZED) {
  1032. m.slab_bottom_slide_b = cl_slide_OD;
  1033. m.slab_top_slide_b = cl_slide_OD;
  1034. }
  1035. if (cl_sample_d != UNINITIALIZED)
  1036. m.slab_thickness = cl_sample_d;
  1037. if (cl_beam_d != UNINITIALIZED)
  1038. m.d_beam = cl_beam_d;
  1039. if (cl_slide_d != UNINITIALIZED) {
  1040. m.slab_bottom_slide_thickness = cl_slide_d;
  1041. m.slab_top_slide_thickness = cl_slide_d;
  1042. }
  1043. if (cl_slides == NO_SLIDES) {
  1044. m.slab_bottom_slide_index = 1.0;
  1045. m.slab_bottom_slide_thickness = 0.0;
  1046. m.slab_top_slide_index = 1.0;
  1047. m.slab_top_slide_thickness = 0.0;
  1048. }
  1049. if (cl_slides == ONE_SLIDE_ON_TOP || cl_slides == ONE_SLIDE_NEAR_SPHERE) {
  1050. m.slab_bottom_slide_index = 1.0;
  1051. m.slab_bottom_slide_thickness = 0.0;
  1052. }
  1053. if (cl_slides == ONE_SLIDE_ON_BOTTOM || cl_slides == ONE_SLIDE_NOT_NEAR_SPHERE) {
  1054. m.slab_top_slide_index = 1.0;
  1055. m.slab_top_slide_thickness = 0.0;
  1056. }
  1057. if (cl_slides == ONE_SLIDE_NEAR_SPHERE || cl_slides == ONE_SLIDE_NOT_NEAR_SPHERE)
  1058. m.flip_sample = 1;
  1059. else
  1060. m.flip_sample = 0;
  1061. if (cl_slides == NO_SLIDES) {
  1062. m.slab_top_slide_b = 0.0;
  1063. m.slab_bottom_slide_b = 0.0;
  1064. }
  1065. if (cl_slides == ONE_SLIDE_ON_TOP || cl_slides == ONE_SLIDE_NEAR_SPHERE)
  1066. m.slab_bottom_slide_b = 0.0;
  1067. if (cl_slides == ONE_SLIDE_ON_BOTTOM || cl_slides == ONE_SLIDE_NOT_NEAR_SPHERE)
  1068. m.slab_top_slide_b = 0.0;
  1069. if (cl_method != UNINITIALIZED)
  1070. m.method = (int) cl_method;
  1071. if (cl_rstd_r != UNINITIALIZED) {
  1072. m.rstd_r = cl_rstd_r;
  1073. m.rstd_t = cl_rstd_r;
  1074. }
  1075. if (cl_rstd_t != UNINITIALIZED) {
  1076. m.rstd_t = cl_rstd_t;
  1077. if (cl_rstd_r == UNINITIALIZED)
  1078. m.rstd_r = cl_rstd_t;
  1079. }
  1080. if (cl_rwall_r != UNINITIALIZED || cl_rwall_t != UNINITIALIZED) {
  1081. if (cl_sphere_one[0] != UNINITIALIZED || cl_sphere_two[0] != UNINITIALIZED) {
  1082. fprintf(stderr, "A wall reflectance cannot accompany a sphere description.\n");
  1083. fprintf(stderr, " -1 and -2 already carry one as their fifth value\n");
  1084. fprintf(stderr, " -w and -W are for overriding the wall in an .rxt file\n");
  1085. exit(EXIT_FAILURE);
  1086. }
  1087. }
  1088. if (cl_rwall_r != UNINITIALIZED)
  1089. m.rw_r = cl_rwall_r;
  1090. if (cl_rwall_t != UNINITIALIZED)
  1091. m.rw_t = cl_rwall_t;
  1092. if (cl_sphere_one[0] != UNINITIALIZED) {
  1093. double d_sample_r, d_third_r, d_detector_r;
  1094. m.d_sphere_r = cl_sphere_one[0];
  1095. d_sample_r = cl_sphere_one[1];
  1096. d_third_r = cl_sphere_one[2];
  1097. d_detector_r = cl_sphere_one[3];
  1098. m.rw_r = cl_sphere_one[4];
  1099. m.as_r = sqr(d_sample_r / m.d_sphere_r / 2);
  1100. m.at_r = sqr(d_third_r / m.d_sphere_r / 2);
  1101. m.ad_r = sqr(d_detector_r / m.d_sphere_r / 2);
  1102. m.aw_r = 1.0 - m.as_r - m.at_r - m.ad_r;
  1103. m.d_sphere_t = m.d_sphere_r;
  1104. m.as_t = m.as_r;
  1105. m.at_t = m.at_r;
  1106. m.ad_t = m.ad_r;
  1107. m.aw_t = m.aw_r;
  1108. m.rw_t = m.rw_r;
  1109. if (cl_num_spheres == UNINITIALIZED)
  1110. m.num_spheres = 1;
  1111. }
  1112. if (cl_sphere_two[0] != UNINITIALIZED) {
  1113. double d_sample_t, d_third_t, d_detector_t;
  1114. m.d_sphere_t = cl_sphere_two[0];
  1115. d_sample_t = cl_sphere_two[1];
  1116. d_third_t = cl_sphere_two[2];
  1117. d_detector_t = cl_sphere_two[3];
  1118. m.rw_t = cl_sphere_two[4];
  1119. m.as_t = sqr(d_sample_t / m.d_sphere_t / 2);
  1120. m.at_t = sqr(d_third_t / m.d_sphere_t / 2);
  1121. m.ad_t = sqr(d_detector_t / m.d_sphere_t / 2);
  1122. m.aw_t = 1.0 - m.as_t - m.at_t - m.ad_t;
  1123. if (cl_num_spheres == UNINITIALIZED)
  1124. m.num_spheres = 2;
  1125. }
  1126. if (cl_num_spheres != UNINITIALIZED) {
  1127. m.num_spheres = (int) cl_num_spheres;
  1128. if (m.num_spheres > 0 && m.method == UNKNOWN)
  1129. m.method = SUBSTITUTION;
  1130. }
  1131. if (cl_ru_fraction != UNINITIALIZED)
  1132. m.fraction_of_ru_in_mr = cl_ru_fraction;
  1133. if (cl_tu_fraction != UNINITIALIZED)
  1134. m.fraction_of_tu_in_mt = cl_tu_fraction;
  1135. if (cl_UR1 != UNINITIALIZED)
  1136. m.m_r = cl_UR1;
  1137. if (cl_UT1 != UNINITIALIZED)
  1138. m.m_t = cl_UT1;
  1139. if (cl_Tc != UNINITIALIZED)
  1140. m.m_u = cl_Tc;
  1141. if (cl_default_fr != UNINITIALIZED)
  1142. m.f_r = cl_default_fr;
  1143. if (cl_baffle_r != UNINITIALIZED)
  1144. m.baffle_r = cl_baffle_r;
  1145. if (cl_baffle_t != UNINITIALIZED)
  1146. m.baffle_t = cl_baffle_t;
  1147. if (cl_lambda != UNINITIALIZED)
  1148. m.lambda = cl_lambda;
  1149. Initialize_Result(m, &r, TRUE);
  1150. if (cl_forward_calc != UNINITIALIZED) {
  1151. if (cl_quadrature_points != UNINITIALIZED)
  1152. r.method.quad_pts = cl_quadrature_points;
  1153. else
  1154. r.method.quad_pts = 8;
  1155. if (cl_default_a != UNINITIALIZED)
  1156. r.default_a = cl_default_a;
  1157. if (cl_default_mua != UNINITIALIZED) {
  1158. r.default_mua = cl_default_mua;
  1159. if (cl_sample_d != UNINITIALIZED)
  1160. r.default_ba = cl_default_mua * cl_sample_d;
  1161. else
  1162. r.default_ba = cl_default_mua * m.slab_thickness;
  1163. }
  1164. if (cl_default_b != UNINITIALIZED)
  1165. r.default_b = cl_default_b;
  1166. if (cl_default_g != UNINITIALIZED)
  1167. r.default_g = cl_default_g;
  1168. if (cl_tolerance != UNINITIALIZED) {
  1169. r.tolerance = cl_tolerance;
  1170. r.MC_tolerance = cl_tolerance;
  1171. }
  1172. if (cl_mus0 != UNINITIALIZED) {
  1173. if (m.lambda != 0) {
  1174. cl_default_mus = cl_mus0 * pow(m.lambda / cl_mus0_lambda, cl_mus0_pwr);
  1175. }
  1176. else {
  1177. fprintf(stderr, "Seems like you want to constrain scattering to a power law.\n");
  1178. fprintf(stderr, "Unfortunately, there is no wavelength so this cannot be done.\n");
  1179. }
  1180. }
  1181. if (cl_default_mus != UNINITIALIZED) {
  1182. r.default_mus = cl_default_mus;
  1183. if (cl_sample_d != UNINITIALIZED)
  1184. r.default_bs = cl_default_mus * cl_sample_d;
  1185. else
  1186. r.default_bs = cl_default_mus * m.slab_thickness;
  1187. }
  1188. if (cl_default_musp != UNINITIALIZED) {
  1189. if (cl_default_g != UNINITIALIZED)
  1190. r.default_mus = cl_default_musp / (1.0 - cl_default_g);
  1191. else
  1192. r.default_mus = cl_default_musp;
  1193. if (cl_sample_d != UNINITIALIZED)
  1194. r.default_bs = r.default_mus * cl_sample_d;
  1195. else
  1196. r.default_bs = r.default_mus * m.slab_thickness;
  1197. }
  1198. if (cl_search != UNINITIALIZED)
  1199. r.search = cl_search;
  1200. double temp_mus = 1;
  1201. double temp_mua = 0;
  1202. r.g = 0;
  1203. if (cl_default_mus != UNINITIALIZED)
  1204. temp_mus = cl_default_mus;
  1205. if (cl_default_mua != UNINITIALIZED)
  1206. temp_mua = cl_default_mua;
  1207. if (cl_default_g != UNINITIALIZED)
  1208. r.g = cl_default_g;
  1209. if (cl_default_musp != UNINITIALIZED)
  1210. temp_mus = cl_default_musp / (1 - r.g);
  1211. if (cl_default_a != UNINITIALIZED) {
  1212. r.a = cl_default_a;
  1213. if (cl_default_b != UNINITIALIZED && cl_sample_d != UNINITIALIZED) {
  1214. temp_mus = cl_default_a * cl_default_b / cl_sample_d;
  1215. temp_mua = cl_default_b / cl_sample_d - temp_mus;
  1216. }
  1217. else {
  1218. if (cl_default_a == 0) {
  1219. temp_mus = 0;
  1220. temp_mua = 1;
  1221. }
  1222. else
  1223. temp_mua = temp_mus / cl_default_a - temp_mus;
  1224. }
  1225. }
  1226. else
  1227. r.a = temp_mus / (temp_mus + temp_mua);
  1228. if (cl_default_b != UNINITIALIZED) {
  1229. r.b = cl_default_b;
  1230. }
  1231. else {
  1232. if (cl_sample_d == UNINITIALIZED)
  1233. r.b = HUGE_VAL;
  1234. else
  1235. r.b = (temp_mus + temp_mua) * cl_sample_d;
  1236. }
  1237. r.slab.a = r.a;
  1238. r.slab.b = r.b;
  1239. r.slab.g = r.g;
  1240. {
  1241. double mu_s, mu_sp, mu_a, m_r, m_t;
  1242. double ur1, ut1, uru, utu, ru, tu;
  1243. double cos_critical, theta_inc, theta_crit;
  1244. double thickness, denom;
  1245. if (MAX_MC_iterations == 0 || m.num_spheres == 0) {
  1246. Calculate_MR_MT(m, r, MC_NONE, TRUE, &m_r, &m_t);
  1247. }
  1248. else {
  1249. Calculate_MR_MT(m, r, MC_REDO, TRUE, &m_r, &m_t);
  1250. }
  1251. Calculate_Mua_Musp(m, r, &mu_s, &mu_sp, &mu_a);
  1252. RT(r.method.quad_pts, &r.slab, &ur1, &ut1, &uru, &utu);
  1253. Sp_mu_RT_Flip(m.flip_sample,
  1254. r.slab.n_top_slide, r.slab.n_slab, r.slab.n_bottom_slide,
  1255. r.slab.b_top_slide, r.slab.b, r.slab.b_bottom_slide, r.slab.cos_angle, &ru, &tu);
  1256. thickness = (m.slab_thickness > 0) ? m.slab_thickness : 1.0;
  1257. cos_critical = Cos_Critical_Angle(r.slab.n_slab, 1.0);
  1258. theta_inc = acos(r.slab.cos_angle) * 180.0 / M_PI;
  1259. theta_crit = acos(cos_critical) * 180.0 / M_PI;
  1260. if (cl_verbosity > 0) {
  1261. printf("Intrinsic Properties\n");
  1262. printf(" albedo = %.3f\n", r.slab.a);
  1263. if (r.slab.b == HUGE_VAL)
  1264. printf(" optical thickness = inf\n");
  1265. else
  1266. printf(" optical thickness = %.3f\n", r.slab.b);
  1267. printf(" anisotropy = %.3f\n", r.slab.g);
  1268. printf(" thickness = %.3f mm\n", m.slab_thickness);
  1269. printf(" sample index = %.3f\n", r.slab.n_slab);
  1270. printf(" top slide index = %.3f\n", r.slab.n_top_slide);
  1271. printf(" bottom slide index = %.3f\n", r.slab.n_bottom_slide);
  1272. printf(" cos(theta incident) = %.3f\n", r.slab.cos_angle);
  1273. printf(" quadrature points = %d\n", r.method.quad_pts);
  1274. printf("\n");
  1275. printf("Derived quantities\n");
  1276. denom = (r.slab.b == HUGE_VAL) ? 1.0 : thickness;
  1277. printf(" mu_a = %.4f 1/mm\n",
  1278. (r.slab.b == HUGE_VAL) ? ((r.slab.a > 0) ? (1.0 - r.slab.a) / r.slab.a : 1.0)
  1279. : (1.0 - r.slab.a) * r.slab.b / denom);
  1280. printf(" mu_s = %.4f 1/mm\n",
  1281. (r.slab.b == HUGE_VAL) ? 1.0 : r.slab.a * r.slab.b / denom);
  1282. printf(" mu_s*(1-g) = %.4f 1/mm\n", (r.slab.b == HUGE_VAL) ? (1.0 - r.slab.g)
  1283. : (1.0 - r.slab.g) * r.slab.a * r.slab.b / denom);
  1284. printf(" theta incident = %.1f\xc2\xb0\n", theta_inc);
  1285. printf(" cos(theta critical) = %.4f\n", cos_critical);
  1286. printf(" theta critical = %.1f\xc2\xb0\n", theta_crit);
  1287. if (m.num_spheres > 0) {
  1288. printf("\n");
  1289. printf("\nSphere properties (%d sphere%s)\n", m.num_spheres, (m.num_spheres == 1) ? "" : "s");
  1290. {
  1291. const char *baffle_text = m.baffle_r ? "has a baffle" : "has no baffle";
  1292. printf(" Reflection sphere %s between sample and detector\n", baffle_text);
  1293. printf(" sphere diameter = %7.1f mm\n", m.d_sphere_r);
  1294. printf(" sample port diameter = %7.1f mm\n", 2 * m.d_sphere_r * sqrt(m.as_r));
  1295. printf(" entrance port diameter = %7.1f mm\n", 2 * m.d_sphere_r * sqrt(m.at_r));
  1296. printf(" detector port diameter = %7.1f mm\n", 2 * m.d_sphere_r * sqrt(m.ad_r));
  1297. printf(" detector reflectance = %7.1f %%\n", m.rd_r * 100);
  1298. printf(" wall reflectance = %7.1f %%\n", m.rw_r * 100);
  1299. printf(" calibration standard = %7.1f %%\n", m.rstd_r * 100);
  1300. }
  1301. if (m.num_spheres == 2) {
  1302. {
  1303. const char *baffle_text = m.baffle_t ? "has a baffle" : "has no baffle";
  1304. printf(" Transmission sphere %s between sample and detector\n", baffle_text);
  1305. printf(" sphere diameter = %7.1f mm\n", m.d_sphere_t);
  1306. printf(" sample port diameter = %7.1f mm\n",
  1307. 2 * m.d_sphere_t * sqrt(m.as_t));
  1308. printf(" third port diameter = %7.1f mm\n",
  1309. 2 * m.d_sphere_t * sqrt(m.at_t));
  1310. printf(" detector port diameter = %7.1f mm\n",
  1311. 2 * m.d_sphere_t * sqrt(m.ad_t));
  1312. printf(" detector reflectance = %7.1f %%\n", m.rd_t * 100);
  1313. printf(" wall reflectance = %7.1f %%\n", m.rw_t * 100);
  1314. printf(" calibration standard = %7.1f %%\n", m.rstd_t * 100);
  1315. }
  1316. }
  1317. }
  1318. printf("Calculated quantities\n");
  1319. printf(" R total = %.4f\n", ur1);
  1320. printf(" R scattered = %.4f\n", ur1 - ru);
  1321. printf(" R unscattered = %.4f\n", ru);
  1322. printf(" T total = %.4f\n", ut1);
  1323. printf(" T scattered = %.4f\n", ut1 - tu);
  1324. printf(" T unscattered = %.4f\n", tu);
  1325. if (m.num_spheres > 0) {
  1326. printf(" M_R (sphere) = %.4f\n", m_r);
  1327. printf(" M_T (sphere) = %.4f\n", m_t);
  1328. }
  1329. }
  1330. (void) mu_s;
  1331. (void) mu_sp;
  1332. (void) mu_a;
  1333. }
  1334. exit(EXIT_SUCCESS);
  1335. }
  1336. {
  1337. int i;
  1338. for (i = 0; i < argc - 1; i++) {
  1339. if (strcmp(argv[i], "-o") == 0 && g_out_name == NULL) {
  1340. g_out_name = strdup(argv[i + 1]);
  1341. memmove(argv + i, argv + i + 2, (argc - i - 2) * sizeof(char *));
  1342. argc -= 2;
  1343. i--;
  1344. }
  1345. }
  1346. }
  1347. if (argc > 1) {
  1348. int file_arg_index;
  1349. int write_index = 0;
  1350. for (file_arg_index = 0; file_arg_index < argc; file_arg_index++) {
  1351. if (has_rxt_extension(argv[file_arg_index]))
  1352. argv[write_index++] = argv[file_arg_index];
  1353. }
  1354. argc = write_index;
  1355. if (argc == 0)
  1356. exit(EXIT_SUCCESS);
  1357. if (argc > 1 && g_out_name != NULL) {
  1358. fprintf(stderr, "The -o option cannot be used with multiple input files\n");
  1359. exit(EXIT_FAILURE);
  1360. }
  1361. }
  1362. if (argc == 1 && strcmp(argv[0], "-") != 0) {
  1363. int n;
  1364. char *base_name, *rt_name;
  1365. base_name = strdup(argv[0]);
  1366. n = (int) (strlen(base_name) - strlen(".rxt"));
  1367. if (n > 0 && strstr(base_name + n, ".rxt") != NULL)
  1368. base_name[n] = '\0';
  1369. rt_name = strdup_together(base_name, ".rxt");
  1370. if (freopen(argv[0], "r", stdin) == NULL && freopen(rt_name, "r", stdin) == NULL) {
  1371. fprintf(stderr, "Could not open either '%s' or '%s'\n", argv[0], rt_name);
  1372. exit(EXIT_FAILURE);
  1373. }
  1374. if (g_out_name == NULL)
  1375. g_out_name = strdup_together(base_name, ".txt");
  1376. if (g_grid_name == NULL)
  1377. g_grid_name = strdup_together(base_name, ".grid");
  1378. free(rt_name);
  1379. free(base_name);
  1380. process_command_line = 0;
  1381. }
  1382. if (g_out_name != NULL) {
  1383. if (freopen(g_out_name, "w", stdout) == NULL) {
  1384. fprintf(stderr, "Could not open file '%s' for output\n", g_out_name);
  1385. exit(EXIT_FAILURE);
  1386. }
  1387. }
  1388. if (process_command_line) {
  1389. if (cl_num_spheres >= 1 && cl_sphere_one[0] == UNINITIALIZED) {
  1390. fprintf(stderr, "Sphere measurements need the sphere described.\n");
  1391. fprintf(stderr, " -S %d was given without -1\n", cl_num_spheres);
  1392. fprintf(stderr, " -1 'd_sphere d_sample d_entrance d_detector r_wall'\n");
  1393. exit(EXIT_FAILURE);
  1394. }
  1395. if (cl_num_spheres == 2 && cl_sphere_two[0] == UNINITIALIZED) {
  1396. fprintf(stderr, "Two spheres need both spheres described.\n");
  1397. fprintf(stderr, " -S 2 was given without -2\n");
  1398. fprintf(stderr, " -2 'd_sphere d_sample d_third d_detector r_wall'\n");
  1399. exit(EXIT_FAILURE);
  1400. }
  1401. m.num_measures = 3;
  1402. if (m.m_r == 0)
  1403. m.num_measures--;
  1404. if (m.m_t == 0)
  1405. m.num_measures--;
  1406. if (m.m_u == 0)
  1407. m.num_measures--;
  1408. params = m.num_measures;
  1409. {
  1410. double ur1 = 0;
  1411. double ut1 = 0;
  1412. double uru = 0;
  1413. double utu = 0;
  1414. double mu_a = 0;
  1415. double mu_sp = 0;
  1416. double LR = 0;
  1417. double LT = 0;
  1418. int skip = FALSE;
  1419. if (cl_wave_limit[0] != UNINITIALIZED) {
  1420. if (m.lambda != 0) {
  1421. if (m.lambda < cl_wave_limit[0])
  1422. skip = TRUE;
  1423. if (m.lambda > cl_wave_limit[1])
  1424. skip = TRUE;
  1425. }
  1426. }
  1427. if (Debug(DEBUG_ANY) && !skip) {
  1428. fprintf(stderr, "\n-------------------NEXT DATA POINT---------------------\n");
  1429. if (m.lambda != 0)
  1430. fprintf(stderr, "lambda=%6.1f ", m.lambda);
  1431. fprintf(stderr, "MR=%8.5f MT=%8.5f\n\n", m.m_r, m.m_t);
  1432. if (skip)
  1433. fprintf(stderr, "skipping, wavelength out of range.\n");
  1434. }
  1435. if (!skip) {
  1436. rt_total++;
  1437. Initialize_Result(m, &r, FALSE);
  1438. if (cl_quadrature_points != UNINITIALIZED)
  1439. r.method.quad_pts = cl_quadrature_points;
  1440. else
  1441. r.method.quad_pts = 8;
  1442. if (cl_default_a != UNINITIALIZED)
  1443. r.default_a = cl_default_a;
  1444. if (cl_default_mua != UNINITIALIZED) {
  1445. r.default_mua = cl_default_mua;
  1446. if (cl_sample_d != UNINITIALIZED)
  1447. r.default_ba = cl_default_mua * cl_sample_d;
  1448. else
  1449. r.default_ba = cl_default_mua * m.slab_thickness;
  1450. }
  1451. if (cl_default_b != UNINITIALIZED)
  1452. r.default_b = cl_default_b;
  1453. if (cl_default_g != UNINITIALIZED)
  1454. r.default_g = cl_default_g;
  1455. if (cl_tolerance != UNINITIALIZED) {
  1456. r.tolerance = cl_tolerance;
  1457. r.MC_tolerance = cl_tolerance;
  1458. }
  1459. if (cl_mus0 != UNINITIALIZED) {
  1460. if (m.lambda != 0) {
  1461. cl_default_mus = cl_mus0 * pow(m.lambda / cl_mus0_lambda, cl_mus0_pwr);
  1462. }
  1463. else {
  1464. fprintf(stderr, "Seems like you want to constrain scattering to a power law.\n");
  1465. fprintf(stderr, "Unfortunately, there is no wavelength so this cannot be done.\n");
  1466. }
  1467. }
  1468. if (cl_default_mus != UNINITIALIZED) {
  1469. r.default_mus = cl_default_mus;
  1470. if (cl_sample_d != UNINITIALIZED)
  1471. r.default_bs = cl_default_mus * cl_sample_d;
  1472. else
  1473. r.default_bs = cl_default_mus * m.slab_thickness;
  1474. }
  1475. if (cl_default_musp != UNINITIALIZED) {
  1476. if (cl_default_g != UNINITIALIZED)
  1477. r.default_mus = cl_default_musp / (1.0 - cl_default_g);
  1478. else
  1479. r.default_mus = cl_default_musp;
  1480. if (cl_sample_d != UNINITIALIZED)
  1481. r.default_bs = r.default_mus * cl_sample_d;
  1482. else
  1483. r.default_bs = r.default_mus * m.slab_thickness;
  1484. }
  1485. if (cl_search != UNINITIALIZED)
  1486. r.search = cl_search;
  1487. if (cl_method == COMPARISON && m.d_sphere_r != 0 && m.as_r == 0) {
  1488. fprintf(stderr, "A dual-beam measurement is specified, but no port sizes.\n");
  1489. fprintf(stderr, "You might forsake the -X option and use zero spheres (which gives\n");
  1490. fprintf(stderr, "the same result except lost light is not taken into account).\n");
  1491. fprintf(stderr, "Alternatively, bite the bullet and enter your sphere parameters,\n");
  1492. fprintf(stderr, "with the knowledge that only the beam diameter and sample port\n");
  1493. fprintf(stderr, "diameter will be used to estimate lost light from the edges.\n");
  1494. exit(EXIT_SUCCESS);
  1495. }
  1496. if (cl_method == COMPARISON && m.num_spheres == 2) {
  1497. fprintf(stderr, "A dual-beam measurement is specified, but a two sphere experiment\n");
  1498. fprintf(stderr, "is specified. Since this seems impossible, I will make it\n");
  1499. fprintf(stderr, "impossible for you unless you specify 0 or 1 sphere.\n");
  1500. exit(EXIT_SUCCESS);
  1501. }
  1502. if (cl_method == COMPARISON && m.f_r != 0) {
  1503. fprintf(stderr, "A dual-beam measurement is specified, but a fraction of light\n");
  1504. fprintf(stderr, "is specified to hit the sphere wall first. This situation\n");
  1505. fprintf(stderr, "is not supported by iad. Sorry.\n");
  1506. exit(EXIT_SUCCESS);
  1507. }
  1508. if (rt_total == 1 && cl_verbosity > 0) {
  1509. Write_Header(m, r, params, command_line);
  1510. if (MAX_MC_iterations > 0) {
  1511. if (n_photons >= 0)
  1512. fprintf(stdout, "# Photons used to estimate lost light = %ld\n", n_photons);
  1513. else
  1514. fprintf(stdout, "# Time used to estimate lost light = %ld ms\n", -n_photons);
  1515. }
  1516. else
  1517. fprintf(stdout, "# Photons used to estimate lost light = 0\n");
  1518. fprintf(stdout, "#\n");
  1519. print_results_header(stdout);
  1520. }
  1521. m.lost_r.direct = 0;
  1522. m.lost_r.diffuse = 0;
  1523. m.lost_t.diffuse = 0;
  1524. m.lost_t.direct = 0;
  1525. m.utu_lost = 0;
  1526. Inverse_RT(m, &r);
  1527. calculate_coefficients(m, r, &LR, &LT, &mu_sp, &mu_a);
  1528. if (m.num_spheres > 0 && r.found && r.error == IAD_NO_ERROR) {
  1529. if (Debug(DEBUG_LOST_LIGHT)) {
  1530. print_results_header(stderr);
  1531. print_optical_property_result(stderr, m, r, LR, LT, mu_a, mu_sp, rt_total);
  1532. }
  1533. {
  1534. int mc_failed = 0;
  1535. int mc_unreachable = 0;
  1536. int pinned = 0;
  1537. struct measure_type good_m = m;
  1538. struct invert_type good_r = r;
  1539. double mc_prev_a = r.slab.a;
  1540. double mc_prev_b = r.slab.b;
  1541. double mc_prev_g = r.slab.g;
  1542. {
  1543. double b0 = (r.slab.b < 1e8) ? r.slab.b : 1.0;
  1544. r.mc_simplex_a_step = 1e-3;
  1545. r.mc_simplex_b_step = 1e-2 * (b0 > 1.0 ? b0 : 1.0);
  1546. r.mc_simplex_g_step = 1e-3;
  1547. }
  1548. int has_prev_diff_ur1_lost = 0;
  1549. double prev_abs_diff_ur1_lost = 0.0;
  1550. while (r.MC_iterations < MAX_MC_iterations) {
  1551. long n_photons_this;
  1552. double last_mu_sp, last_mu_a;
  1553. struct lost_type current_r, current_t;
  1554. double current_utu_lost;
  1555. double diff_ur1_lost, diff_ut1_lost, diff_uru_lost, diff_utu_lost;
  1556. double diff_uru_lost_t;
  1557. double factor = 0.3;
  1558. int too_much_lost;
  1559. double tol = r.MC_tolerance;
  1560. calculate_coefficients(m, r, &LR, &LT, &mu_sp, &mu_a);
  1561. last_mu_sp = mu_sp;
  1562. last_mu_a = mu_a;
  1563. if (Debug(DEBUG_ITERATIONS) || Debug(DEBUG_A_LITTLE)) {
  1564. fprintf(stderr, "\n------------- Monte Carlo Iteration %d -----------------\n",
  1565. r.MC_iterations + 1);
  1566. }
  1567. if (n_photons < 0)
  1568. n_photons_this = n_photons;
  1569. else if (!has_prev_diff_ur1_lost || prev_abs_diff_ur1_lost > 0.01)
  1570. n_photons_this = (n_photons / 10 > 10000) ? n_photons / 10 : 10000;
  1571. else if (prev_abs_diff_ur1_lost > 0.001)
  1572. n_photons_this = n_photons;
  1573. else
  1574. n_photons_this = (n_photons * 5 < 10000000) ? n_photons * 5 : 10000000;
  1575. MC_Lost(m, r, n_photons_this, &ur1, &ut1, &uru, &utu,
  1576. &current_r, &current_t, &current_utu_lost);
  1577. diff_ur1_lost = current_r.direct - m.lost_r.direct;
  1578. diff_uru_lost = current_r.diffuse - m.lost_r.diffuse;
  1579. diff_uru_lost_t = current_t.diffuse - m.lost_t.diffuse;
  1580. diff_ut1_lost = current_t.direct - m.lost_t.direct;
  1581. diff_utu_lost = current_utu_lost - m.utu_lost;
  1582. prev_abs_diff_ur1_lost = fabs(diff_ur1_lost);
  1583. has_prev_diff_ur1_lost = 1;
  1584. if (fabs(diff_ur1_lost) > 0.001 || fabs(diff_ut1_lost) > 0.001)
  1585. too_much_lost = 1;
  1586. else
  1587. too_much_lost = 0;
  1588. m.lost_r.direct += factor * diff_ur1_lost;
  1589. m.lost_r.diffuse += factor * diff_uru_lost;
  1590. m.lost_t.diffuse += factor * diff_uru_lost_t;
  1591. m.lost_t.direct += factor * diff_ut1_lost;
  1592. m.utu_lost += factor * diff_utu_lost;
  1593. mc_total++;
  1594. r.MC_iterations++;
  1595. Inverse_RT(m, &r);
  1596. {
  1597. double new_a = r.slab.a, new_b = r.slab.b, new_g = r.slab.g;
  1598. double da = fabs(new_a - mc_prev_a);
  1599. double db = fabs(new_b - mc_prev_b);
  1600. double dg = fabs(new_g - mc_prev_g);
  1601. double b_safe = (new_b < 1e8) ? new_b : 1.0;
  1602. double a_fixed = 1e-3;
  1603. double b_fixed = 1e-2 * (b_safe > 1.0 ? b_safe : 1.0);
  1604. double g_fixed = 1e-3;
  1605. r.mc_simplex_a_step = (da < 1e-5) ? 1e-5 : (da > a_fixed ? a_fixed : da);
  1606. r.mc_simplex_b_step = (db < 1e-4) ? 1e-4 : (db > b_fixed ? b_fixed : db);
  1607. r.mc_simplex_g_step = (dg < 1e-5) ? 1e-5 : (dg > g_fixed ? g_fixed : dg);
  1608. mc_prev_a = new_a;
  1609. mc_prev_b = new_b;
  1610. mc_prev_g = new_g;
  1611. }
  1612. calculate_coefficients(m, r, &LR, &LT, &mu_sp, &mu_a);
  1613. if (r.found && r.error == IAD_NO_ERROR) {
  1614. good_m = m;
  1615. good_r = r;
  1616. }
  1617. if (0) {
  1618. fprintf(stderr, "%2d %2d %2d | %7.4f %7.4f %7.4f | %7.4f %7.4f %7.4f\n",
  1619. r.MC_iterations, too_much_lost, r.found,
  1620. m.m_r, current_r.direct, m.lost_r.direct, m.m_t, current_t.direct, m.lost_t.direct);
  1621. }
  1622. if (Debug(DEBUG_LOST_LIGHT))
  1623. print_optical_property_result(stderr, m, r, LR, LT, mu_a, mu_sp, rt_total);
  1624. else
  1625. print_dot(start_time, r.error, mc_total, FALSE, cl_verbosity);
  1626. if (mu_a <= 0.0 && too_much_lost)
  1627. pinned++;
  1628. else
  1629. pinned = 0;
  1630. if (pinned >= 2) {
  1631. {
  1632. double b_thin = 1e-6;
  1633. double b_t, mr_at, mt_at, mr_thin, mt_thin, mr_dn, mt_dn, mr_up, mt_up;
  1634. int unreachable = 0;
  1635. bright_mr_mt(m, r, b_thin, &mr_thin, &mt_thin);
  1636. if (m.m_t > mt_thin) {
  1637. {
  1638. unreachable = 1;
  1639. b_t = b_thin;
  1640. mr_at = mr_thin;
  1641. mt_at = mt_thin;
  1642. }
  1643. }
  1644. else {
  1645. b_t = solve_for_b_from_mt(m, r, m.m_t);
  1646. bright_mr_mt(m, r, b_t, &mr_at, &mt_at);
  1647. bright_mr_mt(m, r, b_t * 0.9, &mr_dn, &mt_dn);
  1648. bright_mr_mt(m, r, b_t * 1.1, &mr_up, &mt_up);
  1649. if (m.m_r > mr_at && mr_up > mr_at && mr_at > mr_dn)
  1650. unreachable = 1;
  1651. }
  1652. if (unreachable) {
  1653. r.error = IAD_UNREACHABLE_WITH_LOST_LIGHT;
  1654. if (Debug(DEBUG_LOST_LIGHT) || Debug(DEBUG_A_LITTLE) || Debug(DEBUG_ITERATIONS)) {
  1655. fprintf(stderr, "The lost light puts the measurements out of reach.\n");
  1656. fprintf(stderr, " with no absorption at b = %.4f the model gives\n",
  1657. b_t);
  1658. fprintf(stderr, " M_R %8.5f measured %8.5f short by %8.5f\n", mr_at,
  1659. m.m_r, m.m_r - mr_at);
  1660. fprintf(stderr, " M_T %8.5f measured %8.5f\n", mt_at, m.m_t);
  1661. }
  1662. }
  1663. }
  1664. if (r.error == IAD_UNREACHABLE_WITH_LOST_LIGHT) {
  1665. mc_unreachable = 1;
  1666. if (Debug(DEBUG_ITERATIONS))
  1667. fprintf(stderr,
  1668. "absorption is spent and the data is out of reach — stopping\n");
  1669. break;
  1670. }
  1671. }
  1672. if (r.found) {
  1673. if (fabs(last_mu_a - mu_a) > tol) {
  1674. if (Debug(DEBUG_ITERATIONS))
  1675. fprintf(stderr, "Repeat MC because mua is still changing\n");
  1676. continue;
  1677. }
  1678. if (fabs(last_mu_sp - mu_sp) > tol) {
  1679. if (Debug(DEBUG_ITERATIONS))
  1680. fprintf(stderr, "Repeat MC because musp is still changing\n");
  1681. continue;
  1682. }
  1683. if (too_much_lost) {
  1684. if (Debug(DEBUG_ITERATIONS))
  1685. fprintf(stderr, "Repeat MC because mua and musp are still changing\n");
  1686. continue;
  1687. }
  1688. if (Debug(DEBUG_ITERATIONS))
  1689. fprintf(stderr, "found!\n");
  1690. break;
  1691. }
  1692. else {
  1693. if (Debug(DEBUG_ITERATIONS))
  1694. fprintf(stderr, "AD did not converge — stopping MC loop\n");
  1695. mc_failed = 1;
  1696. break;
  1697. }
  1698. }
  1699. if (mc_unreachable) {
  1700. r.found = 0;
  1701. r.error = IAD_UNREACHABLE_WITH_LOST_LIGHT;
  1702. {
  1703. int why = r.error;
  1704. struct lost_type lost_r = m.lost_r;
  1705. struct lost_type lost_t = m.lost_t;
  1706. double utu_lost = m.utu_lost;
  1707. m = good_m;
  1708. m.lost_r = lost_r;
  1709. m.lost_t = lost_t;
  1710. m.utu_lost = utu_lost;
  1711. r = good_r;
  1712. r.error = why;
  1713. r.found = 0;
  1714. }
  1715. }
  1716. else if (mc_failed) {
  1717. r.found = 0;
  1718. {
  1719. double b_thin = 1e-6;
  1720. double b_t, mr_at, mt_at, mr_thin, mt_thin, mr_dn, mt_dn, mr_up, mt_up;
  1721. int unreachable = 0;
  1722. bright_mr_mt(m, r, b_thin, &mr_thin, &mt_thin);
  1723. if (m.m_t > mt_thin) {
  1724. {
  1725. unreachable = 1;
  1726. b_t = b_thin;
  1727. mr_at = mr_thin;
  1728. mt_at = mt_thin;
  1729. }
  1730. }
  1731. else {
  1732. b_t = solve_for_b_from_mt(m, r, m.m_t);
  1733. bright_mr_mt(m, r, b_t, &mr_at, &mt_at);
  1734. bright_mr_mt(m, r, b_t * 0.9, &mr_dn, &mt_dn);
  1735. bright_mr_mt(m, r, b_t * 1.1, &mr_up, &mt_up);
  1736. if (m.m_r > mr_at && mr_up > mr_at && mr_at > mr_dn)
  1737. unreachable = 1;
  1738. }
  1739. if (unreachable) {
  1740. r.error = IAD_UNREACHABLE_WITH_LOST_LIGHT;
  1741. if (Debug(DEBUG_LOST_LIGHT) || Debug(DEBUG_A_LITTLE) || Debug(DEBUG_ITERATIONS)) {
  1742. fprintf(stderr, "The lost light puts the measurements out of reach.\n");
  1743. fprintf(stderr, " with no absorption at b = %.4f the model gives\n", b_t);
  1744. fprintf(stderr, " M_R %8.5f measured %8.5f short by %8.5f\n",
  1745. mr_at, m.m_r, m.m_r - mr_at);
  1746. fprintf(stderr, " M_T %8.5f measured %8.5f\n", mt_at, m.m_t);
  1747. }
  1748. }
  1749. }
  1750. if (r.error == IAD_NO_ERROR)
  1751. r.error = IAD_MC_DID_NOT_CONVERGE;
  1752. {
  1753. int why = r.error;
  1754. struct lost_type lost_r = m.lost_r;
  1755. struct lost_type lost_t = m.lost_t;
  1756. double utu_lost = m.utu_lost;
  1757. m = good_m;
  1758. m.lost_r = lost_r;
  1759. m.lost_t = lost_t;
  1760. m.utu_lost = utu_lost;
  1761. r = good_r;
  1762. r.error = why;
  1763. r.found = 0;
  1764. }
  1765. }
  1766. }
  1767. }
  1768. if (!r.found && r.error == IAD_NO_ERROR)
  1769. r.error = IAD_SEARCH_STALLED;
  1770. calculate_coefficients(m, r, &LR, &LT, &mu_sp, &mu_a);
  1771. print_optical_property_result(stdout, m, r, LR, LT, mu_a, mu_sp, rt_total);
  1772. if (r.error != IAD_NO_ERROR) {
  1773. any_error = 1;
  1774. last_error = r.error;
  1775. }
  1776. if (Debug(DEBUG_ANY))
  1777. print_long_error(r.error);
  1778. else
  1779. print_dot(start_time, r.error, mc_total, TRUE, cl_verbosity);
  1780. }
  1781. }
  1782. if (cl_verbosity > 0)
  1783. fprintf(stderr, "\n\n");
  1784. if (any_error && cl_verbosity > 1)
  1785. print_long_error(last_error);
  1786. exit(EXIT_SUCCESS);
  1787. }
  1788. file_count = argc > 1 ? argc : 1;
  1789. file_index = 0;
  1790. read_next_file:
  1791. if (argc > 1) {
  1792. {
  1793. int n;
  1794. char *base_name, *rt_name, *out_name;
  1795. base_name = strdup(argv[file_index]);
  1796. n = (int) (strlen(base_name) - strlen(".rxt"));
  1797. base_name[n] = '\0';
  1798. rt_name = strdup_together(base_name, ".rxt");
  1799. out_name = strdup_together(base_name, ".txt");
  1800. if (freopen(rt_name, "r", stdin) == NULL) {
  1801. fprintf(stderr, "Could not open file '%s'\n", rt_name);
  1802. exit(EXIT_FAILURE);
  1803. }
  1804. if (freopen(out_name, "w", stdout) == NULL) {
  1805. fprintf(stderr, "Could not open file '%s' for output\n", out_name);
  1806. exit(EXIT_FAILURE);
  1807. }
  1808. free(out_name);
  1809. free(rt_name);
  1810. free(base_name);
  1811. }
  1812. }
  1813. Initialize_Measure(&m);
  1814. if (cl_cos_angle != UNINITIALIZED) {
  1815. m.slab_cos_angle = cl_cos_angle;
  1816. if (cl_quadrature_points == UNINITIALIZED)
  1817. cl_quadrature_points = 12;
  1818. if (cl_quadrature_points != 12 * (cl_quadrature_points / 12)) {
  1819. fprintf(stderr, "If you use the -i option to specify an oblique incidence angle, then\n");
  1820. fprintf(stderr, "the number of quadrature points must be a multiple of 12\n");
  1821. exit(EXIT_SUCCESS);
  1822. }
  1823. }
  1824. if (cl_sample_n != UNINITIALIZED)
  1825. m.slab_index = cl_sample_n;
  1826. if (cl_slide_n != UNINITIALIZED) {
  1827. m.slab_bottom_slide_index = cl_slide_n;
  1828. m.slab_top_slide_index = cl_slide_n;
  1829. if (cl_slide_d == UNINITIALIZED && !Column_Label_Present('D')) {
  1830. if (m.slab_top_slide_thickness == 0)
  1831. m.slab_top_slide_thickness = 1.0;
  1832. if (m.slab_bottom_slide_thickness == 0)
  1833. m.slab_bottom_slide_thickness = 1.0;
  1834. }
  1835. }
  1836. if (cl_slide_OD != UNINITIALIZED) {
  1837. m.slab_bottom_slide_b = cl_slide_OD;
  1838. m.slab_top_slide_b = cl_slide_OD;
  1839. }
  1840. if (cl_sample_d != UNINITIALIZED)
  1841. m.slab_thickness = cl_sample_d;
  1842. if (cl_beam_d != UNINITIALIZED)
  1843. m.d_beam = cl_beam_d;
  1844. if (cl_slide_d != UNINITIALIZED) {
  1845. m.slab_bottom_slide_thickness = cl_slide_d;
  1846. m.slab_top_slide_thickness = cl_slide_d;
  1847. }
  1848. if (cl_slides == NO_SLIDES) {
  1849. m.slab_bottom_slide_index = 1.0;
  1850. m.slab_bottom_slide_thickness = 0.0;
  1851. m.slab_top_slide_index = 1.0;
  1852. m.slab_top_slide_thickness = 0.0;
  1853. }
  1854. if (cl_slides == ONE_SLIDE_ON_TOP || cl_slides == ONE_SLIDE_NEAR_SPHERE) {
  1855. m.slab_bottom_slide_index = 1.0;
  1856. m.slab_bottom_slide_thickness = 0.0;
  1857. }
  1858. if (cl_slides == ONE_SLIDE_ON_BOTTOM || cl_slides == ONE_SLIDE_NOT_NEAR_SPHERE) {
  1859. m.slab_top_slide_index = 1.0;
  1860. m.slab_top_slide_thickness = 0.0;
  1861. }
  1862. if (cl_slides == ONE_SLIDE_NEAR_SPHERE || cl_slides == ONE_SLIDE_NOT_NEAR_SPHERE)
  1863. m.flip_sample = 1;
  1864. else
  1865. m.flip_sample = 0;
  1866. if (cl_slides == NO_SLIDES) {
  1867. m.slab_top_slide_b = 0.0;
  1868. m.slab_bottom_slide_b = 0.0;
  1869. }
  1870. if (cl_slides == ONE_SLIDE_ON_TOP || cl_slides == ONE_SLIDE_NEAR_SPHERE)
  1871. m.slab_bottom_slide_b = 0.0;
  1872. if (cl_slides == ONE_SLIDE_ON_BOTTOM || cl_slides == ONE_SLIDE_NOT_NEAR_SPHERE)
  1873. m.slab_top_slide_b = 0.0;
  1874. if (cl_method != UNINITIALIZED)
  1875. m.method = (int) cl_method;
  1876. if (cl_rstd_r != UNINITIALIZED) {
  1877. m.rstd_r = cl_rstd_r;
  1878. m.rstd_t = cl_rstd_r;
  1879. }
  1880. if (cl_rstd_t != UNINITIALIZED) {
  1881. m.rstd_t = cl_rstd_t;
  1882. if (cl_rstd_r == UNINITIALIZED)
  1883. m.rstd_r = cl_rstd_t;
  1884. }
  1885. if (cl_rwall_r != UNINITIALIZED || cl_rwall_t != UNINITIALIZED) {
  1886. if (cl_sphere_one[0] != UNINITIALIZED || cl_sphere_two[0] != UNINITIALIZED) {
  1887. fprintf(stderr, "A wall reflectance cannot accompany a sphere description.\n");
  1888. fprintf(stderr, " -1 and -2 already carry one as their fifth value\n");
  1889. fprintf(stderr, " -w and -W are for overriding the wall in an .rxt file\n");
  1890. exit(EXIT_FAILURE);
  1891. }
  1892. }
  1893. if (cl_rwall_r != UNINITIALIZED)
  1894. m.rw_r = cl_rwall_r;
  1895. if (cl_rwall_t != UNINITIALIZED)
  1896. m.rw_t = cl_rwall_t;
  1897. if (cl_sphere_one[0] != UNINITIALIZED) {
  1898. double d_sample_r, d_third_r, d_detector_r;
  1899. m.d_sphere_r = cl_sphere_one[0];
  1900. d_sample_r = cl_sphere_one[1];
  1901. d_third_r = cl_sphere_one[2];
  1902. d_detector_r = cl_sphere_one[3];
  1903. m.rw_r = cl_sphere_one[4];
  1904. m.as_r = sqr(d_sample_r / m.d_sphere_r / 2);
  1905. m.at_r = sqr(d_third_r / m.d_sphere_r / 2);
  1906. m.ad_r = sqr(d_detector_r / m.d_sphere_r / 2);
  1907. m.aw_r = 1.0 - m.as_r - m.at_r - m.ad_r;
  1908. m.d_sphere_t = m.d_sphere_r;
  1909. m.as_t = m.as_r;
  1910. m.at_t = m.at_r;
  1911. m.ad_t = m.ad_r;
  1912. m.aw_t = m.aw_r;
  1913. m.rw_t = m.rw_r;
  1914. if (cl_num_spheres == UNINITIALIZED)
  1915. m.num_spheres = 1;
  1916. }
  1917. if (cl_sphere_two[0] != UNINITIALIZED) {
  1918. double d_sample_t, d_third_t, d_detector_t;
  1919. m.d_sphere_t = cl_sphere_two[0];
  1920. d_sample_t = cl_sphere_two[1];
  1921. d_third_t = cl_sphere_two[2];
  1922. d_detector_t = cl_sphere_two[3];
  1923. m.rw_t = cl_sphere_two[4];
  1924. m.as_t = sqr(d_sample_t / m.d_sphere_t / 2);
  1925. m.at_t = sqr(d_third_t / m.d_sphere_t / 2);
  1926. m.ad_t = sqr(d_detector_t / m.d_sphere_t / 2);
  1927. m.aw_t = 1.0 - m.as_t - m.at_t - m.ad_t;
  1928. if (cl_num_spheres == UNINITIALIZED)
  1929. m.num_spheres = 2;
  1930. }
  1931. if (cl_num_spheres != UNINITIALIZED) {
  1932. m.num_spheres = (int) cl_num_spheres;
  1933. if (m.num_spheres > 0 && m.method == UNKNOWN)
  1934. m.method = SUBSTITUTION;
  1935. }
  1936. if (cl_ru_fraction != UNINITIALIZED)
  1937. m.fraction_of_ru_in_mr = cl_ru_fraction;
  1938. if (cl_tu_fraction != UNINITIALIZED)
  1939. m.fraction_of_tu_in_mt = cl_tu_fraction;
  1940. if (cl_UR1 != UNINITIALIZED)
  1941. m.m_r = cl_UR1;
  1942. if (cl_UT1 != UNINITIALIZED)
  1943. m.m_t = cl_UT1;
  1944. if (cl_Tc != UNINITIALIZED)
  1945. m.m_u = cl_Tc;
  1946. if (cl_default_fr != UNINITIALIZED)
  1947. m.f_r = cl_default_fr;
  1948. if (cl_baffle_r != UNINITIALIZED)
  1949. m.baffle_r = cl_baffle_r;
  1950. if (cl_baffle_t != UNINITIALIZED)
  1951. m.baffle_t = cl_baffle_t;
  1952. if (cl_lambda != UNINITIALIZED)
  1953. m.lambda = cl_lambda;
  1954. Initialize_Result(m, &r, TRUE);
  1955. params = 0;
  1956. rt_total = 0;
  1957. mc_total = 0;
  1958. if (Read_Header(stdin, &m, &params) != 0)
  1959. exit(EXIT_FAILURE);
  1960. start_time = clock();
  1961. while (Read_Data_Line(stdin, &m, &r, params) == 0) {
  1962. if (cl_cos_angle != UNINITIALIZED) {
  1963. m.slab_cos_angle = cl_cos_angle;
  1964. if (cl_quadrature_points == UNINITIALIZED)
  1965. cl_quadrature_points = 12;
  1966. if (cl_quadrature_points != 12 * (cl_quadrature_points / 12)) {
  1967. fprintf(stderr, "If you use the -i option to specify an oblique incidence angle, then\n");
  1968. fprintf(stderr, "the number of quadrature points must be a multiple of 12\n");
  1969. exit(EXIT_SUCCESS);
  1970. }
  1971. }
  1972. if (cl_sample_n != UNINITIALIZED)
  1973. m.slab_index = cl_sample_n;
  1974. if (cl_slide_n != UNINITIALIZED) {
  1975. m.slab_bottom_slide_index = cl_slide_n;
  1976. m.slab_top_slide_index = cl_slide_n;
  1977. if (cl_slide_d == UNINITIALIZED && !Column_Label_Present('D')) {
  1978. if (m.slab_top_slide_thickness == 0)
  1979. m.slab_top_slide_thickness = 1.0;
  1980. if (m.slab_bottom_slide_thickness == 0)
  1981. m.slab_bottom_slide_thickness = 1.0;
  1982. }
  1983. }
  1984. if (cl_slide_OD != UNINITIALIZED) {
  1985. m.slab_bottom_slide_b = cl_slide_OD;
  1986. m.slab_top_slide_b = cl_slide_OD;
  1987. }
  1988. if (cl_sample_d != UNINITIALIZED)
  1989. m.slab_thickness = cl_sample_d;
  1990. if (cl_beam_d != UNINITIALIZED)
  1991. m.d_beam = cl_beam_d;
  1992. if (cl_slide_d != UNINITIALIZED) {
  1993. m.slab_bottom_slide_thickness = cl_slide_d;
  1994. m.slab_top_slide_thickness = cl_slide_d;
  1995. }
  1996. if (cl_slides == NO_SLIDES) {
  1997. m.slab_bottom_slide_index = 1.0;
  1998. m.slab_bottom_slide_thickness = 0.0;
  1999. m.slab_top_slide_index = 1.0;
  2000. m.slab_top_slide_thickness = 0.0;
  2001. }
  2002. if (cl_slides == ONE_SLIDE_ON_TOP || cl_slides == ONE_SLIDE_NEAR_SPHERE) {
  2003. m.slab_bottom_slide_index = 1.0;
  2004. m.slab_bottom_slide_thickness = 0.0;
  2005. }
  2006. if (cl_slides == ONE_SLIDE_ON_BOTTOM || cl_slides == ONE_SLIDE_NOT_NEAR_SPHERE) {
  2007. m.slab_top_slide_index = 1.0;
  2008. m.slab_top_slide_thickness = 0.0;
  2009. }
  2010. if (cl_slides == ONE_SLIDE_NEAR_SPHERE || cl_slides == ONE_SLIDE_NOT_NEAR_SPHERE)
  2011. m.flip_sample = 1;
  2012. else
  2013. m.flip_sample = 0;
  2014. if (cl_slides == NO_SLIDES) {
  2015. m.slab_top_slide_b = 0.0;
  2016. m.slab_bottom_slide_b = 0.0;
  2017. }
  2018. if (cl_slides == ONE_SLIDE_ON_TOP || cl_slides == ONE_SLIDE_NEAR_SPHERE)
  2019. m.slab_bottom_slide_b = 0.0;
  2020. if (cl_slides == ONE_SLIDE_ON_BOTTOM || cl_slides == ONE_SLIDE_NOT_NEAR_SPHERE)
  2021. m.slab_top_slide_b = 0.0;
  2022. if (cl_method != UNINITIALIZED)
  2023. m.method = (int) cl_method;
  2024. if (cl_rstd_r != UNINITIALIZED) {
  2025. m.rstd_r = cl_rstd_r;
  2026. m.rstd_t = cl_rstd_r;
  2027. }
  2028. if (cl_rstd_t != UNINITIALIZED) {
  2029. m.rstd_t = cl_rstd_t;
  2030. if (cl_rstd_r == UNINITIALIZED)
  2031. m.rstd_r = cl_rstd_t;
  2032. }
  2033. if (cl_rwall_r != UNINITIALIZED || cl_rwall_t != UNINITIALIZED) {
  2034. if (cl_sphere_one[0] != UNINITIALIZED || cl_sphere_two[0] != UNINITIALIZED) {
  2035. fprintf(stderr, "A wall reflectance cannot accompany a sphere description.\n");
  2036. fprintf(stderr, " -1 and -2 already carry one as their fifth value\n");
  2037. fprintf(stderr, " -w and -W are for overriding the wall in an .rxt file\n");
  2038. exit(EXIT_FAILURE);
  2039. }
  2040. }
  2041. if (cl_rwall_r != UNINITIALIZED)
  2042. m.rw_r = cl_rwall_r;
  2043. if (cl_rwall_t != UNINITIALIZED)
  2044. m.rw_t = cl_rwall_t;
  2045. if (cl_sphere_one[0] != UNINITIALIZED) {
  2046. double d_sample_r, d_third_r, d_detector_r;
  2047. m.d_sphere_r = cl_sphere_one[0];
  2048. d_sample_r = cl_sphere_one[1];
  2049. d_third_r = cl_sphere_one[2];
  2050. d_detector_r = cl_sphere_one[3];
  2051. m.rw_r = cl_sphere_one[4];
  2052. m.as_r = sqr(d_sample_r / m.d_sphere_r / 2);
  2053. m.at_r = sqr(d_third_r / m.d_sphere_r / 2);
  2054. m.ad_r = sqr(d_detector_r / m.d_sphere_r / 2);
  2055. m.aw_r = 1.0 - m.as_r - m.at_r - m.ad_r;
  2056. m.d_sphere_t = m.d_sphere_r;
  2057. m.as_t = m.as_r;
  2058. m.at_t = m.at_r;
  2059. m.ad_t = m.ad_r;
  2060. m.aw_t = m.aw_r;
  2061. m.rw_t = m.rw_r;
  2062. if (cl_num_spheres == UNINITIALIZED)
  2063. m.num_spheres = 1;
  2064. }
  2065. if (cl_sphere_two[0] != UNINITIALIZED) {
  2066. double d_sample_t, d_third_t, d_detector_t;
  2067. m.d_sphere_t = cl_sphere_two[0];
  2068. d_sample_t = cl_sphere_two[1];
  2069. d_third_t = cl_sphere_two[2];
  2070. d_detector_t = cl_sphere_two[3];
  2071. m.rw_t = cl_sphere_two[4];
  2072. m.as_t = sqr(d_sample_t / m.d_sphere_t / 2);
  2073. m.at_t = sqr(d_third_t / m.d_sphere_t / 2);
  2074. m.ad_t = sqr(d_detector_t / m.d_sphere_t / 2);
  2075. m.aw_t = 1.0 - m.as_t - m.at_t - m.ad_t;
  2076. if (cl_num_spheres == UNINITIALIZED)
  2077. m.num_spheres = 2;
  2078. }
  2079. if (cl_num_spheres != UNINITIALIZED) {
  2080. m.num_spheres = (int) cl_num_spheres;
  2081. if (m.num_spheres > 0 && m.method == UNKNOWN)
  2082. m.method = SUBSTITUTION;
  2083. }
  2084. if (cl_ru_fraction != UNINITIALIZED)
  2085. m.fraction_of_ru_in_mr = cl_ru_fraction;
  2086. if (cl_tu_fraction != UNINITIALIZED)
  2087. m.fraction_of_tu_in_mt = cl_tu_fraction;
  2088. if (cl_UR1 != UNINITIALIZED)
  2089. m.m_r = cl_UR1;
  2090. if (cl_UT1 != UNINITIALIZED)
  2091. m.m_t = cl_UT1;
  2092. if (cl_Tc != UNINITIALIZED)
  2093. m.m_u = cl_Tc;
  2094. if (cl_default_fr != UNINITIALIZED)
  2095. m.f_r = cl_default_fr;
  2096. if (cl_baffle_r != UNINITIALIZED)
  2097. m.baffle_r = cl_baffle_r;
  2098. if (cl_baffle_t != UNINITIALIZED)
  2099. m.baffle_t = cl_baffle_t;
  2100. if (cl_lambda != UNINITIALIZED)
  2101. m.lambda = cl_lambda;
  2102. {
  2103. double ur1 = 0;
  2104. double ut1 = 0;
  2105. double uru = 0;
  2106. double utu = 0;
  2107. double mu_a = 0;
  2108. double mu_sp = 0;
  2109. double LR = 0;
  2110. double LT = 0;
  2111. int skip = FALSE;
  2112. if (cl_wave_limit[0] != UNINITIALIZED) {
  2113. if (m.lambda != 0) {
  2114. if (m.lambda < cl_wave_limit[0])
  2115. skip = TRUE;
  2116. if (m.lambda > cl_wave_limit[1])
  2117. skip = TRUE;
  2118. }
  2119. }
  2120. if (Debug(DEBUG_ANY) && !skip) {
  2121. fprintf(stderr, "\n-------------------NEXT DATA POINT---------------------\n");
  2122. if (m.lambda != 0)
  2123. fprintf(stderr, "lambda=%6.1f ", m.lambda);
  2124. fprintf(stderr, "MR=%8.5f MT=%8.5f\n\n", m.m_r, m.m_t);
  2125. if (skip)
  2126. fprintf(stderr, "skipping, wavelength out of range.\n");
  2127. }
  2128. if (!skip) {
  2129. rt_total++;
  2130. Initialize_Result(m, &r, FALSE);
  2131. if (cl_quadrature_points != UNINITIALIZED)
  2132. r.method.quad_pts = cl_quadrature_points;
  2133. else
  2134. r.method.quad_pts = 8;
  2135. if (cl_default_a != UNINITIALIZED)
  2136. r.default_a = cl_default_a;
  2137. if (cl_default_mua != UNINITIALIZED) {
  2138. r.default_mua = cl_default_mua;
  2139. if (cl_sample_d != UNINITIALIZED)
  2140. r.default_ba = cl_default_mua * cl_sample_d;
  2141. else
  2142. r.default_ba = cl_default_mua * m.slab_thickness;
  2143. }
  2144. if (cl_default_b != UNINITIALIZED)
  2145. r.default_b = cl_default_b;
  2146. if (cl_default_g != UNINITIALIZED)
  2147. r.default_g = cl_default_g;
  2148. if (cl_tolerance != UNINITIALIZED) {
  2149. r.tolerance = cl_tolerance;
  2150. r.MC_tolerance = cl_tolerance;
  2151. }
  2152. if (cl_mus0 != UNINITIALIZED) {
  2153. if (m.lambda != 0) {
  2154. cl_default_mus = cl_mus0 * pow(m.lambda / cl_mus0_lambda, cl_mus0_pwr);
  2155. }
  2156. else {
  2157. fprintf(stderr, "Seems like you want to constrain scattering to a power law.\n");
  2158. fprintf(stderr, "Unfortunately, there is no wavelength so this cannot be done.\n");
  2159. }
  2160. }
  2161. if (cl_default_mus != UNINITIALIZED) {
  2162. r.default_mus = cl_default_mus;
  2163. if (cl_sample_d != UNINITIALIZED)
  2164. r.default_bs = cl_default_mus * cl_sample_d;
  2165. else
  2166. r.default_bs = cl_default_mus * m.slab_thickness;
  2167. }
  2168. if (cl_default_musp != UNINITIALIZED) {
  2169. if (cl_default_g != UNINITIALIZED)
  2170. r.default_mus = cl_default_musp / (1.0 - cl_default_g);
  2171. else
  2172. r.default_mus = cl_default_musp;
  2173. if (cl_sample_d != UNINITIALIZED)
  2174. r.default_bs = r.default_mus * cl_sample_d;
  2175. else
  2176. r.default_bs = r.default_mus * m.slab_thickness;
  2177. }
  2178. if (cl_search != UNINITIALIZED)
  2179. r.search = cl_search;
  2180. if (cl_method == COMPARISON && m.d_sphere_r != 0 && m.as_r == 0) {
  2181. fprintf(stderr, "A dual-beam measurement is specified, but no port sizes.\n");
  2182. fprintf(stderr, "You might forsake the -X option and use zero spheres (which gives\n");
  2183. fprintf(stderr, "the same result except lost light is not taken into account).\n");
  2184. fprintf(stderr, "Alternatively, bite the bullet and enter your sphere parameters,\n");
  2185. fprintf(stderr, "with the knowledge that only the beam diameter and sample port\n");
  2186. fprintf(stderr, "diameter will be used to estimate lost light from the edges.\n");
  2187. exit(EXIT_SUCCESS);
  2188. }
  2189. if (cl_method == COMPARISON && m.num_spheres == 2) {
  2190. fprintf(stderr, "A dual-beam measurement is specified, but a two sphere experiment\n");
  2191. fprintf(stderr, "is specified. Since this seems impossible, I will make it\n");
  2192. fprintf(stderr, "impossible for you unless you specify 0 or 1 sphere.\n");
  2193. exit(EXIT_SUCCESS);
  2194. }
  2195. if (cl_method == COMPARISON && m.f_r != 0) {
  2196. fprintf(stderr, "A dual-beam measurement is specified, but a fraction of light\n");
  2197. fprintf(stderr, "is specified to hit the sphere wall first. This situation\n");
  2198. fprintf(stderr, "is not supported by iad. Sorry.\n");
  2199. exit(EXIT_SUCCESS);
  2200. }
  2201. if (rt_total == 1 && cl_verbosity > 0) {
  2202. Write_Header(m, r, params, command_line);
  2203. if (MAX_MC_iterations > 0) {
  2204. if (n_photons >= 0)
  2205. fprintf(stdout, "# Photons used to estimate lost light = %ld\n", n_photons);
  2206. else
  2207. fprintf(stdout, "# Time used to estimate lost light = %ld ms\n", -n_photons);
  2208. }
  2209. else
  2210. fprintf(stdout, "# Photons used to estimate lost light = 0\n");
  2211. fprintf(stdout, "#\n");
  2212. print_results_header(stdout);
  2213. }
  2214. m.lost_r.direct = 0;
  2215. m.lost_r.diffuse = 0;
  2216. m.lost_t.diffuse = 0;
  2217. m.lost_t.direct = 0;
  2218. m.utu_lost = 0;
  2219. Inverse_RT(m, &r);
  2220. calculate_coefficients(m, r, &LR, &LT, &mu_sp, &mu_a);
  2221. if (m.num_spheres > 0 && r.found && r.error == IAD_NO_ERROR) {
  2222. if (Debug(DEBUG_LOST_LIGHT)) {
  2223. print_results_header(stderr);
  2224. print_optical_property_result(stderr, m, r, LR, LT, mu_a, mu_sp, rt_total);
  2225. }
  2226. {
  2227. int mc_failed = 0;
  2228. int mc_unreachable = 0;
  2229. int pinned = 0;
  2230. struct measure_type good_m = m;
  2231. struct invert_type good_r = r;
  2232. double mc_prev_a = r.slab.a;
  2233. double mc_prev_b = r.slab.b;
  2234. double mc_prev_g = r.slab.g;
  2235. {
  2236. double b0 = (r.slab.b < 1e8) ? r.slab.b : 1.0;
  2237. r.mc_simplex_a_step = 1e-3;
  2238. r.mc_simplex_b_step = 1e-2 * (b0 > 1.0 ? b0 : 1.0);
  2239. r.mc_simplex_g_step = 1e-3;
  2240. }
  2241. int has_prev_diff_ur1_lost = 0;
  2242. double prev_abs_diff_ur1_lost = 0.0;
  2243. while (r.MC_iterations < MAX_MC_iterations) {
  2244. long n_photons_this;
  2245. double last_mu_sp, last_mu_a;
  2246. struct lost_type current_r, current_t;
  2247. double current_utu_lost;
  2248. double diff_ur1_lost, diff_ut1_lost, diff_uru_lost, diff_utu_lost;
  2249. double diff_uru_lost_t;
  2250. double factor = 0.3;
  2251. int too_much_lost;
  2252. double tol = r.MC_tolerance;
  2253. calculate_coefficients(m, r, &LR, &LT, &mu_sp, &mu_a);
  2254. last_mu_sp = mu_sp;
  2255. last_mu_a = mu_a;
  2256. if (Debug(DEBUG_ITERATIONS) || Debug(DEBUG_A_LITTLE)) {
  2257. fprintf(stderr, "\n------------- Monte Carlo Iteration %d -----------------\n",
  2258. r.MC_iterations + 1);
  2259. }
  2260. if (n_photons < 0)
  2261. n_photons_this = n_photons;
  2262. else if (!has_prev_diff_ur1_lost || prev_abs_diff_ur1_lost > 0.01)
  2263. n_photons_this = (n_photons / 10 > 10000) ? n_photons / 10 : 10000;
  2264. else if (prev_abs_diff_ur1_lost > 0.001)
  2265. n_photons_this = n_photons;
  2266. else
  2267. n_photons_this = (n_photons * 5 < 10000000) ? n_photons * 5 : 10000000;
  2268. MC_Lost(m, r, n_photons_this, &ur1, &ut1, &uru, &utu,
  2269. &current_r, &current_t, &current_utu_lost);
  2270. diff_ur1_lost = current_r.direct - m.lost_r.direct;
  2271. diff_uru_lost = current_r.diffuse - m.lost_r.diffuse;
  2272. diff_uru_lost_t = current_t.diffuse - m.lost_t.diffuse;
  2273. diff_ut1_lost = current_t.direct - m.lost_t.direct;
  2274. diff_utu_lost = current_utu_lost - m.utu_lost;
  2275. prev_abs_diff_ur1_lost = fabs(diff_ur1_lost);
  2276. has_prev_diff_ur1_lost = 1;
  2277. if (fabs(diff_ur1_lost) > 0.001 || fabs(diff_ut1_lost) > 0.001)
  2278. too_much_lost = 1;
  2279. else
  2280. too_much_lost = 0;
  2281. m.lost_r.direct += factor * diff_ur1_lost;
  2282. m.lost_r.diffuse += factor * diff_uru_lost;
  2283. m.lost_t.diffuse += factor * diff_uru_lost_t;
  2284. m.lost_t.direct += factor * diff_ut1_lost;
  2285. m.utu_lost += factor * diff_utu_lost;
  2286. mc_total++;
  2287. r.MC_iterations++;
  2288. Inverse_RT(m, &r);
  2289. {
  2290. double new_a = r.slab.a, new_b = r.slab.b, new_g = r.slab.g;
  2291. double da = fabs(new_a - mc_prev_a);
  2292. double db = fabs(new_b - mc_prev_b);
  2293. double dg = fabs(new_g - mc_prev_g);
  2294. double b_safe = (new_b < 1e8) ? new_b : 1.0;
  2295. double a_fixed = 1e-3;
  2296. double b_fixed = 1e-2 * (b_safe > 1.0 ? b_safe : 1.0);
  2297. double g_fixed = 1e-3;
  2298. r.mc_simplex_a_step = (da < 1e-5) ? 1e-5 : (da > a_fixed ? a_fixed : da);
  2299. r.mc_simplex_b_step = (db < 1e-4) ? 1e-4 : (db > b_fixed ? b_fixed : db);
  2300. r.mc_simplex_g_step = (dg < 1e-5) ? 1e-5 : (dg > g_fixed ? g_fixed : dg);
  2301. mc_prev_a = new_a;
  2302. mc_prev_b = new_b;
  2303. mc_prev_g = new_g;
  2304. }
  2305. calculate_coefficients(m, r, &LR, &LT, &mu_sp, &mu_a);
  2306. if (r.found && r.error == IAD_NO_ERROR) {
  2307. good_m = m;
  2308. good_r = r;
  2309. }
  2310. if (0) {
  2311. fprintf(stderr, "%2d %2d %2d | %7.4f %7.4f %7.4f | %7.4f %7.4f %7.4f\n",
  2312. r.MC_iterations, too_much_lost, r.found,
  2313. m.m_r, current_r.direct, m.lost_r.direct, m.m_t, current_t.direct, m.lost_t.direct);
  2314. }
  2315. if (Debug(DEBUG_LOST_LIGHT))
  2316. print_optical_property_result(stderr, m, r, LR, LT, mu_a, mu_sp, rt_total);
  2317. else
  2318. print_dot(start_time, r.error, mc_total, FALSE, cl_verbosity);
  2319. if (mu_a <= 0.0 && too_much_lost)
  2320. pinned++;
  2321. else
  2322. pinned = 0;
  2323. if (pinned >= 2) {
  2324. {
  2325. double b_thin = 1e-6;
  2326. double b_t, mr_at, mt_at, mr_thin, mt_thin, mr_dn, mt_dn, mr_up, mt_up;
  2327. int unreachable = 0;
  2328. bright_mr_mt(m, r, b_thin, &mr_thin, &mt_thin);
  2329. if (m.m_t > mt_thin) {
  2330. {
  2331. unreachable = 1;
  2332. b_t = b_thin;
  2333. mr_at = mr_thin;
  2334. mt_at = mt_thin;
  2335. }
  2336. }
  2337. else {
  2338. b_t = solve_for_b_from_mt(m, r, m.m_t);
  2339. bright_mr_mt(m, r, b_t, &mr_at, &mt_at);
  2340. bright_mr_mt(m, r, b_t * 0.9, &mr_dn, &mt_dn);
  2341. bright_mr_mt(m, r, b_t * 1.1, &mr_up, &mt_up);
  2342. if (m.m_r > mr_at && mr_up > mr_at && mr_at > mr_dn)
  2343. unreachable = 1;
  2344. }
  2345. if (unreachable) {
  2346. r.error = IAD_UNREACHABLE_WITH_LOST_LIGHT;
  2347. if (Debug(DEBUG_LOST_LIGHT) || Debug(DEBUG_A_LITTLE) || Debug(DEBUG_ITERATIONS)) {
  2348. fprintf(stderr, "The lost light puts the measurements out of reach.\n");
  2349. fprintf(stderr, " with no absorption at b = %.4f the model gives\n",
  2350. b_t);
  2351. fprintf(stderr, " M_R %8.5f measured %8.5f short by %8.5f\n", mr_at,
  2352. m.m_r, m.m_r - mr_at);
  2353. fprintf(stderr, " M_T %8.5f measured %8.5f\n", mt_at, m.m_t);
  2354. }
  2355. }
  2356. }
  2357. if (r.error == IAD_UNREACHABLE_WITH_LOST_LIGHT) {
  2358. mc_unreachable = 1;
  2359. if (Debug(DEBUG_ITERATIONS))
  2360. fprintf(stderr,
  2361. "absorption is spent and the data is out of reach — stopping\n");
  2362. break;
  2363. }
  2364. }
  2365. if (r.found) {
  2366. if (fabs(last_mu_a - mu_a) > tol) {
  2367. if (Debug(DEBUG_ITERATIONS))
  2368. fprintf(stderr, "Repeat MC because mua is still changing\n");
  2369. continue;
  2370. }
  2371. if (fabs(last_mu_sp - mu_sp) > tol) {
  2372. if (Debug(DEBUG_ITERATIONS))
  2373. fprintf(stderr, "Repeat MC because musp is still changing\n");
  2374. continue;
  2375. }
  2376. if (too_much_lost) {
  2377. if (Debug(DEBUG_ITERATIONS))
  2378. fprintf(stderr, "Repeat MC because mua and musp are still changing\n");
  2379. continue;
  2380. }
  2381. if (Debug(DEBUG_ITERATIONS))
  2382. fprintf(stderr, "found!\n");
  2383. break;
  2384. }
  2385. else {
  2386. if (Debug(DEBUG_ITERATIONS))
  2387. fprintf(stderr, "AD did not converge — stopping MC loop\n");
  2388. mc_failed = 1;
  2389. break;
  2390. }
  2391. }
  2392. if (mc_unreachable) {
  2393. r.found = 0;
  2394. r.error = IAD_UNREACHABLE_WITH_LOST_LIGHT;
  2395. {
  2396. int why = r.error;
  2397. struct lost_type lost_r = m.lost_r;
  2398. struct lost_type lost_t = m.lost_t;
  2399. double utu_lost = m.utu_lost;
  2400. m = good_m;
  2401. m.lost_r = lost_r;
  2402. m.lost_t = lost_t;
  2403. m.utu_lost = utu_lost;
  2404. r = good_r;
  2405. r.error = why;
  2406. r.found = 0;
  2407. }
  2408. }
  2409. else if (mc_failed) {
  2410. r.found = 0;
  2411. {
  2412. double b_thin = 1e-6;
  2413. double b_t, mr_at, mt_at, mr_thin, mt_thin, mr_dn, mt_dn, mr_up, mt_up;
  2414. int unreachable = 0;
  2415. bright_mr_mt(m, r, b_thin, &mr_thin, &mt_thin);
  2416. if (m.m_t > mt_thin) {
  2417. {
  2418. unreachable = 1;
  2419. b_t = b_thin;
  2420. mr_at = mr_thin;
  2421. mt_at = mt_thin;
  2422. }
  2423. }
  2424. else {
  2425. b_t = solve_for_b_from_mt(m, r, m.m_t);
  2426. bright_mr_mt(m, r, b_t, &mr_at, &mt_at);
  2427. bright_mr_mt(m, r, b_t * 0.9, &mr_dn, &mt_dn);
  2428. bright_mr_mt(m, r, b_t * 1.1, &mr_up, &mt_up);
  2429. if (m.m_r > mr_at && mr_up > mr_at && mr_at > mr_dn)
  2430. unreachable = 1;
  2431. }
  2432. if (unreachable) {
  2433. r.error = IAD_UNREACHABLE_WITH_LOST_LIGHT;
  2434. if (Debug(DEBUG_LOST_LIGHT) || Debug(DEBUG_A_LITTLE) || Debug(DEBUG_ITERATIONS)) {
  2435. fprintf(stderr, "The lost light puts the measurements out of reach.\n");
  2436. fprintf(stderr, " with no absorption at b = %.4f the model gives\n", b_t);
  2437. fprintf(stderr, " M_R %8.5f measured %8.5f short by %8.5f\n",
  2438. mr_at, m.m_r, m.m_r - mr_at);
  2439. fprintf(stderr, " M_T %8.5f measured %8.5f\n", mt_at, m.m_t);
  2440. }
  2441. }
  2442. }
  2443. if (r.error == IAD_NO_ERROR)
  2444. r.error = IAD_MC_DID_NOT_CONVERGE;
  2445. {
  2446. int why = r.error;
  2447. struct lost_type lost_r = m.lost_r;
  2448. struct lost_type lost_t = m.lost_t;
  2449. double utu_lost = m.utu_lost;
  2450. m = good_m;
  2451. m.lost_r = lost_r;
  2452. m.lost_t = lost_t;
  2453. m.utu_lost = utu_lost;
  2454. r = good_r;
  2455. r.error = why;
  2456. r.found = 0;
  2457. }
  2458. }
  2459. }
  2460. }
  2461. if (!r.found && r.error == IAD_NO_ERROR)
  2462. r.error = IAD_SEARCH_STALLED;
  2463. calculate_coefficients(m, r, &LR, &LT, &mu_sp, &mu_a);
  2464. print_optical_property_result(stdout, m, r, LR, LT, mu_a, mu_sp, rt_total);
  2465. if (r.error != IAD_NO_ERROR) {
  2466. any_error = 1;
  2467. last_error = r.error;
  2468. }
  2469. if (Debug(DEBUG_ANY))
  2470. print_long_error(r.error);
  2471. else
  2472. print_dot(start_time, r.error, mc_total, TRUE, cl_verbosity);
  2473. }
  2474. }
  2475. }
  2476. file_index++;
  2477. if (file_index < file_count)
  2478. goto read_next_file;
  2479. if (cl_grid_calc != UNINITIALIZED) {
  2480. double aa[] = { 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.85,
  2481. 0.9, 0.93, 0.95, 0.97, 0.98, 0.99, 0.995, 1.0
  2482. };
  2483. double bb[] = { 0, 0.05, 0.1, 0.2, 0.3, 0.5, 0.7, 1.0, 1.5, 2.0,
  2484. 3.0, 5.0, 7.0, 10.0, 20.0, 30.0, 50.0, 100.0
  2485. };
  2486. int na = (int) (sizeof(aa) / sizeof(aa[0]));
  2487. int nb = (int) (sizeof(bb) / sizeof(bb[0]));
  2488. double grid_step = 1e-3;
  2489. int use_mc = (MAX_MC_iterations != 0 && m.num_spheres != 0);
  2490. double g;
  2491. int i, j, k;
  2492. int count = 0;
  2493. FILE *grid;
  2494. grid = fopen(g_grid_name, "w");
  2495. if (grid == NULL) {
  2496. fprintf(stderr, "Could not open grid file '%s' for output\n", g_out_name);
  2497. exit(EXIT_FAILURE);
  2498. }
  2499. m.lost_r.direct = 0;
  2500. m.lost_r.diffuse = 0;
  2501. m.lost_t.diffuse = 0;
  2502. m.lost_t.direct = 0;
  2503. m.utu_lost = 0;
  2504. if (r.default_g != UNINITIALIZED) {
  2505. g = r.default_g;
  2506. }
  2507. else if (r.found) {
  2508. g = r.slab.g;
  2509. }
  2510. else {
  2511. g = 0;
  2512. }
  2513. fprintf(grid, "# %s (g=%6.4f)\n", command_line, g);
  2514. fprintf(grid, "# a' b' g M_R M_T rho12\n");
  2515. fprintf(stderr, "\ndoing grid calculation\n");
  2516. for (i = 0; i < na; i++) {
  2517. for (j = 0; j < nb; j++) {
  2518. double ap[3], bp[3], m_r[3], m_t[3];
  2519. ap[0] = aa[i];
  2520. bp[0] = bb[j];
  2521. ap[1] = (aa[i] + grid_step <= 1) ? aa[i] + grid_step : aa[i] - grid_step;
  2522. bp[1] = bb[j];
  2523. ap[2] = aa[i];
  2524. bp[2] = bb[j] * (1 + grid_step);
  2525. if (use_mc) {
  2526. double ur1, ut1, uru, utu;
  2527. r.slab.a = ap[0] / (1 - g + ap[0] * g);
  2528. r.slab.b = bp[0] / (1 - r.slab.a * g);
  2529. r.slab.g = g;
  2530. r.a = r.slab.a;
  2531. r.b = r.slab.b;
  2532. r.g = g;
  2533. MC_Lost(m, r, 100000, &ur1, &ut1, &uru, &utu, &m.lost_r, &m.lost_t, &m.utu_lost);
  2534. }
  2535. for (k = 0; k < 3; k++) {
  2536. r.slab.a = ap[k] / (1 - g + ap[k] * g);
  2537. r.slab.b = bp[k] / (1 - r.slab.a * g);
  2538. r.slab.g = g;
  2539. r.a = r.slab.a;
  2540. r.b = r.slab.b;
  2541. r.g = g;
  2542. Calculate_MR_MT(m, r, use_mc ? MC_USE_EXISTING : MC_NONE, TRUE, &m_r[k], &m_t[k]);
  2543. }
  2544. fprintf(grid, "%10.5f, %10.5f, %10.5f, %10.5f, %10.5f, ", ap[0], bp[0], g, m_r[0], m_t[0]);
  2545. {
  2546. double dMR_da, dMT_da, dMR_db, dMT_db, u_r, u_t, v_r, v_t, uu, vv;
  2547. u_r = u_t = v_r = v_t = 0;
  2548. uu = 0;
  2549. vv = 0;
  2550. if (bp[0] > 0) {
  2551. dMR_da = (m_r[1] - m_r[0]) / (ap[1] - ap[0]);
  2552. dMT_da = (m_t[1] - m_t[0]) / (ap[1] - ap[0]);
  2553. dMR_db = (m_r[2] - m_r[0]) / (bp[2] - bp[0]);
  2554. dMT_db = (m_t[2] - m_t[0]) / (bp[2] - bp[0]);
  2555. u_r = dMR_db - ap[0] / bp[0] * dMR_da;
  2556. u_t = dMT_db - ap[0] / bp[0] * dMT_da;
  2557. v_r = dMR_db + (1 - ap[0]) / bp[0] * dMR_da;
  2558. v_t = dMT_db + (1 - ap[0]) / bp[0] * dMT_da;
  2559. uu = u_r * u_r + u_t * u_t;
  2560. vv = v_r * v_r + v_t * v_t;
  2561. }
  2562. if (uu > 1e-20 && vv > 1e-20)
  2563. fprintf(grid, "%10.5f\n", -(u_r * v_r + u_t * v_t) / sqrt(uu * vv));
  2564. else
  2565. fprintf(grid, "%10s\n", "nan");
  2566. }
  2567. count++;
  2568. fprintf(stderr, "*");
  2569. if (count % 10 == 0)
  2570. fprintf(stderr, " ");
  2571. if (count % 50 == 0)
  2572. fprintf(stderr, "\n");
  2573. }
  2574. }
  2575. fclose(grid);
  2576. fprintf(stderr, "\n");
  2577. }
  2578. if (cl_verbosity > 0)
  2579. fprintf(stderr, "\n\n");
  2580. if (any_error && cl_verbosity > 1)
  2581. print_error_legend();
  2582. exit(EXIT_SUCCESS);
  2583. }

iad_main.c at commit 8a1da20, under MIT · at the source

Overview

Authors: Silvére Ségaud1, Charlie Budd1, Matthew Elliot1,2, Graeme J Stasiuk3, Jonathan Shapey1,2, Yijing Xie1, Tom Vercauteren1
  1. Research Department of Surgical & Interventional Engineering, School of Biomedical Engineering & Imaging Sciences, King’s College London, London SE1 7EH, United Kingdom
  2. Department of Neurosurgery, King’s College London Hospital NHS Foundation Trust, London SE5 9RS, United Kingdom
  3. Research Department of Imaging Chemistry & Biology, School of Biomedical Engineering & Imaging Sciences, King’s College London, London SE1 7EH, United Kingdom
Institutions: King's College London (United Kingdom); King's College Hospital NHS Foundation Trust (United Kingdom)
Journal: Physics in medicine and biology, volume 71, issue 5, article 055006
Dates: received 17 October 2025; accepted 13 February 2026; published online 6 March 2026; in print 14 March 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1088/1361-6560/ae45e6 · PMID 41687253 · PMCID PMC12965099 · OpenAlex W4416056020
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), other condition (population), clinical / translational (subfield)
Methods: Connectivity, Machine learning
Keywords: glioma, fluorescence guided surgery, quantitative fluorescence, tissue-mimicking phantom, 5-ALA-PpIX, PpIX620, PpIX635
MeSH: Aminolevulinic Acid*, Optical Imaging*, Phantoms, Imaging*, Protoporphyrins*, Brain Neoplasms, Glioma, Humans, Spectrometry, Fluorescence (* major topic)
Topic: Photodynamic Therapy Research Studies (Pulmonary and Respiratory Medicine, Medicine), according to OpenAlex
Funding: Wellcome Trust (WT223880/Z/21/Z); National Institute for Health Research (NIHR) (NIHR202114); National Institute for Health and Care Research (NIHR202114); Wellcome/EPSRC (NS/A000049/1)
Citations: not cited yet (Europe PMC); 55 references in the paper

Abstract

Quantification of protoporphyrin IX (PpIX) fluorescence in human brain tumours has the potential to significantly improve patient outcomes in neuro-oncology, but represents a formidable imaging challenge. Protoporphyrin is a biological molecule which interacts with the tissue micro-environment to form two photochemical states in glioma. Each exhibits markedly different quantum efficiencies, with distinct but overlapping emission spectra that also overlap with tissue autofluorescence. Fluorescence emission is known to be distorted by the intrinsic optical properties of tissue, coupled with marked intra-tumoural heterogeneity as a hallmark of glioma tumours. Existing quantitative fluorescence systems are developed and validated using simplified phantoms that do not simultaneously mimic the complex interactions between fluorophores and tissue optical properties or micro-environment. Consequently, existing systems risk introducing systematic errors into PpIX quantification when used in tissue. In this work, we introduce a novel pipeline for quantification of PpIX in glioma, which robustly differentiates both emission states from background autofluorescence without reliance on a priori spectral information, and accounts for variations in their quantum efficiency. Unmixed PpIX emission forms are then corrected for wavelength-dependent optical distortions and weighted for accurate quantification. Significantly, this pipeline is developed and validated using novel tissue-mimicking phantoms replicating the optical properties of glioma tissues and photochemical variability of PpIX fluorescence in glioma. Our workflow achieves strong correlation with ground-truth PpIX concentrations (R2=0.918±0.002), demonstrating its potential for robust, quantitative PpIX fluorescence imaging in clinical settings.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 2 matches between paragraphs and lines of code.

scottprahl/iad

License: MIT
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: 8a1da205b9e198011fa6903bd4d896affce228dd, 26 September 2026
Languages: C (37), C/C++ (31), Shell (16), Python (3), Perl (2)
Size: 230 files, 89 scripts
Software Heritage: archived
Found in: the text, “Footnotes”
Holds: README, license file, CITATION.cff, tests, continuous integration, documentation
Not found: environment file
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
91 files

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 89 scripts, each with its path and the digest of its content;
  • 2 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data availability statement

All data that support the findings of this study are included within the article (and any supplementary information files). Data will be available from 20 March 2026.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 30 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 7 authors, 7 keywords, 8 MeSH terms, 4 funders, 54 references.

Cite

This paper

Ségaud, S., Budd, C., Elliot, M., Stasiuk, G. J., Shapey, J., Xie, Y., & Vercauteren, T. (2026). Quantification of dual-state 5-ALA-induced PpIX fluorescence: methodology and validation in tissue-mimicking phantoms. Physics in medicine and biology, 71(5), 055006. https://doi.org/10.1088/1361-6560/ae45e6

BibTeX

@article{segaud2026quantification,
author = {Ségaud, Silvére and Budd, Charlie and Elliot, Matthew and Stasiuk, Graeme J and Shapey, Jonathan and Xie, Yijing and Vercauteren, Tom},
title = {{Quantification of dual-state 5-ALA-induced PpIX fluorescence: methodology and validation in tissue-mimicking phantoms}},
journal = {Physics in medicine and biology},
year = {2026},
month = mar,
volume = {71},
number = {5},
pages = {055006},
publisher = {IOP Publishing},
issn = {0031-9155},
doi = {10.1088/1361-6560/ae45e6},
url = {https://doi.org/10.1088/1361-6560/ae45e6},
pmid = {41687253},
pmcid = {PMC12965099}
}

RIS

TY - JOUR
AU - Ségaud, Silvére
AU - Budd, Charlie
AU - Elliot, Matthew
AU - Stasiuk, Graeme J
AU - Shapey, Jonathan
AU - Xie, Yijing
AU - Vercauteren, Tom
TI - Quantification of dual-state 5-ALA-induced PpIX fluorescence: methodology and validation in tissue-mimicking phantoms
T2 - Physics in medicine and biology
J2 - Phys Med Biol
PY - 2026
DA - 2026/03/06
VL - 71
IS - 5
SP - 055006
SN - 0031-9155
PB - IOP Publishing
DO - 10.1088/1361-6560/ae45e6
UR - https://doi.org/10.1088/1361-6560/ae45e6
LA - en
ER -

CSL-JSON

{
"id": "10.1088/1361-6560/ae45e6",
"type": "article-journal",
"title": "Quantification of dual-state 5-ALA-induced PpIX fluorescence: methodology and validation in tissue-mimicking phantoms",
"container-title": "Physics in medicine and biology",
"author": [
{
"family": "Ségaud",
"given": "Silvére"
},
{
"family": "Budd",
"given": "Charlie"
},
{
"family": "Elliot",
"given": "Matthew"
},
{
"family": "Stasiuk",
"given": "Graeme J"
},
{
"family": "Shapey",
"given": "Jonathan"
},
{
"family": "Xie",
"given": "Yijing"
},
{
"family": "Vercauteren",
"given": "Tom"
}
],
"container-title-short": "Phys Med Biol",
"volume": "71",
"issue": "5",
"page": "055006",
"DOI": "10.1088/1361-6560/ae45e6",
"PMID": "41687253",
"PMCID": "PMC12965099",
"ISSN": "0031-9155",
"publisher": "IOP Publishing",
"URL": "https://doi.org/10.1088/1361-6560/ae45e6",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
6
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1117/1.nph.13.3.035007 [code]
Analytical model enabling the correction of absorption and scattering in dual-color ratiometric multiphoton microscopy of brain tissue.
Journal: Neurophotonics
In common: 4 references
[2] doi:10.21037/jtd-2026-0997 [code]
Machine learning models based on XGBoost algorithm to predict prognosis of lung cancer brain metastases.
Journal: Journal of thoracic disease
In common: clinical / translational, other condition, 1 reference
[3] doi:10.1117/1.jbo.31.8.086005
Label-free wide-field imaging of brain tumors using near-infrared endogenous fluorescence and reflectance normalization.
Journal: Journal of biomedical optics
In common: other condition, 1 reference
[4] doi:
Toward reliable computer-aided brain tumor diagnosis: a contrast-enhanced deep learning approach with hybrid KNN classification
Journal: Frontiers in human neuroscience
In common: clinical / translational, other condition, 1 reference
[5] doi:10.1136/jitc-2025-014421
Spatial immune atlas of breast cancer brain metastasis reveals CD163&lt;sup&gt;+&lt;/sup&gt; macrophage reprogramming associated with immune escape.
Journal: Journal for immunotherapy of cancer
In common: other condition, 1 reference
[6] doi:10.1038/s41746-026-02735-x [code]
Multimodal interpretable deep learning for transcriptome-informed precision oncology and drug mechanism analysis.
Journal: NPJ digital medicine
In common: other condition, 1 reference
[7] doi:10.3389/fimmu.2026.1792836
Multi-omics investigation of perineural invasion in head and neck squamous cell carcinoma: neuroimmune mechanisms and clinical implications.
Journal: Frontiers in immunology
In common: other condition, 1 reference
[8] doi:10.2196/84095
Detection of Interpretable and Fine-Grained Brain Tumor Magnetic Resonance Imaging Based on Progressive Pruning: Machine Learning Model Development and Validation Study.
Journal: JMIR medical informatics
In common: other condition, 1 reference
[9] doi:10.1080/10717544.2026.2660007
Angiopep-2-decorated bacterial outer membrane vesicles penetrate the blood-brain barrier for glioblastoma chemo-immunotherapy.
Journal: Drug delivery
In common: other condition, 1 reference
[10] doi:10.1002/adhm.202504889 [code]
Mapping the Cerebral Organoid Landscape: A Systematic Review of Preclinical 3D Models in Neuroscience.
Journal: Advanced healthcare materials
In common: other condition, 1 reference

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.