/// <summary> /// method to tally to detector /// </summary> /// <param name="photon">photon data needed to tally</param> public void Tally(Photon photon) { var dp = photon.DP; var ia = DetectorBinning.WhichBin(Math.Acos(photon.DP.Direction.Uz), Angle.Count - 1, Angle.Delta, Angle.Start); if (ia != -1) { var x = dp.Position.X; var fxArray = Fx.AsEnumerable().ToArray(); for (int ifx = 0; ifx < fxArray.Length; ifx++) { double freq = fxArray[ifx]; var sinNegativeTwoPiFX = Math.Sin(-2 * Math.PI * freq * x); var cosNegativeTwoPiFX = Math.Cos(-2 * Math.PI * freq * x); // convert to Hz-sec from GHz-ns 1e-9*1e9=1 var deltaWeight = dp.Weight * (cosNegativeTwoPiFX + Complex.ImaginaryOne * sinNegativeTwoPiFX); Mean[ifx, ia] += deltaWeight; if (TallySecondMoment) // 2nd moment is E[xx*]=E[xreal^2]+E[ximag^2] { var deltaWeight2 = dp.Weight * dp.Weight * cosNegativeTwoPiFX * cosNegativeTwoPiFX + dp.Weight * dp.Weight * sinNegativeTwoPiFX * sinNegativeTwoPiFX; SecondMoment[ifx, ia] += deltaWeight2; } } TallyCount++; } }
/// <summary> /// method to tally given two consecutive photon data points /// </summary> /// <param name="previousDP">previous data point</param> /// <param name="dp">current data point</param> /// <param name="currentRegionIndex">index of region photon current is in</param> public void TallySingle(PhotonDataPoint previousDP, PhotonDataPoint dp, int currentRegionIndex) { var iz = DetectorBinning.WhichBin(dp.Position.Z, Z.Count - 1, Z.Delta, Z.Start); // using Acos, -1<Uz<1 goes to pi<theta<0, so first bin is most forward directed angle var ia = DetectorBinning.WhichBin(Math.Acos(dp.Direction.Uz), Angle.Count - 1, Angle.Delta, Angle.Start); var weight = _absorptionWeightingMethod(previousDP, dp, currentRegionIndex); if (weight != 0.0) { var x = dp.Position.X; var fxArray = Fx.AsEnumerable().ToArray(); for (int ifx = 0; ifx < fxArray.Length; ifx++) { double freq = fxArray[ifx]; var sinNegativeTwoPiFX = Math.Sin(-2 * Math.PI * freq * x); var cosNegativeTwoPiFX = Math.Cos(-2 * Math.PI * freq * x); // convert to Hz-sec from GHz-ns 1e-9*1e9=1 var deltaWeight = (weight / _ops[currentRegionIndex].Mua) * (cosNegativeTwoPiFX + Complex.ImaginaryOne * sinNegativeTwoPiFX); Mean[ifx, iz, ia] += deltaWeight; if (TallySecondMoment) { _tallyForOnePhoton[ifx, iz, ia] += deltaWeight; } TallyCount++; } } }
/// <summary> /// method to tally to detector /// </summary> /// <param name="photon">photon data needed to tally</param> public void Tally(Photon photon) { if (!IsWithinDetectorAperture(photon)) { return; } var dp = photon.DP; var x = dp.Position.X; var fxArray = Fx.AsEnumerable().ToArray(); for (int i = 0; i < fxArray.Length; i++) { double freq = fxArray[i]; var sinNegativeTwoPiFX = Math.Sin(-2 * Math.PI * freq * x); var cosNegativeTwoPiFX = Math.Cos(-2 * Math.PI * freq * x); // convert to Hz-sec from GHz-ns 1e-9*1e9=1 var deltaWeight = dp.Weight * (cosNegativeTwoPiFX + Complex.ImaginaryOne * sinNegativeTwoPiFX); Mean[i] += deltaWeight; if (TallySecondMoment) // 2nd moment is E[xx*]=E[xreal^2]+E[ximag^2] { var deltaWeight2 = dp.Weight * dp.Weight * cosNegativeTwoPiFX * cosNegativeTwoPiFX + dp.Weight * dp.Weight * sinNegativeTwoPiFX * sinNegativeTwoPiFX; SecondMoment[i] += deltaWeight2; } } TallyCount++; }
/// <summary> /// method to tally given two consecutive photon data points /// </summary> /// <param name="previousDP">previous data point</param> /// <param name="dp">current data point</param> /// <param name="currentRegionIndex">index of region photon current is in</param> public void TallySingle(PhotonDataPoint previousDP, PhotonDataPoint dp, int currentRegionIndex) { var x = dp.Position.X; var fxArray = Fx.AsEnumerable().ToArray(); var iz = DetectorBinning.WhichBin(dp.Position.Z, Z.Count - 1, Z.Delta, Z.Start); var weight = _absorptionWeightingMethod(previousDP, dp, currentRegionIndex); // Note: GetVolumeAbsorptionWeightingMethod in Initialize method determines the *absorbed* weight // so for fluence this weight is divided by Mua var regionIndex = currentRegionIndex; if (weight != 0) { for (int ifx = 0; ifx < fxArray.Length; ifx++) { double freq = fxArray[ifx]; var sinNegativeTwoPiFX = Math.Sin(-2 * Math.PI * freq * x); var cosNegativeTwoPiFX = Math.Cos(-2 * Math.PI * freq * x); // convert to Hz-sec from GHz-ns 1e-9*1e9=1 var deltaWeight = weight * (cosNegativeTwoPiFX + Complex.ImaginaryOne * sinNegativeTwoPiFX); Mean[ifx, iz] += (deltaWeight / _ops[regionIndex].Mua); if (TallySecondMoment) // 2nd moment is E[xx*]=E[xreal^2]+E[ximag^2] { _tallyForOnePhoton[ifx, iz] += (deltaWeight / _ops[regionIndex].Mua) * (deltaWeight / _ops[regionIndex].Mua) * cosNegativeTwoPiFX * cosNegativeTwoPiFX + (deltaWeight / _ops[regionIndex].Mua) * (deltaWeight / _ops[regionIndex].Mua) * sinNegativeTwoPiFX * sinNegativeTwoPiFX; } } TallyCount++; } }
public void Initialize(ITissue tissue, Random rng) { // assign any user-defined outputs (except arrays...we'll make those on-demand) TallyCount = 0; // if the data arrays are null, create them (only create second moment if TallySecondMoment is true) Mean = Mean ?? new Complex[Fx.Count, Time.Count - 1]; SecondMoment = SecondMoment ?? (TallySecondMoment ? new Complex[Fx.Count, Time.Count - 1] : null); // intialize any other necessary class fields here _perturbedOps = PerturbedOps; _perturbedRegionsIndices = PerturbedRegionsIndices; _referenceOps = tissue.Regions.Select(r => r.RegionOP).ToArray(); _absorbAction = AbsorptionWeightingMethods.GetpMCTerminationAbsorptionWeightingMethod(tissue, this); _fxArray = Fx.AsEnumerable().ToArray(); }
/// <summary> /// method to tally to detector /// </summary> /// <param name="photon">photon data needed to tally</param> public void Tally(Photon photon) { // calculate the radial bin to attribute the deposition var tissueMT = new double[2]; // 2 is for [static, dynamic] tally separation bool talliedMT = false; double totalMT = 0; var totalMTOfZForOnePhoton = new Complex[Fx.Count, Z.Count - 1]; var dynamicMTOfZForOnePhoton = new Complex[Fx.Count, Z.Count - 1]; var fxArray = Fx.AsEnumerable().ToArray(); var x = photon.DP.Position.X; // use final exiting x position var sinNegativeTwoPiFX = fxArray.Select(fx => Math.Sin(-2 * Math.PI * fx * x)).ToArray(); var cosNegativeTwoPiFX = fxArray.Select(fx => Math.Cos(-2 * Math.PI * fx * x)).ToArray(); // go through photon history and claculate momentum transfer // assumes that no MT tallied at pseudo-collisions (reflections and refractions) // this algorithm needs to look ahead to angle of next DP, but needs info from previous to determine whether real or pseudo-collision PhotonDataPoint previousDP = photon.History.HistoryData.First(); PhotonDataPoint currentDP = photon.History.HistoryData.Skip(1).Take(1).First(); foreach (PhotonDataPoint nextDP in photon.History.HistoryData.Skip(2)) { if (previousDP.Weight != currentDP.Weight) // only for true collision points { var csr = _tissue.GetRegionIndex(currentDP.Position); // get current region index // get z bin of current position var iz = DetectorBinning.WhichBin(currentDP.Position.Z, Z.Count - 1, Z.Delta, Z.Start); // get angle between current and next double cosineBetweenTrajectories = Direction.GetDotProduct(currentDP.Direction, nextDP.Direction); var momentumTransfer = 1 - cosineBetweenTrajectories; totalMT += momentumTransfer; for (int ifx = 0; ifx < Fx.Count; ifx++) { var deltaWeight = photon.DP.Weight * cosNegativeTwoPiFX[ifx] + Complex.ImaginaryOne * sinNegativeTwoPiFX[ifx]; TotalMTOfZ[ifx, iz] += deltaWeight * momentumTransfer; totalMTOfZForOnePhoton[ifx, iz] += deltaWeight * momentumTransfer; } if (_rng.NextDouble() < _bloodVolumeFraction[csr]) // hit blood { tissueMT[1] += momentumTransfer; for (int ifx = 0; ifx < Fx.Count; ifx++) { var deltaWeight = photon.DP.Weight * cosNegativeTwoPiFX[ifx] + Complex.ImaginaryOne * sinNegativeTwoPiFX[ifx]; DynamicMTOfZ[ifx, iz] += deltaWeight * momentumTransfer; dynamicMTOfZForOnePhoton[ifx, iz] += deltaWeight * momentumTransfer; } SubregionCollisions[csr, 1] += 1; // add to dynamic collision count } else // index 0 captures static events { tissueMT[0] += momentumTransfer; SubregionCollisions[csr, 0] += 1; // add to static collision count } talliedMT = true; } previousDP = currentDP; currentDP = nextDP; } if (totalMT > 0.0) // only tally if momentum transfer accumulated { var imt = DetectorBinning.WhichBin(totalMT, MTBins.Count - 1, MTBins.Delta, MTBins.Start); for (int ifx = 0; ifx < Fx.Count; ifx++) { var deltaWeight = photon.DP.Weight * cosNegativeTwoPiFX[ifx] + Complex.ImaginaryOne * sinNegativeTwoPiFX[ifx]; Mean[ifx, imt] += deltaWeight; if (TallySecondMoment) { SecondMoment[ifx, imt] += deltaWeight * deltaWeight; for (int i = 0; i < Fx.Count - 1; i++) { for (int j = 0; j < Z.Count - 1; j++) { TotalMTOfZSecondMoment[i, j] += totalMTOfZForOnePhoton[i, j] * totalMTOfZForOnePhoton[i, j]; DynamicMTOfZSecondMoment[i, j] += dynamicMTOfZForOnePhoton[i, j] * dynamicMTOfZForOnePhoton[i, j]; } } } if (talliedMT) { TallyCount++; } // tally DYNAMIC fractional MT in each subregion int ifrac; for (int isr = 0; isr < NumSubregions; isr++) { // add 1 to ifrac to offset bin 0 added for =0 only tallies ifrac = DetectorBinning.WhichBin(tissueMT[1] / totalMT, FractionalMTBins.Count - 1, FractionalMTBins.Delta, FractionalMTBins.Start) + 1; // put identically 0 fractional MT into separate bin at index 0 if (tissueMT[1] / totalMT == 0.0) { ifrac = 0; } // put identically 1 fractional MT into separate bin at index Count+1 -1 if (tissueMT[1] / totalMT == 1.0) { ifrac = FractionalMTBins.Count; } FractionalMT[ifx, imt, ifrac] += deltaWeight; } } } }