/// <summary> /// Create a DiffusionParameters object from OpticalProperties object and a ForwardModel /// choice. /// </summary> /// <param name="op">OpticalProperties object</param> /// <param name="fm">ForwardModel enum</param> /// <returns>new DiffusionParameters object</returns> public static DiffusionParameters Create(OpticalProperties op, ForwardModel fm) { var mua = op.Mua; var mutr = op.Mua + op.Musp; var cn = GlobalConstants.C / op.N; var D = 1 / (3 * mutr); var A = CalculatorToolbox.GetCubicAParameter(op.N); var mueff = Math.Sqrt(3 * op.Mua * mutr); var zb = 2 / (3 * mutr) * A; double musTilde; double gTilde; switch (fm) { case ForwardModel.SDA: default: musTilde = op.Musp; gTilde = op.G; break; case ForwardModel.DeltaPOne: musTilde = op.Musp * (1 - op.G * op.G) / (1 - op.G); gTilde = op.G / (op.G + 1); break; } var mutTilde = op.Mua + musTilde; var zp = 1 / mutTilde; return(new DiffusionParameters(A, mueff, zb, zp, mutTilde, musTilde, mutr, gTilde, D, cn, mua)); }
// Steady-State Domain... ///// <summary> ///// Steady-State centerline Photon Hitting Density by the Green's function multiplication ///// </summary> ///// <param name="ops">optical properties object</param> ///// <param name="rhos">Source Detector separation</param> ///// <param name="rProbe">Radial distance from source to "iterogation" location</param> ///// <param name="z">Depth being probed</param> ///// <returns>The Photon Hitting Density at specified location</returns> //public IEnumerable<double> SteadyStatePointSourceCenterlinePHD(IEnumerable<OpticalProperties> ops, IEnumerable<double> rProbes, // IEnumerable<double> rhos, IEnumerable<double> zs) //{ // foreach (var op in ops) // { // DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); // foreach (var rProbe in rProbes) // { // foreach (var rho in rhos) // { // foreach (var z in zs) // { // var r11 = DiffusionBase.Radius1(rProbe, z, dp.zp); // var r12 = DiffusionBase.Radius2(rProbe, z, dp.zp, dp.zb); // var r21 = DiffusionBase.Radius1(rho - rProbe, z, 0.0); // var r22 = DiffusionBase.Radius2(rho - rProbe, z, 0.0, dp.zb); // var fluence1 = DiffusionBase.SteadyStatePointSourceImageGreensFunction(dp, r11, r12); // var fluence2 = DiffusionBase.SteadyStatePointSourceImageGreensFunction(dp, r21, r22); // yield return (fluence1 * fluence2); // } // } // } // } //} public static IEnumerable <Complex> TimeFrequencyDomainFluence2SurfacePointPHD( this IForwardSolver myForwardSolver, double timeModulationFrequency, IEnumerable <OpticalProperties> ops, IEnumerable <double> rhoPrimes, IEnumerable <double> zs) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); Complex k = ( (op.Mua * dp.cn + Complex.ImaginaryOne * timeModulationFrequency * 2 * Math.PI) / (dp.D * dp.cn) ).SquareRoot(); foreach (var rho in rhoPrimes) { foreach (var z in zs) { var r21 = CalculatorToolbox.GetRadius(rho, z); var r22 = CalculatorToolbox.GetRadius(rho, z + 2 * dp.zb); yield return (DiffusionGreensFunctions.TemporalFrequencyPointSourceImageGreensFunction(dp, r21, r22, k)); } } } }
/// <summary> /// Evaluates the temporal frequency radially resolved reflectance using the distribution of /// the source-image point source configuration. /// </summary> /// <param name="dp">DiffusionParameters object</param> /// <param name="rho">radial location</param> /// <param name="k">wavevector</param> /// <param name="fr1">Fresnel Moment 1</param> /// <param name="fr2">Fresnel Moment 2</param> /// <returns></returns> public override Complex TemporalFrequencyReflectance(DiffusionParameters dp, double rho, Complex k, double fr1, double fr2) { var dpLocalReal = DiffusionParameters.Copy(dp); var dpLocalImag = DiffusionParameters.Copy(dp); Func <DiffusionParameters, double, Complex, double, Complex> kernelFunc = (dpLocal, r, kLocal, zp) => { dpLocal.zp = zp; return (_pointSourceForwardSolver.TemporalFrequencyReflectance( dpLocal, r, kLocal, fr1, fr2)); }; return ((CalculatorToolbox.EvaluateDistributedExponentialLineSourceIntegral( zp => kernelFunc(dpLocalReal, rho, k, zp).Real, dp.mutTilde) + Complex.ImaginaryOne * CalculatorToolbox.EvaluateDistributedExponentialLineSourceIntegral( zp => kernelFunc(dpLocalImag, rho, k, zp).Imaginary, dp.mutTilde) ) * dp.musTilde); }
/// <summary> /// Evaluate the temporally-radially resolved fluence using the point source-image configuration /// </summary> /// <param name="dp">DiffusionParameters object</param> /// <param name="rho">radial location</param> /// <param name="z">depth location</param> /// <param name="t">time</param> /// <returns>fluence</returns> public override double TemporalFluence( DiffusionParameters dp, double rho, double z, double t) { return(DiffusionGreensFunctions.TemporalPointSourceImageGreensFunction(dp, CalculatorToolbox.GetRadius(rho, z - dp.zp), CalculatorToolbox.GetRadius(rho, z + dp.zp + 2 * dp.zb), t)); }
// this method builds an R(rho, ft) array and then uses FFT to generate R(rho, t) private double[] DetermineROfTimeFromROfFtForFixedRho(double rho, IOpticalPropertyRegion[] regions, out double[] FFTTimeSequence) { // get ops of top tissue region var op0 = regions[0].RegionOP; var fr1 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder1(op0.N); var fr2 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder2(op0.N); var diffusionParameters = GetDiffusionParameters(regions); var layerThicknesses = GetLayerThicknesses(regions); int numFreq = 512; // Kienle used 512 and deltaFreq = 0.1 // Kienle says deltaFrequency depends on source-detector separation var deltaFrequency = 0.1; // 100 MHz if (rho <= 3) { deltaFrequency = 0.5; // so far I've found this value works for smaller rho } var F = numFreq * deltaFrequency; // 51 GHz var deltaTime = 1.0 / (numFreq * deltaFrequency); // 0.02 ns => T = 10 ns // var homoSDA = new PointSourceSDAForwardSolver(); // debug with h**o SDA // var rOfTime = new Complex[numFreq]; // debug array // considerations: 2n datapoint and pad with 0s beyond (deltaTime * numFreq) var rOfFt = new Complex[numFreq]; double[] ft = Enumerable.Range(0, numFreq).Select(x => x * deltaFrequency).ToArray(); FFTTimeSequence = Enumerable.Range(0, numFreq).Select(x => x * deltaTime).ToArray(); for (int i = 0; i < numFreq; i++) { // normalize by F=(numFreq*deltaFrequency) rOfFt[i] = TemporalFrequencyReflectance(rho, ft[i], diffusionParameters, layerThicknesses, fr1, fr2) * F; // rOfTime[i] = homoSDA.ROfRhoAndTime(regions[1].RegionOP, rho, t[i]); // debug array } // to debug, use R(t) and FFT to see if result R(ft) is close to rOfFt //var dft2 = new MathNet.Numerics.IntegralTransforms.Algorithms.DiscreteFourierTransform(); //dft2.Radix2Forward(rOfTime, FourierOptions.NoScaling); // convert to R(ft) to compare with rOfFt //var relDiffReal = Enumerable.Zip(rOfTime, rOfFt, (x, y) => Math.Abs((y.Real - x.Real) / x.Real)); //var relDiffImag = Enumerable.Zip(rOfTime, rOfFt, (x, y) => Math.Abs((y.Imaginary - x.Imaginary) / x.Imaginary)); //var maxReal = relDiffReal.Max(); //var maxImag = relDiffImag.Max(); //var dum1 = maxReal; //var dum2 = maxImag; //dft2.Inverse(rOfTime, FourierOptions.NoScaling); // debug convert to R(t) // end debug code // FFT R(ft) to R(t) //var dft = new MathNet.Numerics.IntegralTransforms.Algorithms.DiscreteFourierTransform(); //dft.Inverse(rOfFt, FourierOptions.NoScaling); // convert to R(t) Fourier.Inverse(rOfFt, FourierOptions.NoScaling); var rOfTime = new double[FFTTimeSequence.Length]; rOfTime = rOfFt.Select(r => r.Real / (numFreq / 2)).ToArray(); return(rOfTime); }
// protected methods /// <summary> /// Calculates the reflectance based on the integral of the radiance over the backward hemisphere... /// </summary> /// <param name="surfaceFluence">diffuse fluence at the surface</param> /// <param name="surfaceFlux">diffuse flux at the surface</param> /// <param name="mediaRefractiveIndex">refractive index of the medium</param> /// <returns></returns> protected static double GetBackwardHemisphereIntegralDiffuseReflectance( double surfaceFluence, double surfaceFlux, double mediaRefractiveIndex) { var fr1 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder1( mediaRefractiveIndex); var fr2 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder2( mediaRefractiveIndex); return(GetBackwardHemisphereIntegralDiffuseReflectance(surfaceFluence, surfaceFlux, fr1, fr2)); }
private double StationaryFlux(double rho, double z, DiffusionParameters dp) { var dpLocal = DiffusionParameters.Copy(dp); return(CalculatorToolbox.EvaluateDistributedExponentialLineSourceIntegral( zp => { dpLocal.zp = zp; return _pointSourceForwardSolver.StationaryFlux(rho, z, dpLocal); }, dp.mutTilde) * dp.musTilde); }
/// <summary> /// Evaluation of the temporally and radially resolved fluence rate using the distribution of /// source-image point sources. /// </summary> /// <param name="dp">DiffusionParameters object</param> /// <param name="rho">radial location</param> /// <param name="z">depth location</param> /// <param name="t">time</param> /// <returns>fluence rate</returns> public override double TemporalFluence( DiffusionParameters dp, double rho, double z, double t) { var dpLocal = DiffusionParameters.Copy(dp); return(CalculatorToolbox.EvaluateDistributedExponentialLineSourceIntegral( zp => { dpLocal.zp = zp; return _pointSourceForwardSolver.TemporalFluence(dpLocal, rho, z, t); }, dp.mutTilde) * dp.musTilde); }
public override IEnumerable <double> ROfRho( IEnumerable <OpticalProperties> ops, IEnumerable <double> rhos) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, this.ForwardModel); var fr1 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder1(op.N); var fr2 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder2(op.N); foreach (var rho in rhos) { yield return(StationaryReflectance(dp, rho, fr1, fr2)); } } }
public override double ROfFx(IOpticalPropertyRegion[] regions, double fx) { // get ops of top tissue region var op0 = regions[0].RegionOP; var fr1 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder1(op0.N); var fr2 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder2(op0.N); var diffusionParameters = GetDiffusionParameters(regions); var layerThicknesses = GetLayerThicknesses(regions); // check that embedded source is within top layer, otherwise solution invalid if (diffusionParameters[0].zp > layerThicknesses[0]) { throw new ArgumentException("Top layer thickness must be greater than l* = 1/(mua+musp)"); } return(SpatialFrequencyReflectance(2 * Math.PI * fx, diffusionParameters, layerThicknesses, fr1, fr2)); }
/// <summary> /// Modulation frequency-dependent reflectance. Modified from Pham et al, Appl. Opt. Sept 2000 /// to include spatial modulation, as described in Cuccia et al, J. Biomed. Opt. March/April 2009 /// </summary> /// <param name="op">optical properties of the medium</param> /// <param name="fx">spatial frequency</param> /// <param name="ft">modulation frequency (GHz)</param> /// <returns></returns> public override Complex ROfFxAndFt(OpticalProperties op, double fx, double ft) { double A = CalculatorToolbox.GetCubicAParameter(op.N); double wOverC = Math.PI * ft * op.N / GlobalConstants.C; double mutr = op.Mua + op.Musp; Complex D = 1 / (3 * mutr * (1.0 + Complex.ImaginaryOne * wOverC / mutr)); Complex mueff = ( 3.0 * op.Mua * mutr - 3.0 * wOverC * wOverC + Complex.ImaginaryOne * (1 + op.Mua / mutr) * 3.0 * mutr * wOverC + 4.0 * Math.PI * Math.PI * fx * fx ).SquareRoot(); Complex temp = mueff * D; return(3 * A * op.Musp * D / (temp + 1.0 / 3.0) / (temp + A)); }
public override IEnumerable <Complex> ROfRhoAndFt(IEnumerable <OpticalProperties> ops, IEnumerable <double> rhos, IEnumerable <double> fts) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, this.ForwardModel); var fr1 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder1(op.N); var fr2 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder2(op.N); foreach (var rho in rhos) { foreach (var ft in fts) { Complex k = ((op.Mua * dp.cn + Complex.ImaginaryOne * ft * 2 * Math.PI) / (dp.cn * dp.D)).SquareRoot(); yield return(TemporalFrequencyReflectance(dp, rho, k, fr1, fr2)); } } } }
public static IEnumerable <double> SteadyStateFluence2SurfacePointPHD( this IForwardSolver myForwardSolver, IEnumerable <OpticalProperties> ops, IEnumerable <double> rhoPrimes, IEnumerable <double> zs) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); foreach (var rho in rhoPrimes) { foreach (var z in zs) { var r21 = CalculatorToolbox.GetRadius(rho, z); var r22 = CalculatorToolbox.GetRadius(rho, z + 2 * dp.zb); yield return (DiffusionGreensFunctions.StationaryPointSourceImageGreensFunction(dp, r21, r22)); } } } }
///// <summary> ///// Modified from a code of Fred's written in Matlab ///// </summary> ///// <param name="dp">diffusion parameters object</param> ///// <param name="k">complex diffusion constant, ((mua*cn +i*(ft*2*pi))/(D*cn)).^0.5</param> ///// <param name="rho">source-detector separation</param> ///// <param name="rProbe">radius from source</param> ///// <param name="y">omitted</param> ///// <param name="z">depth</param> ///// <param name="ft">temporal frequency</param> ///// <returns></returns> //public static double DepthProbFrequencyDomainPhotonMigration(DiffusionParameters dp, Complex k, // double rho, double rProbe, double z, double ft) //{ // var r11 = DiffusionBase.Radius1(rho, z, dp.zp); // var r12 = DiffusionBase.Radius2(rho, z, dp.zp, dp.zb); // var r21 = DiffusionBase.Radius1(rProbe - rho, z, 0.0); // var r22 = DiffusionBase.Radius2(rProbe - rho, z, 0.0, dp.zb); // var phi1 = SDAForwardSolver.TemporalFrequencyFluence(dp, k, r11, r12); // var phi2 = SDAForwardSolver.TemporalFrequencyFluence(dp, k, r21, r22); // return (phi1 * phi2).Modulus; // see Kienle and Patterson, JOSA A 14(1), 246-254,1997 //} public static IEnumerable <double> TemporalFrequencyPointSourceCenterlinePHD( this IForwardSolver myForwardSolver, IEnumerable <OpticalProperties> ops, IEnumerable <double> rProbes, IEnumerable <double> rhos, IEnumerable <double> zs, IEnumerable <double> fts) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); foreach (var rProbe in rProbes) { foreach (var rho in rhos) { foreach (var z in zs) { var r11 = CalculatorToolbox.GetRadius(rProbe, z - dp.zp); var r12 = CalculatorToolbox.GetRadius(rProbe, z + dp.zp + 2 * dp.zb); var r21 = CalculatorToolbox.GetRadius(rho - rProbe, z); var r22 = CalculatorToolbox.GetRadius(rho - rProbe, z + 2 * dp.zb); foreach (var ft in fts) { Complex k = ( (op.Mua * dp.cn + Complex.ImaginaryOne * ft * 2 * Math.PI) / (dp.D * dp.cn) ).SquareRoot(); var phi1 = DiffusionGreensFunctions.TemporalFrequencyPointSourceImageGreensFunction( dp, r11, r12, k); var phi2 = DiffusionGreensFunctions.TemporalFrequencyPointSourceImageGreensFunction( dp, r21, r22, k); yield return((phi1 * phi2).Magnitude); } } } } } }
public static IEnumerable <double> TemporalPointSourceCenterlinePHD( this IForwardSolver myForwardSolver, IEnumerable <OpticalProperties> ops, IEnumerable <double> rProbes, IEnumerable <double> rhos, IEnumerable <double> zs, IEnumerable <double> ts) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); foreach (var rProbe in rProbes) { foreach (var rho in rhos) { foreach (var z in zs) { var r11 = CalculatorToolbox.GetRadius(rProbe, z - dp.zp); var r12 = CalculatorToolbox.GetRadius(rProbe, z + dp.zp + 2 * dp.zb); var r21 = CalculatorToolbox.GetRadius(rho - rProbe, z); var r22 = CalculatorToolbox.GetRadius(rho - rProbe, z + 2 * dp.zb); foreach (var t in ts) { Func <double, double> integrandConvolve = tau => { return (DiffusionGreensFunctions.TemporalPointSourceImageGreensFunction (dp, r11, r12, tau) * DiffusionGreensFunctions.TemporalPointSourceImageGreensFunction (dp, r21, r22, t - tau)); }; yield return(FunctionMath.Integrate( integrandConvolve, Meta.Numerics.Interval.FromEndpoints(0.0, t))); } } } } } }
// this method builds an R(fx, ft) array and then uses FFT to generate R(fx, t) private double[] DetermineROfTimeFromROfFtForFixedFx(double fx, IOpticalPropertyRegion[] regions, out double[] FFTTimeSequence) { // get ops of top tissue region var op0 = regions[0].RegionOP; var fr1 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder1(op0.N); var fr2 = CalculatorToolbox.GetCubicFresnelReflectionMomentOfOrder2(op0.N); var diffusionParameters = GetDiffusionParameters(regions); var layerThicknesses = GetLayerThicknesses(regions); int numFreq = 512; // Kienle used 512 and deltaFreq = 0.1 // Kienle says deltaFrequency depends on source-detector separation var deltaFrequency = 0.5; // 500 MHz good for all fx var F = numFreq * deltaFrequency; // 51 GHz var deltaTime = 1.0 / (numFreq * deltaFrequency); // 0.02 ns => T = 10 ns // considerations: 2n datapoint and pad with 0s beyond (deltaTime * numFreq) var rOfFt = new Complex[numFreq]; double[] ft = Enumerable.Range(0, numFreq).Select(x => x * deltaFrequency).ToArray(); FFTTimeSequence = Enumerable.Range(0, numFreq).Select(x => x * deltaTime).ToArray(); for (int i = 0; i < numFreq; i++) { // normalize by F=(numFreq*deltaFrequency) rOfFt[i] = SpatialAndTemporalFrequencyReflectance(2 * Math.PI * fx, ft[i], diffusionParameters, layerThicknesses, fr1, fr2) * F; } // FFT R(ft) to R(t) //var dft = new MathNet.Numerics.IntegralTransforms.Algorithms.DiscreteFourierTransform(); //dft.Radix2Inverse(rOfFt, FourierOptions.NoScaling); // convert to R(t) Fourier.Radix2Inverse(rOfFt, FourierOptions.NoScaling); var rOfTime = new double[FFTTimeSequence.Length]; rOfTime = rOfFt.Select(r => r.Real / (numFreq / 2)).ToArray(); return(rOfTime); }
/// <summary> /// Evaluate the stationary radially resolved z-flux with the point source-image /// configuration /// </summary> /// <param name="rho">radial location</param> /// <param name="z">depth location</param> /// <param name="dp">DiffusionParamters object</param> /// <returns></returns> public double StationaryFlux(double rho, double z, DiffusionParameters dp) { var zSource = z - dp.zp; var zImage = z + dp.zp + 2 * dp.zb; return (DiffusionGreensFunctions.StationaryPointSourceImageGreensFunctionZFlux(dp, CalculatorToolbox.GetRadius(rho, zSource), zSource, CalculatorToolbox.GetRadius(rho, zImage), zImage)); }
/// <summary> /// Evaluate the stationary radially resolved fluence with the point source-image /// configuration /// </summary> /// <param name="rho">radial location</param> /// <param name="z">depth location</param> /// <param name="dp">DiffusionParameters object</param> /// <returns>fluence</returns> public override double StationaryFluence(double rho, double z, DiffusionParameters dp) { return(DiffusionGreensFunctions.StationaryPointSourceImageGreensFunction(dp, CalculatorToolbox.GetRadius(rho, z - dp.zp), CalculatorToolbox.GetRadius(rho, z + dp.zp + 2 * dp.zb))); }
public Complex TemporalFrequencyZFlux( DiffusionParameters dp, double rho, double z, Complex k) { var zSource = z - dp.zp; var zImage = z + dp.zp + 2 * dp.zb; return(DiffusionGreensFunctions.TemporalFrequencyPointSourceImageGreensFunctionZFlux(dp, CalculatorToolbox.GetRadius(rho, zSource), zSource, CalculatorToolbox.GetRadius(rho, zImage), zImage, k)); }
public override Complex TemporalFrequencyFluence(DiffusionParameters dp, double rho, double z, Complex k) { return(DiffusionGreensFunctions.TemporalFrequencyPointSourceImageGreensFunction(dp, CalculatorToolbox.GetRadius(rho, z - dp.zp), CalculatorToolbox.GetRadius(rho, z + dp.zp + 2 * dp.zb), k)); }
/// <summary> /// Evaluate the temporally-radially resolved z-flux using the point source-image configuration /// </summary> /// <param name="dp">DiffusionParameters object</param> /// <param name="rho">radial location</param> /// <param name="z">depth location</param> /// <param name="t">time</param> /// <returns></returns> public double TemporalFlux( DiffusionParameters dp, double rho, double z, double t) { var zSource = z - dp.zp; var zImage = z + dp.zp + 2 * dp.zb; return(DiffusionGreensFunctions.TemporalPointSourceImageGreensFunctionZFlux(dp, CalculatorToolbox.GetRadius(rho, zSource), zSource, CalculatorToolbox.GetRadius(rho, zImage), zImage, t)); }