OSCR

Monte Carlo simulations of time-resolved blood flow index: times-of-flight beyond ∼1 ns are necessary for brain-dominated measurements.

Code ↔ Paper

7 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 7 matches
  1. [1] § Methods › Simulation Setup ↔ mc_analysis/fitting/scos_utils.py, lines 64 to 106 · score 0.64 · squared speckle contrast, coherence factor, autocorrelation, exposure, SCOS · Report a mismatch
  2. [2] § Methods › Evaluation of Pulsatile CBF Recovery ↔ mc_analysis/fitting/correlation_metrics.py, lines 238 to 246 · score 0.60 · rCBF, rSBF, ground truth, correlation, BFi, scalp · Report a mismatch
  3. [3] § Methods › Experimental Validation of the Simulation Stack › In vitro benchmarking ↔ mc_analysis/fitting/fit_funcs.py, lines 9 to 40 · score 0.58 · reduced scattering coefficient, absorption coefficient, R1, refractive, fitted, BFi · Report a mismatch
  4. [4] § Results › Recovery of Pulsatile BFi ↔ mc_analysis/fitting/correlation_metrics.py, lines 238 to 246 · score 0.57 · rCBF, rSBF, ground truth, correlation, pulse, BFi · Report a mismatch
  5. [5] § Methods › Simulation Setup ↔ mc_analysis/fitting/fit_funcs.py, lines 9 to 40 · score 0.54 · reduced scattering coefficients, absorption coefficients, wavelength, BFi · Report a mismatch
  6. [6] § Results › Effects of ToF, SDS, and Acquisition Frequency on Brain Sensitivity in the Multilayer Slab Model ↔ mc_analysis/examples/plot_lag_integrated_flow_sensitivity_vs_lag.py, lines 115 to 154 · score 0.50 · source detector separations, Optical properties, Brain sensitivity, configurations, lags, SDS · Report a mismatch
  7. [7] § Methods › Evaluation of Pulsatile CBF Recovery ↔ mc_analysis/fitting/correlation_metrics.py, lines 297 to 331 · score 0.50 · rCBF, rSBF, metric, correlation, scalp, brain · Report a mismatch

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 373 lines · 14 KB · MIT · 3 matches

  1. """Collection of functions to evaluate brain sensitivity selectivity and crossing ToFs
  2. """
  3. from dataclasses import dataclass
  4. from typing import Optional
  5. import numpy as np
  6. from scipy.ndimage import gaussian_filter1d
  7. from scipy.signal import find_peaks
  8. @dataclass
  9. class Metric:
  10. corr_per_pulse_brain: np.ndarray
  11. corr_per_pulse_scalp: np.ndarray
  12. rmse_norm_brain: np.ndarray
  13. rmse_norm_scalp: np.ndarray
  14. bfi: np.ndarray
  15. tof: np.ndarray = None
  16. sds: int = None
  17. file_stem: str = None
  18. fit_method: str = None
  19. db_c: Optional[np.ndarray] = None
  20. db_s: Optional[np.ndarray] = None
  21. def get_rms_error(rcbf_estimate, rcbf_truth):
  22. error = rcbf_estimate - rcbf_truth
  23. rms_error = np.sqrt(np.sum(error**2, axis=1) / len(error))
  24. return error, rms_error
  25. def get_relative_cbf(cbf, cbf_baseline=None):
  26. """Definition of rCBF used in Zhao et al. https://doi.org/10.1364/BOE.438303
  27. rCBFitrue = (CBFitrue − CBFi0)/CBFi0 × 100%,
  28. rCBFiestimated = (CBFiestimated − CBFiestimated,0)/CBFiestimated,0 × 100%
  29. """
  30. if cbf_baseline is None:
  31. cbf_baseline = cbf[0]
  32. rcbf = (cbf - cbf_baseline) / cbf_baseline
  33. return rcbf
  34. def get_crossing_time(
  35. rms_error_brain,
  36. rms_error_scalp,
  37. tof_centered,
  38. i_tof_min=2,
  39. i_tof_max=None,
  40. is_rms_error=True,
  41. ):
  42. diff = rms_error_brain - rms_error_scalp
  43. n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
  44. if n_crossings > 1:
  45. print("Warning - more than one crossing detected")
  46. diff = gaussian_filter1d(rms_error_brain, 1) - gaussian_filter1d(rms_error_scalp, 1)
  47. n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
  48. indices_crossing_tof = np.argwhere((np.diff(np.sign(diff[i_tof_min:i_tof_max])) != 0)) + i_tof_min
  49. indices_crossing_tof = indices_crossing_tof.flatten()
  50. if len(indices_crossing_tof) == 0:
  51. crossing_time = np.nan
  52. else:
  53. # Neglect crossing ToF if RMS error is greater than 1
  54. if is_rms_error:
  55. mask = rms_error_brain[indices_crossing_tof] < 1.0
  56. else:
  57. mask = np.ones(len(indices_crossing_tof), dtype=bool)
  58. if np.sum(mask) == 0:
  59. crossing_time = np.nan
  60. else:
  61. index_min = int(np.min(indices_crossing_tof[mask]))
  62. crossing_time = tof_centered[index_min]
  63. return crossing_time, n_crossings
  64. def get_crossing_tof_correlation(corr_scalp, corr_brain, tof_centered, i_tof_min=2, i_tof_max=None):
  65. diff = corr_scalp - corr_brain
  66. if np.sum(diff[:4]) < 0:
  67. print("Warning - negative difference detected at early ToF - check corr_brain and corr_scalp ordering correct.")
  68. n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
  69. if n_crossings > 1:
  70. print("Warning - more than one crossing detected")
  71. diff = gaussian_filter1d(corr_scalp, 1) - gaussian_filter1d(corr_brain, 1)
  72. n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
  73. indices_crossing_tof = np.argwhere((np.diff(np.sign(diff[i_tof_min:i_tof_max])) != 0)) + i_tof_min
  74. indices_crossing_tof = indices_crossing_tof.flatten()
  75. if len(indices_crossing_tof) == 0:
  76. crossing_time = np.nan
  77. else:
  78. # Neglect crossing ToF if RMS error is greater than 1
  79. index_min = int(np.min(indices_crossing_tof))
  80. crossing_time = tof_centered[index_min]
  81. return crossing_time, n_crossings
  82. def get_crossing_tof_rmse(rmse_brain, rmse_scalp, tofs, i_tof_min=2, i_tof_max=None):
  83. """Function to get the 'tof' at which the RMSE of the brain and scalp waveforms cross.
  84. Args:
  85. rmse_brain: array, RMSE error brain
  86. rmse_scalp: array, RMSE error scalp
  87. tofs: array, ToF centered
  88. i_tof_min: int, minimum ToF index for search space
  89. i_tof_max: int, maximum ToF index for search space
  90. Returns:
  91. crossing_tof: float, crossing time
  92. n_crossings: int, number of crossings
  93. """
  94. diff = rmse_brain - rmse_scalp
  95. if np.sum(diff[:4]) < 0:
  96. print("Warning - negative difference detected at early ToF - check rmse_brain and rmse_scalp ordering correct.")
  97. n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
  98. if n_crossings > 1:
  99. print("Warning - more than one crossing detected, applying smoothing.")
  100. diff = gaussian_filter1d(rmse_brain, 1) - gaussian_filter1d(rmse_scalp, 1)
  101. n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
  102. # Find crossing tof indices
  103. indices_crossing_tof = np.argwhere((np.diff(np.sign(diff[i_tof_min:i_tof_max])) != 0)) + i_tof_min
  104. indices_crossing_tof = indices_crossing_tof.flatten()
  105. if len(indices_crossing_tof) == 0:
  106. crossing_tof = np.nan
  107. else:
  108. # Neglect crossing ToF if RMS error is greater than 1
  109. mask = rmse_brain[indices_crossing_tof] < 1.0
  110. if np.sum(mask) == 0:
  111. crossing_tof = np.nan
  112. else:
  113. # select the first crossing tof
  114. index_min = int(np.min(indices_crossing_tof[mask]))
  115. crossing_tof = tofs[index_min]
  116. return crossing_tof, n_crossings
  117. def get_number_crossings(diff):
  118. return np.sum(np.diff(np.sign(diff)) != 0)
  119. def normalise(bfi, allow_nan=False):
  120. _initial_shape = bfi.shape
  121. if bfi.ndim == 1:
  122. bfi = bfi.reshape(1, -1)
  123. if np.any(np.isnan(bfi)) and not allow_nan:
  124. raise ValueError("bfi contains NaNs")
  125. bfi = bfi - np.nanmin(bfi, axis=1)[:, None]
  126. bfi = bfi / np.nanmax(bfi, axis=1)[:, None]
  127. return bfi.reshape(_initial_shape)
  128. def compute_normalised_rmse(bfi_estimate, bfi_truth):
  129. """Compute the normalised RMSE. RMSE of waveforms whose amplitdues are normalised from 0 to 1
  130. Args:
  131. bfi_estimate: array, BFI estimate
  132. bfi_truth: array, BFI truth
  133. Returns:
  134. rms_error_cbf: array, RMS error
  135. """
  136. if bfi_estimate.ndim != 2:
  137. raise ValueError(f"bfi_estimate should be 2D, got {bfi_estimate.shape}")
  138. bfi_est_norm = normalise(bfi_estimate, allow_nan=True)
  139. bfi_est_truth = normalise(bfi_truth, allow_nan=True)
  140. error, rms_error_cbf = get_rms_error(bfi_est_norm, bfi_est_truth)
  141. return rms_error_cbf
  142. def _get_per_pulse_correlation(bfi_estimate, i_end_list, i_start_list, rcbf_truth, rsbf_truth):
  143. """Compute the correlation of bfi with ground truth SBF and CBF per pulse then take the average.
  144. Args:
  145. bfi_estimate: array, BFI estimate
  146. i_end_list: list, list of end indices for each pulse
  147. i_start_list: list, list of start indices for each pulse
  148. rcbf_truth: array, ground truth relative CBF
  149. rsbf_truth: array, ground truth relative SBF
  150. Returns:
  151. corr_scalp: array, correlation with SBF
  152. corr_brain: array, correlation with CBF
  153. """
  154. corr_brain = np.empty(bfi_estimate.shape[0])
  155. corr_scalp = np.empty(bfi_estimate.shape[0])
  156. depth_corr = np.empty(bfi_estimate.shape[0])
  157. for itof in range(bfi_estimate.shape[0]):
  158. rms_brain_seg_corrs = []
  159. rms_scalp_seg_corrs = []
  160. for (i_start, i_end) in zip(i_start_list, i_end_list):
  161. try:
  162. rms_brain_seg_corrs.append(
  163. np.corrcoef(
  164. np.asarray(rcbf_truth)[i_start:i_end],
  165. np.asarray(bfi_estimate)[itof, i_start:i_end],
  166. )[0, 1]
  167. )
  168. rms_scalp_seg_corrs.append(
  169. np.corrcoef(
  170. np.asarray(rsbf_truth)[i_start:i_end],
  171. np.asarray(bfi_estimate)[itof, i_start:i_end],
  172. )[0, 1]
  173. )
  174. except ValueError:
  175. print(i_start, i_end, rms_scalp_seg_corrs, rms_brain_seg_corrs)
  176. print(
  177. np.corrcoef(
  178. np.asarray(rcbf_truth)[i_start:i_end],
  179. bfi_estimate[itof, i_start:i_end],
  180. )[0, 1]
  181. )
  182. print(
  183. np.corrcoef(
  184. np.asarray(rsbf_truth)[i_start:i_end],
  185. bfi_estimate[itof, i_start:i_end],
  186. )[0, 1]
  187. )
  188. print(
  189. "Error in sequence",
  190. np.asarray(rcbf_truth).shape,
  191. np.asarray(rsbf_truth).shape,
  192. np.asarray(bfi_estimate).shape,
  193. )
  194. corr_brain[itof] = np.mean(rms_brain_seg_corrs)
  195. corr_scalp[itof] = np.mean(rms_scalp_seg_corrs)
  196. depth_corr[itof] = (1 - np.abs(corr_scalp[itof])) / (1 - np.abs(corr_brain[itof]))
  197. return corr_scalp, corr_brain
  198. def get_per_pulse_correlation(bfi_estimate, rcbf_truth, rsbf_truth, peak_indices=None):
  199. """Get correlation per/pulse of bfi with ground truth rCBF and rSBF."""
  200. if peak_indices is None:
  201. peak_indices = segment_bfi(rcbf_truth)
  202. i_end_list = peak_indices[0][1:]
  203. i_start_list = peak_indices[0][:-1]
  204. corr_scalp, corr_brain = _get_per_pulse_correlation(bfi_estimate, i_end_list, i_start_list, rcbf_truth, rsbf_truth)
  205. return corr_scalp, corr_brain
  206. def segment_bfi(ground_truth):
  207. """Segment ground truth BFI into pulses.
  208. Only tested for ground truth simulation data with clean peaks. Will likely brake for real data.
  209. Args:
  210. ground_truth: array, ground truth BFI
  211. Returns:
  212. index_peaks: list, list of pulse peak indices
  213. """
  214. # smooth
  215. bfi_filt = gaussian_filter1d(ground_truth, sigma=10)
  216. index_peaks = find_peaks(-1 * bfi_filt, distance=50)
  217. return index_peaks
  218. def get_normalised_metrics(adb, brain_db, scalp_db, peak_indices=None):
  219. n_time = adb.shape[1]
  220. rcbf_truth = get_relative_cbf(brain_db)[:n_time]
  221. rsbf_truth = get_relative_cbf(scalp_db)[:n_time]
  222. _mask = None
  223. if np.any(adb[:, 0] == 0):
  224. _mask = adb[:, 0] == 0.0
  225. print("Warning - First time point of ADB is zero - this will cause division by zero")
  226. print(np.sum(adb == 0.0), np.sum(np.isnan(adb)))
  227. if adb.ndim == 3 and adb.shape[0] == 1 and adb.shape[-1] == 1:
  228. bfi_estimate = get_relative_cbf(adb[:, :, 0], cbf_baseline=adb[:, 0, :])
  229. elif adb.ndim == 2 and adb.shape[0] == 1:
  230. bfi_estimate = get_relative_cbf(adb[:, :], cbf_baseline=adb[:, 0])
  231. elif adb.ndim == 1:
  232. bfi_estimate = get_relative_cbf(np.squeeze(adb), cbf_baseline=np.squeeze(adb)[:, 0, None])
  233. else:
  234. bfi_estimate = get_relative_cbf(np.squeeze(adb), cbf_baseline=np.squeeze(adb)[:, 0, None])
  235. if bfi_estimate.ndim != 2:
  236. raise ValueError(f"bfi_estimate should be 2D, got {bfi_estimate.shape}")
  237. corr_per_pulse_scalp, corr_per_pulse_brain = get_per_pulse_correlation(
  238. bfi_estimate, rcbf_truth, rsbf_truth, peak_indices=peak_indices
  239. )
  240. rmse_norm_brain = compute_normalised_rmse(bfi_estimate, rcbf_truth)
  241. rmse_norm_scalp = compute_normalised_rmse(bfi_estimate, rsbf_truth)
  242. if _mask is not None:
  243. rmse_norm_brain[_mask] = np.nan
  244. rmse_norm_scalp[_mask] = np.nan
  245. corr_per_pulse_scalp[_mask] = np.nan
  246. corr_per_pulse_brain[_mask] = np.nan
  247. return corr_per_pulse_brain, corr_per_pulse_scalp, rmse_norm_brain, rmse_norm_scalp
  248. def get_td_metrics(bfi, brain_db, scalp_db, return_metric_obj=False):
  249. """Get time domain metrics. - Correlation with rCBF/rSBF and normalised RMSE with rCBF/rSBF
  250. Args:
  251. bfi: array, BFI estimate
  252. brain_db: array, ground truth brain DB
  253. scalp_db: array, ground truth scalp DB
  254. return_metric_obj: bool, if True return Metric object
  255. Returns:
  256. either metric object
  257. or..
  258. corr_per_pulse_brain: array, correlation with rCBF
  259. corr_per_pulse_scalp: array, correlation with rSBF
  260. rmse_norm_brain: array, normalised RMSE with rCBF
  261. rmse_norm_scalp: array, normalised RMSE with rSBF
  262. """
  263. (
  264. corr_per_pulse_brain,
  265. corr_per_pulse_scalp,
  266. rmse_norm_brain,
  267. rmse_norm_scalp,
  268. ) = get_normalised_metrics(bfi, brain_db, scalp_db, peak_indices=None)
  269. if return_metric_obj:
  270. return Metric(
  271. corr_per_pulse_brain=corr_per_pulse_brain,
  272. corr_per_pulse_scalp=corr_per_pulse_scalp,
  273. rmse_norm_brain=rmse_norm_brain,
  274. rmse_norm_scalp=rmse_norm_scalp,
  275. bfi=bfi,
  276. db_c=brain_db,
  277. db_s=scalp_db,
  278. )
  279. return corr_per_pulse_brain, corr_per_pulse_scalp, rmse_norm_brain, rmse_norm_scalp
  280. def get_cw_metrics(bfi_cw, brain_db, scalp_db, return_metric_obj=False):
  281. """Get continuous wave metrics. - Correlation with groundtruth rCBF/rSBF
  282. and normalised RMSE with rCBF/rSBF
  283. Args:
  284. bfi_cw: array, BFI estimate
  285. brain_db: array, ground truth brain DB
  286. scalp_db: array, ground truth scalp DB
  287. return_metric_obj: bool, if True return Metric object
  288. Returns:
  289. either metric object
  290. or..
  291. corr_per_pulse_brain_cw: float, correlation with rCBF
  292. corr_per_pulse_scalp_cw: float, correlation with rSBF
  293. rmse_norm_brain_cw: float, normalised RMSE with rCBF
  294. rmse_norm_scalp_cw: float, normalised RMSE with rSBF
  295. """
  296. (
  297. corr_per_pulse_brain_cw,
  298. corr_per_pulse_scalp_cw,
  299. rmse_norm_brain_cw,
  300. rmse_norm_scalp_cw,
  301. ) = get_normalised_metrics(np.tile(bfi_cw, (2, 1)), brain_db, scalp_db, peak_indices=None)
  302. if return_metric_obj:
  303. return Metric(
  304. corr_per_pulse_brain=corr_per_pulse_brain_cw,
  305. corr_per_pulse_scalp=corr_per_pulse_scalp_cw,
  306. rmse_norm_brain=rmse_norm_brain_cw,
  307. rmse_norm_scalp=rmse_norm_scalp_cw,
  308. bfi=bfi_cw,
  309. db_c=brain_db,
  310. db_s=scalp_db,
  311. )
  312. return (
  313. corr_per_pulse_brain_cw[0],
  314. corr_per_pulse_scalp_cw[0],
  315. rmse_norm_brain_cw[0],
  316. rmse_norm_scalp_cw[0],
  317. )

