// 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));
                    }
                }
            }
        }
        public void TemporalPointSourceGreensFunctionZFlux_Test()
        {
            double[] greensFunctionValues = new double[] { -0.0280097, -0.0159038, -2.54359e-5 };

            for (int iR = 0; iR < rTestValues.Length; iR++)
            {
                var relDiff = Math.Abs(
                    DiffusionGreensFunctions.TemporalPointSourceGreensFunctionZFlux(dp, rTestValues[iR], -dp.zp, time) -
                    greensFunctionValues[iR]) / greensFunctionValues[iR];
                Assert.IsTrue(relDiff < thresholdValue, "Test failed for r =" + rTestValues[iR] +
                              "mm, with time =" + time + "with relative difference " + relDiff);
            }
        }
        public void StationaryPointSourceGreensFunctionZFlux_Test()
        {
            double[] greensFunctionValues = new double[] { -0.0777258, -0.00263504, -3.78754e-5 };

            for (int iR = 0; iR < rTestValues.Length; iR++)
            {
                var relDiff = Math.Abs(
                    DiffusionGreensFunctions.StationaryPointSourceGreensFunctionZFlux(dp, rTestValues[iR], -dp.zp) -
                    greensFunctionValues[iR]) / greensFunctionValues[iR];
                Assert.IsTrue(relDiff < thresholdValue, "Test failed for r =" + rTestValues[iR] +
                              "mm, with relative difference " + relDiff);
            }
        }
        public void TemporalPointSourceGreensFunction_Test()
        {
            double[] greensFunctionValues = new double[] { 0.605791, 0.343966, 0.000550124 };

            for (int iR = 0; iR < rTestValues.Length; iR++)
            {
                var relDiff = Math.Abs(
                    DiffusionGreensFunctions.TemporalPointSourceGreensFunction(dp, rTestValues[iR], time) -
                    greensFunctionValues[iR]) / greensFunctionValues[iR];
                Assert.IsTrue(relDiff < thresholdValue, "Test failed for r =" + rTestValues[iR] +
                              "mm, with time =" + time + "with relative difference " + relDiff);
            }
        }
        public void StationaryPointSourceGreensFunction_Test()
        {
            double[] greensFunctionValues = new double[] { 0.202598, 0.0476782, 0.00422923 };

            for (int iR = 0; iR < rTestValues.Length; iR++)
            {
                var relDiff = Math.Abs(
                    DiffusionGreensFunctions.StationaryPointSourceGreensFunction(dp, rTestValues[iR]) -
                    greensFunctionValues[iR]) / greensFunctionValues[iR];
                Assert.IsTrue(relDiff < thresholdValue, "Test failed for r =" + rTestValues[iR] +
                              "mm, with relative difference " + relDiff);
            }
        }
        public void TemporalFrequencyPointSourceGreensFunction_Test()
        {
            double[] realGreensFunctionValues      = new double[] { 0.195264, 0.0411688, 0.00145147 };
            double[] imaginaryGreensFunctionValues = new double[] { -0.0212481, -0.0138797, -0.00274176 };


            for (int iR = 0; iR < rTestValues.Length; iR++)
            {
                var tfpsGF = DiffusionGreensFunctions.TemporalFrequencyPointSourceGreensFunction(dp, rTestValues[iR], k);

                var relDiffReal = Math.Abs(tfpsGF.Real - realGreensFunctionValues[iR]) / realGreensFunctionValues[iR];
                Assert.IsTrue(relDiffReal < thresholdValue, "Test failed for r =" + rTestValues[iR] +
                              "mm, with relative difference " + relDiffReal + " for the real compoment");
                var relDiffImag = Math.Abs(tfpsGF.Imaginary - imaginaryGreensFunctionValues[iR]) / imaginaryGreensFunctionValues[iR];
                Assert.IsTrue(relDiffImag < thresholdValue, "Test failed for r =" + rTestValues[iR] +
                              "mm, with relative difference " + relDiffReal + " for the imaginary compoment");
            }
        }
        public void TemporalFrequencyPointSourceGreensFunctionZFlux_Test()
        {
            double[] realGreensFunctionValues      = new double[] { -0.0776417, -0.00257811, -2.41795e-5 };
            double[] imaginaryGreensFunctionValues = new double[] { 0.00145096, 0.000327894, 2.21896e-5 };


            for (int iR = 0; iR < rTestValues.Length; iR++)
            {
                var tfpsGF = DiffusionGreensFunctions.TemporalFrequencyPointSourceGreensFunctionZFlux(dp, rTestValues[iR], -dp.zp, k);

                var relDiffReal = Math.Abs(tfpsGF.Real - realGreensFunctionValues[iR]) / realGreensFunctionValues[iR];
                Assert.IsTrue(relDiffReal < thresholdValue, "Test failed for r =" + rTestValues[iR] +
                              "mm, with relative difference " + relDiffReal + " for the real compoment");
                var relDiffImag = Math.Abs(tfpsGF.Imaginary - imaginaryGreensFunctionValues[iR]) / imaginaryGreensFunctionValues[iR];
                Assert.IsTrue(relDiffImag < thresholdValue, "Test failed for r =" + rTestValues[iR] +
                              "mm, with relative difference " + relDiffReal + " for the imaginary compoment");
            }
        }
 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));
             }
         }
     }
 }
        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)));
                            }
                        }
                    }
                }
            }
        }
        ///// <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);
                            }
                        }
                    }
                }
            }
        }