Monte Carlo simulations of time-resolved blood flow index: times-of-flight beyond ∼1 ns are necessary for brain-dominated measurements.
The 7 matches
- [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] § 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] § 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] § 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] § 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] § 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] § 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
- """Collection of functions to evaluate brain sensitivity selectivity and crossing ToFs
- """
- from dataclasses import dataclass
- from typing import Optional
- import numpy as np
- from scipy.ndimage import gaussian_filter1d
- from scipy.signal import find_peaks
- @dataclass
- class Metric:
- corr_per_pulse_brain: np.ndarray
- corr_per_pulse_scalp: np.ndarray
- rmse_norm_brain: np.ndarray
- rmse_norm_scalp: np.ndarray
- bfi: np.ndarray
- tof: np.ndarray = None
- sds: int = None
- file_stem: str = None
- fit_method: str = None
- db_c: Optional[np.ndarray] = None
- db_s: Optional[np.ndarray] = None
- def get_rms_error(rcbf_estimate, rcbf_truth):
- error = rcbf_estimate - rcbf_truth
- rms_error = np.sqrt(np.sum(error**2, axis=1) / len(error))
- return error, rms_error
- def get_relative_cbf(cbf, cbf_baseline=None):
- """Definition of rCBF used in Zhao et al. https://doi.org/10.1364/BOE.438303
- rCBFitrue = (CBFitrue − CBFi0)/CBFi0 × 100%,
- rCBFiestimated = (CBFiestimated − CBFiestimated,0)/CBFiestimated,0 × 100%
- """
- if cbf_baseline is None:
- cbf_baseline = cbf[0]
- rcbf = (cbf - cbf_baseline) / cbf_baseline
- return rcbf
- def get_crossing_time(
- rms_error_brain,
- rms_error_scalp,
- tof_centered,
- i_tof_min=2,
- i_tof_max=None,
- is_rms_error=True,
- ):
- diff = rms_error_brain - rms_error_scalp
- n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
- if n_crossings > 1:
- print("Warning - more than one crossing detected")
- diff = gaussian_filter1d(rms_error_brain, 1) - gaussian_filter1d(rms_error_scalp, 1)
- n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
- indices_crossing_tof = np.argwhere((np.diff(np.sign(diff[i_tof_min:i_tof_max])) != 0)) + i_tof_min
- indices_crossing_tof = indices_crossing_tof.flatten()
- if len(indices_crossing_tof) == 0:
- crossing_time = np.nan
- else:
- # Neglect crossing ToF if RMS error is greater than 1
- if is_rms_error:
- mask = rms_error_brain[indices_crossing_tof] < 1.0
- else:
- mask = np.ones(len(indices_crossing_tof), dtype=bool)
- if np.sum(mask) == 0:
- crossing_time = np.nan
- else:
- index_min = int(np.min(indices_crossing_tof[mask]))
- crossing_time = tof_centered[index_min]
- return crossing_time, n_crossings
- def get_crossing_tof_correlation(corr_scalp, corr_brain, tof_centered, i_tof_min=2, i_tof_max=None):
- diff = corr_scalp - corr_brain
- if np.sum(diff[:4]) < 0:
- print("Warning - negative difference detected at early ToF - check corr_brain and corr_scalp ordering correct.")
- n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
- if n_crossings > 1:
- print("Warning - more than one crossing detected")
- diff = gaussian_filter1d(corr_scalp, 1) - gaussian_filter1d(corr_brain, 1)
- n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
- indices_crossing_tof = np.argwhere((np.diff(np.sign(diff[i_tof_min:i_tof_max])) != 0)) + i_tof_min
- indices_crossing_tof = indices_crossing_tof.flatten()
- if len(indices_crossing_tof) == 0:
- crossing_time = np.nan
- else:
- # Neglect crossing ToF if RMS error is greater than 1
- index_min = int(np.min(indices_crossing_tof))
- crossing_time = tof_centered[index_min]
- return crossing_time, n_crossings
- def get_crossing_tof_rmse(rmse_brain, rmse_scalp, tofs, i_tof_min=2, i_tof_max=None):
- """Function to get the 'tof' at which the RMSE of the brain and scalp waveforms cross.
- Args:
- rmse_brain: array, RMSE error brain
- rmse_scalp: array, RMSE error scalp
- tofs: array, ToF centered
- i_tof_min: int, minimum ToF index for search space
- i_tof_max: int, maximum ToF index for search space
- Returns:
- crossing_tof: float, crossing time
- n_crossings: int, number of crossings
- """
- diff = rmse_brain - rmse_scalp
- if np.sum(diff[:4]) < 0:
- print("Warning - negative difference detected at early ToF - check rmse_brain and rmse_scalp ordering correct.")
- n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
- if n_crossings > 1:
- print("Warning - more than one crossing detected, applying smoothing.")
- diff = gaussian_filter1d(rmse_brain, 1) - gaussian_filter1d(rmse_scalp, 1)
- n_crossings = get_number_crossings(diff[i_tof_min:i_tof_max])
- # Find crossing tof indices
- indices_crossing_tof = np.argwhere((np.diff(np.sign(diff[i_tof_min:i_tof_max])) != 0)) + i_tof_min
- indices_crossing_tof = indices_crossing_tof.flatten()
- if len(indices_crossing_tof) == 0:
- crossing_tof = np.nan
- else:
- # Neglect crossing ToF if RMS error is greater than 1
- mask = rmse_brain[indices_crossing_tof] < 1.0
- if np.sum(mask) == 0:
- crossing_tof = np.nan
- else:
- # select the first crossing tof
- index_min = int(np.min(indices_crossing_tof[mask]))
- crossing_tof = tofs[index_min]
- return crossing_tof, n_crossings
- def get_number_crossings(diff):
- return np.sum(np.diff(np.sign(diff)) != 0)
- def normalise(bfi, allow_nan=False):
- _initial_shape = bfi.shape
- if bfi.ndim == 1:
- bfi = bfi.reshape(1, -1)
- if np.any(np.isnan(bfi)) and not allow_nan:
- raise ValueError("bfi contains NaNs")
- bfi = bfi - np.nanmin(bfi, axis=1)[:, None]
- bfi = bfi / np.nanmax(bfi, axis=1)[:, None]
- return bfi.reshape(_initial_shape)
- def compute_normalised_rmse(bfi_estimate, bfi_truth):
- """Compute the normalised RMSE. RMSE of waveforms whose amplitdues are normalised from 0 to 1
- Args:
- bfi_estimate: array, BFI estimate
- bfi_truth: array, BFI truth
- Returns:
- rms_error_cbf: array, RMS error
- """
- if bfi_estimate.ndim != 2:
- raise ValueError(f"bfi_estimate should be 2D, got {bfi_estimate.shape}")
- bfi_est_norm = normalise(bfi_estimate, allow_nan=True)
- bfi_est_truth = normalise(bfi_truth, allow_nan=True)
- error, rms_error_cbf = get_rms_error(bfi_est_norm, bfi_est_truth)
- return rms_error_cbf
- def _get_per_pulse_correlation(bfi_estimate, i_end_list, i_start_list, rcbf_truth, rsbf_truth):
- """Compute the correlation of bfi with ground truth SBF and CBF per pulse then take the average.
- Args:
- bfi_estimate: array, BFI estimate
- i_end_list: list, list of end indices for each pulse
- i_start_list: list, list of start indices for each pulse
- rcbf_truth: array, ground truth relative CBF
- rsbf_truth: array, ground truth relative SBF
- Returns:
- corr_scalp: array, correlation with SBF
- corr_brain: array, correlation with CBF
- """
- corr_brain = np.empty(bfi_estimate.shape[0])
- corr_scalp = np.empty(bfi_estimate.shape[0])
- depth_corr = np.empty(bfi_estimate.shape[0])
- for itof in range(bfi_estimate.shape[0]):
- rms_brain_seg_corrs = []
- rms_scalp_seg_corrs = []
- for (i_start, i_end) in zip(i_start_list, i_end_list):
- try:
- rms_brain_seg_corrs.append(
- np.corrcoef(
- np.asarray(rcbf_truth)[i_start:i_end],
- np.asarray(bfi_estimate)[itof, i_start:i_end],
- )[0, 1]
- )
- rms_scalp_seg_corrs.append(
- np.corrcoef(
- np.asarray(rsbf_truth)[i_start:i_end],
- np.asarray(bfi_estimate)[itof, i_start:i_end],
- )[0, 1]
- )
- except ValueError:
- print(i_start, i_end, rms_scalp_seg_corrs, rms_brain_seg_corrs)
- print(
- np.corrcoef(
- np.asarray(rcbf_truth)[i_start:i_end],
- bfi_estimate[itof, i_start:i_end],
- )[0, 1]
- )
- print(
- np.corrcoef(
- np.asarray(rsbf_truth)[i_start:i_end],
- bfi_estimate[itof, i_start:i_end],
- )[0, 1]
- )
- print(
- "Error in sequence",
- np.asarray(rcbf_truth).shape,
- np.asarray(rsbf_truth).shape,
- np.asarray(bfi_estimate).shape,
- )
- corr_brain[itof] = np.mean(rms_brain_seg_corrs)
- corr_scalp[itof] = np.mean(rms_scalp_seg_corrs)
- depth_corr[itof] = (1 - np.abs(corr_scalp[itof])) / (1 - np.abs(corr_brain[itof]))
- return corr_scalp, corr_brain
- def get_per_pulse_correlation(bfi_estimate, rcbf_truth, rsbf_truth, peak_indices=None):
- """Get correlation per/pulse of bfi with ground truth rCBF and rSBF."""
- if peak_indices is None:
- peak_indices = segment_bfi(rcbf_truth)
- i_end_list = peak_indices[0][1:]
- i_start_list = peak_indices[0][:-1]
- corr_scalp, corr_brain = _get_per_pulse_correlation(bfi_estimate, i_end_list, i_start_list, rcbf_truth, rsbf_truth)
- return corr_scalp, corr_brain
- def segment_bfi(ground_truth):
- """Segment ground truth BFI into pulses.
- Only tested for ground truth simulation data with clean peaks. Will likely brake for real data.
- Args:
- ground_truth: array, ground truth BFI
- Returns:
- index_peaks: list, list of pulse peak indices
- """
- # smooth
- bfi_filt = gaussian_filter1d(ground_truth, sigma=10)
- index_peaks = find_peaks(-1 * bfi_filt, distance=50)
- return index_peaks
- def get_normalised_metrics(adb, brain_db, scalp_db, peak_indices=None):
- n_time = adb.shape[1]
- rcbf_truth = get_relative_cbf(brain_db)[:n_time]
- rsbf_truth = get_relative_cbf(scalp_db)[:n_time]
- _mask = None
- if np.any(adb[:, 0] == 0):
- _mask = adb[:, 0] == 0.0
- print("Warning - First time point of ADB is zero - this will cause division by zero")
- print(np.sum(adb == 0.0), np.sum(np.isnan(adb)))
- if adb.ndim == 3 and adb.shape[0] == 1 and adb.shape[-1] == 1:
- bfi_estimate = get_relative_cbf(adb[:, :, 0], cbf_baseline=adb[:, 0, :])
- elif adb.ndim == 2 and adb.shape[0] == 1:
- bfi_estimate = get_relative_cbf(adb[:, :], cbf_baseline=adb[:, 0])
- elif adb.ndim == 1:
- bfi_estimate = get_relative_cbf(np.squeeze(adb), cbf_baseline=np.squeeze(adb)[:, 0, None])
- else:
- bfi_estimate = get_relative_cbf(np.squeeze(adb), cbf_baseline=np.squeeze(adb)[:, 0, None])
- if bfi_estimate.ndim != 2:
- raise ValueError(f"bfi_estimate should be 2D, got {bfi_estimate.shape}")
- corr_per_pulse_scalp, corr_per_pulse_brain = get_per_pulse_correlation(
- bfi_estimate, rcbf_truth, rsbf_truth, peak_indices=peak_indices
- )
- rmse_norm_brain = compute_normalised_rmse(bfi_estimate, rcbf_truth)
- rmse_norm_scalp = compute_normalised_rmse(bfi_estimate, rsbf_truth)
- if _mask is not None:
- rmse_norm_brain[_mask] = np.nan
- rmse_norm_scalp[_mask] = np.nan
- corr_per_pulse_scalp[_mask] = np.nan
- corr_per_pulse_brain[_mask] = np.nan
- return corr_per_pulse_brain, corr_per_pulse_scalp, rmse_norm_brain, rmse_norm_scalp
- def get_td_metrics(bfi, brain_db, scalp_db, return_metric_obj=False):
- """Get time domain metrics. - Correlation with rCBF/rSBF and normalised RMSE with rCBF/rSBF
- Args:
- bfi: array, BFI estimate
- brain_db: array, ground truth brain DB
- scalp_db: array, ground truth scalp DB
- return_metric_obj: bool, if True return Metric object
- Returns:
- either metric object
- or..
- corr_per_pulse_brain: array, correlation with rCBF
- corr_per_pulse_scalp: array, correlation with rSBF
- rmse_norm_brain: array, normalised RMSE with rCBF
- rmse_norm_scalp: array, normalised RMSE with rSBF
- """
- (
- corr_per_pulse_brain,
- corr_per_pulse_scalp,
- rmse_norm_brain,
- rmse_norm_scalp,
- ) = get_normalised_metrics(bfi, brain_db, scalp_db, peak_indices=None)
- if return_metric_obj:
- return Metric(
- corr_per_pulse_brain=corr_per_pulse_brain,
- corr_per_pulse_scalp=corr_per_pulse_scalp,
- rmse_norm_brain=rmse_norm_brain,
- rmse_norm_scalp=rmse_norm_scalp,
- bfi=bfi,
- db_c=brain_db,
- db_s=scalp_db,
- )
- return corr_per_pulse_brain, corr_per_pulse_scalp, rmse_norm_brain, rmse_norm_scalp
- def get_cw_metrics(bfi_cw, brain_db, scalp_db, return_metric_obj=False):
- """Get continuous wave metrics. - Correlation with groundtruth rCBF/rSBF
- and normalised RMSE with rCBF/rSBF
- Args:
- bfi_cw: array, BFI estimate
- brain_db: array, ground truth brain DB
- scalp_db: array, ground truth scalp DB
- return_metric_obj: bool, if True return Metric object
- Returns:
- either metric object
- or..
- corr_per_pulse_brain_cw: float, correlation with rCBF
- corr_per_pulse_scalp_cw: float, correlation with rSBF
- rmse_norm_brain_cw: float, normalised RMSE with rCBF
- rmse_norm_scalp_cw: float, normalised RMSE with rSBF
- """
- (
- corr_per_pulse_brain_cw,
- corr_per_pulse_scalp_cw,
- rmse_norm_brain_cw,
- rmse_norm_scalp_cw,
- ) = get_normalised_metrics(np.tile(bfi_cw, (2, 1)), brain_db, scalp_db, peak_indices=None)
- if return_metric_obj:
- return Metric(
- corr_per_pulse_brain=corr_per_pulse_brain_cw,
- corr_per_pulse_scalp=corr_per_pulse_scalp_cw,
- rmse_norm_brain=rmse_norm_brain_cw,
- rmse_norm_scalp=rmse_norm_scalp_cw,
- bfi=bfi_cw,
- db_c=brain_db,
- db_s=scalp_db,
- )
- return (
- corr_per_pulse_brain_cw[0],
- corr_per_pulse_scalp_cw[0],
- rmse_norm_brain_cw[0],
- rmse_norm_scalp_cw[0],
- )
correlation_metrics.py at commit dc8dea1, under MIT · at the source
Overview
- CoMind Technologies, London, United Kingdom
- UCL, Department of Medical Physics and Biomedical Engineering, Biomedical Optics Research Laboratory, London, United Kingdom
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/
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
dc8dea1d08ad874d9df7ba8a5a42baa39ec1afa6, 25 March 2026Availability: 1 check, the latest on 30 September 2026: the link answers
- 30 September 2026: the link answers
30 files
- mc_analysis/
__init__.py , Python, 1 line - mc_analysis/
__main__.py , Python, 7 lines - mc_analysis/
examples/ , Python, 1 line__init__.py - mc_analysis/
examples/ , Python, 420 linesget_brain_sensitivity_an d_equivalent_tofs.py - mc_analysis/
examples/ , Python, 124 linesplot_bfi_correlation_vs_ tof.py - mc_analysis/
examples/ , Python, 168 linesplot_equivalent_tof.py - mc_analysis/
examples/ , Python, 238 lines, 1 matchplot_lag_integrated_flow _sensitivity_vs_lag.py - mc_analysis/
examples/ , Python, 266 linesrun_fitting.py - mc_analysis/
examples/ , Python, 304 linessimulate_pulsatile_time_ series.py - mc_analysis/
fitting/ , Python, 200 linesbfi_fitting.py - mc_analysis/
fitting/ , Python, 373 lines, 3 matchescorrelation_metrics.py - mc_analysis/
fitting/ , Python, 133 lines, 2 matchesfit_funcs.py - mc_analysis/
fitting/ , Python, 259 linesloaders.py - mc_analysis/
fitting/ , Python, 139 lines, 1 matchscos_utils.py - mc_analysis/
simulator/ , Python, 1 line__init__.py - mc_analysis/
simulator/ , Python, 211 linesapply_irf.py - mc_analysis/
simulator/ , Python, 733 linesg1_simulator.py - mc_analysis/
simulator/ , Python, 112 linesg1_simulator_utils.py - mc_analysis/
simulator/ , Python, 106 linesmch_to_csv.py - mc_analysis/
simulator/ , Python, 147 linesresample_lag_axis.py - mc_analysis/
tests/ , Python, 1 line__init__.py - mc_analysis/
tests/ , Python, 69 linestest_apply_irf.py - mc_analysis/
utils/ , Python, 1 line__init__.py - mc_analysis/
utils/ , Python, 8 linesabsolute_paths.py - mc_analysis/
utils/ , Python, 15 linesbrain_sensitivity_calcul ation.py - mc_analysis/
utils/ , Python, 86 linesplotting.py - mc_analysis/
utils/ , Python, 83 linespulsatile_time_series_ut ils.py - mc_analysis/
version.py , Python, 19 lines - LICENSE, License, 21 lines
- README.md, Text, 113 lines
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://
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://
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/
url = {https://
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/
VL - 13
IS - 2
SP - 025003
SN - 2329-423X
PB - Society of Photo-Optical Instrumentation Engineers
DO - 10.1117/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1117/
"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":
"volume": "13",
"issue": "2",
"page": "025003",
"DOI": "10.1117/
"PMID": "41930223",
"PMCID": "PMC13041720",
"ISSN": "2329-423X",
"publisher": "Society of Photo-Optical Instrumentation Engineers",
"URL": "https://
"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: NeurophotonicsIn 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: NeurophotonicsIn 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: NeurophotonicsIn 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: NeurophotonicsIn 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: NeurophotonicsIn 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: NeurophotonicsIn 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: NeurophotonicsIn 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 biologyIn 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 SocietyIn 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: NeurophotonicsIn 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.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 28 scripts, and 7 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:5e5235e1a3097d05…
Add the badge to the code's README
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 -->
[](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:endHTML
<!-- 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.