correlation_metrics.py at commit dc8dea1, under MIT · at the source

Overview

Authors: Dominic W. Hill1, Anurag Behera1, Octave Etard1, Yuqian Zhang1, Alexandra Tran-Van-Minh1, Alexander Ruesch1, Lior Ness1, Chloe Maine1, Amir Salehi Lashkajani1, Veronika Parfentyeva1, Navjit Singh1, Alexander Antrobus1, Rachel Prudden1, Pablo Villar Sanjurjo1, Stella Avtzi1, Tanja Dragojević1, Dawid Borycki1, Matthew T. Valley1, Robert J. Cooper1,2
  1. CoMind Technologies, London, United Kingdom
  2. UCL, Department of Medical Physics and Biomedical Engineering, Biomedical Optics Research Laboratory, London, United Kingdom
Journal: Neurophotonics, volume 13, issue 2, article 025003
Dates: received 20 August 2025; accepted 3 December 2025; published online 30 March 2026; in print April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1117/1.nph.13.2.025003 · PMID 41930223 · PMCID PMC13041720 · OpenAlex W7143279539
Open access: gold, a free copy (OpenAlex)
Status: code verified (how the code is found and verified)
Categories: computational modeling (no new data) (modality), none (in silico) (organism), computational (subfield)
Methods: Connectivity, Spectral & time-frequency, Graphs
Keywords: Monte Carlo simulation, cerebral blood flow, time-resolved, diffuse correlation spectroscopy, speckle contrast optical spectroscopy
Topic: Optical Imaging and Spectroscopy Techniques (Radiology, Nuclear Medicine and Imaging, Medicine), according to OpenAlex
Citations: not cited yet (Europe PMC); 87 references in the paper

Abstract

Significance: Achieving adequate brain sensitivity remains a significant obstacle to noninvasive optical measurements of pulsatile cerebral blood flow. Increasing source-detector separation (SDS), utilizing time-of-flight (ToF) information, and/or increasing acquisition frequency can increase brain sensitivity. However, optimizing these parameters is nontrivial and must balance the need to achieve sufficient signal-to-noise ratio.

Aim: We aim to guide hardware optimization by evaluating the benefits of ToF gating, autocorrelation time-lag gating, and increased source-detector separation in recovering pulsatile cerebral blood flow.

Approach: Monte Carlo simulations of ToF-resolved pulsatile blood flow index at 1064 nm were performed. A simulation and analysis pipeline is presented and validated against phantom and in vivo measurements.

Results: Brain sensitivity deteriorates for autocorrelation time-lags >1 to 10 μs. Continuous-wave diffuse correlation spectroscopy (CW-DCS) at 40 mm SDS achieves equivalent brain sensitivity to time-resolved measurements with a 1.2 ns ToF gate. For median adult brain depths, a 31 mm SDS (CW-DCS), 35 mm SDS (speckle contrast optical spectroscopy), or 0.9 ns ToF are needed for brain-dominated signals. For the 85th percentile of brain depth, these thresholds increase to 40 mm, 46 mm, and 1.2 ns, respectively.

Conclusions: We quantitatively compare source-detector separation, ToF gating, and acquisition frequency and identify crucial performance requirements for clinically viable optical monitors of cerebral blood flow.

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 7 matches between paragraphs and lines of code.

CoMind-Technologies/mc_analysis

License: MIT
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: dc8dea1d08ad874d9df7ba8a5a42baa39ec1afa6, 25 March 2026
Languages: Python (28)
Size: 49 files, 28 scripts
Software Heritage: not archived
Found in: “Code and Data Availability”
Holds: README, license file, environment (pyproject.toml), tests
Not found: CITATION.cff, continuous integration, documentation
Tools: NumPy (18 files), SciPy (7 files), pandas (6 files), Matplotlib (5 files), h5py (2 files)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
30 files

The paper's code and data availability statement is in the Data section.

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;
  • 28 scripts, each with its path and the digest of its content;
  • 7 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.

Code and Data Availability

Code used to produce pulsatile simulations and a subset of figures presented in this paper is available at: https://github.com/CoMind-Technologies/mc_analysis. Data underlying the results presented in this paper are not publicly available at this time but may be obtained from the authors upon reasonable request.

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, 19 authors, 5 keywords, 82 references.

Cite

This paper

Hill, D. W., Behera, A., Etard, O., Zhang, Y., Tran-Van-Minh, A., Ruesch, A., Ness, L., Maine, C., Salehi Lashkajani, A., Parfentyeva, V., Singh, N., Antrobus, A., Prudden, R., Sanjurjo, P. V., Avtzi, S., Dragojević, T., Borycki, D., Valley, M. T., & Cooper, R. J. (2026). Monte Carlo simulations of time-resolved blood flow index: times-of-flight beyond ∼1 ns are necessary for brain-dominated measurements. Neurophotonics, 13(2), 025003. https://doi.org/10.1117/1.nph.13.2.025003

BibTeX

@article{hill2026monte,
author = {Hill, Dominic W. and Behera, Anurag and Etard, Octave and Zhang, Yuqian and Tran-Van-Minh, Alexandra and Ruesch, Alexander and Ness, Lior and Maine, Chloe and Salehi Lashkajani, Amir and Parfentyeva, Veronika and Singh, Navjit and Antrobus, Alexander and Prudden, Rachel and Sanjurjo, Pablo Villar and Avtzi, Stella and Dragojević, Tanja and Borycki, Dawid and Valley, Matthew T. and Cooper, Robert J.},
title = {{Monte Carlo simulations of time-resolved blood flow index: times-of-flight beyond ∼1 ns are necessary for brain-dominated measurements}},
journal = {Neurophotonics},
year = {2026},
month = mar,
volume = {13},
number = {2},
pages = {025003},
publisher = {Society of Photo-Optical Instrumentation Engineers},
issn = {2329-423X},
doi = {10.1117/1.nph.13.2.025003},
url = {https://doi.org/10.1117/1.nph.13.2.025003},
pmid = {41930223},
pmcid = {PMC13041720}
}

RIS

TY - JOUR
AU - Hill, Dominic W.
AU - Behera, Anurag
AU - Etard, Octave
AU - Zhang, Yuqian
AU - Tran-Van-Minh, Alexandra
AU - Ruesch, Alexander
AU - Ness, Lior
AU - Maine, Chloe
AU - Salehi Lashkajani, Amir
AU - Parfentyeva, Veronika
AU - Singh, Navjit
AU - Antrobus, Alexander
AU - Prudden, Rachel
AU - Sanjurjo, Pablo Villar
AU - Avtzi, Stella
AU - Dragojević, Tanja
AU - Borycki, Dawid
AU - Valley, Matthew T.
AU - Cooper, Robert J.
TI - Monte Carlo simulations of time-resolved blood flow index: times-of-flight beyond ∼1 ns are necessary for brain-dominated measurements
T2 - Neurophotonics
J2 - Neurophotonics
PY - 2026
DA - 2026/03/30
VL - 13
IS - 2
SP - 025003
SN - 2329-423X
PB - Society of Photo-Optical Instrumentation Engineers
DO - 10.1117/1.nph.13.2.025003
UR - https://doi.org/10.1117/1.nph.13.2.025003
LA - en
ER -

CSL-JSON

{
"id": "10.1117/1.nph.13.2.025003",
"type": "article-journal",
"title": "Monte Carlo simulations of time-resolved blood flow index: times-of-flight beyond ∼1 ns are necessary for brain-dominated measurements",
"container-title": "Neurophotonics",
"author": [
{
"family": "Hill",
"given": "Dominic W."
},
{
"family": "Behera",
"given": "Anurag"
},
{
"family": "Etard",
"given": "Octave"
},
{
"family": "Zhang",
"given": "Yuqian"
},
{
"family": "Tran-Van-Minh",
"given": "Alexandra"
},
{
"family": "Ruesch",
"given": "Alexander"
},
{
"family": "Ness",
"given": "Lior"
},
{
"family": "Maine",
"given": "Chloe"
},
{
"family": "Salehi Lashkajani",
"given": "Amir"
},
{
"family": "Parfentyeva",
"given": "Veronika"
},
{
"family": "Singh",
"given": "Navjit"
},
{
"family": "Antrobus",
"given": "Alexander"
},
{
"family": "Prudden",
"given": "Rachel"
},
{
"family": "Sanjurjo",
"given": "Pablo Villar"
},
{
"family": "Avtzi",
"given": "Stella"
},
{
"family": "Dragojević",
"given": "Tanja"
},
{
"family": "Borycki",
"given": "Dawid"
},
{
"family": "Valley",
"given": "Matthew T."
},
{
"family": "Cooper",
"given": "Robert J."
}
],
"container-title-short": "Neurophotonics",
"volume": "13",
"issue": "2",
"page": "025003",
"DOI": "10.1117/1.nph.13.2.025003",
"PMID": "41930223",
"PMCID": "PMC13041720",
"ISSN": "2329-423X",
"publisher": "Society of Photo-Optical Instrumentation Engineers",
"URL": "https://doi.org/10.1117/1.nph.13.2.025003",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
30
]
]
}
}

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.2.025002
CoMind R1: a time-resolved interferometric optical neuromonitoring system for pulsatile cerebral blood flow measurement at late times-of-flight.
Journal: Neurophotonics
In common: 24 references, 3 authors
[2] doi:10.1117/1.nph.9.s2.s24001 [code]
Optical imaging and spectroscopy for the study of the human brain: status report.
Journal: Neurophotonics
In common: pandas, SciPy, Matplotlib, 1 other tool, 22 references
[3] doi:10.1117/1.nph.13.2.025008
Comparative validation of speckle contrast optical spectroscopy against diffuse correlation spectroscopy for monitoring human cerebral blood flow.
Journal: Neurophotonics
In common: 21 references
[4] doi:10.1117/1.nph.12.3.035008
Cerebral blood flow monitoring using a deep learning implementation of the two-layer diffuse correlation spectroscopy analytical model with a 512 × 512 SPAD array.
Journal: Neurophotonics
In common: 18 references
[5] doi:10.1117/1.nph.12.2.025007 [code]
Beneath the surface: revealing deep-tissue blood flow in human subjects with massively parallelized diffuse correlation spectroscopy.
Journal: Neurophotonics
In common: 17 references
[6] doi:10.1117/1.nph.13.2.025005
Depth-sensitive cerebral blood flow and low-frequency oscillations for consciousness assessment using time-domain diffuse correlation spectroscopy.
Journal: Neurophotonics
In common: 13 references
[7] doi:10.1117/1.nph.10.1.015010 [code]
Influence of oversimplifying the head anatomy on cerebral blood flow measurements with diffuse correlation spectroscopy.
Journal: Neurophotonics
In common: 13 references
[8] doi:10.1038/s42003-025-08915-x [code]
Mapping human cerebral blood flow with high-density, multi-channel speckle contrast optical spectroscopy.
Journal: Communications biology
In common: h5py, pandas, SciPy, 2 other tools, 9 references
[9] doi:10.1109/jstqe.2025.3581407 [code]
Validation of the Linearity in Image Reconstruction Methods for Speckle Contrast Optical Tomography.
Journal: IEEE journal of selected topics in quantum electronics : a publication of the IEEE Lasers and Electro-optics Society
In common: 10 references
[10] doi:10.1117/1.nph.10.2.025013
Using depth-enhanced diffuse correlation spectroscopy and near-infrared spectroscopy to isolate cerebral hemodynamics during transient hypotension.
Journal: Neurophotonics
In common: 10 references

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.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.1117/1.nph.13.2.025003.svg)](https://openscicode.org/badge/doi/10.1117/1.nph.13.2.025003)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.1117/1.nph.13.2.025003.svg
   :target: https://openscicode.org/badge/doi/10.1117/1.nph.13.2.025003
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1117/1.nph.13.2.025003"><img src="https://openscicode.org/badge/doi/10.1117/1.nph.13.2.025003.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge 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: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.

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