protected override void OnDoWork(System.ComponentModel.DoWorkEventArgs e) { int matrixHorizontalSize = 0; double currentHorizontalPos = 0; int counter = 0; // Get Circle/Square fractions needed for round laser beam double[,] fractions = CalcCircleSquareAreaFractions(); while (currentHorizontalPos < SampleSizeHor) { currentHorizontalPos = LaserBeamSize + (counter * ScanSpeed) / LaserFrequency; counter++; } int[,] SampleOriginal = new int[SampleMatrix.GetLength(0), SampleMatrix.GetLength(1)]; SampleOriginal = SampleMatrix; matrixHorizontalSize = counter; double[,] ablatedSampleMatrix = new double [(int)Math.Ceiling((double)SampleSizeVer / LineSpacing), // the vertical sample size divided by the LaserBeamSize matrixHorizontalSize // the horizontal sample size divided by ScanSpeed and multiplied by LaserFrequency, to get the number of pulses in a line ]; // ** removed, i can calculate time if i need it; 1 for the resulting sum, and 1 for time double horLocation = 0; // the horizontal, x, location of the laser beam (within a line) int verLocation = 0; // the vertical, y, location of the laser beam (lines) int laserPulseNumber = 0; // will be used to show which the current pulse is, within a single line scen int lineNumber = 0; // will be used to show which the current line is double localSum = 0; // initiation and declaration // GasFlow * 0.01 is there because of 10 ms time precision double ratio = (double)GasFlow * 0.01 / (double)ChamberVolume; // now to sum up these exponential fall-out values in time, so we get a representation // of how the concentration of ablated material/element changes in the gas with time int maxTime = 0; //Determine the approxiate washout time of the model, so we don't waste calculations int washoutTime = (int)Math.Round(6.746 * Math.Pow(ratio * 100, -1.0661) * 100); // If ratio is for example 1, (meaning 1 ml chamber, 100 ml/s gas flow), to keep the AUC for // integrate A*e^((-t)*1) from 0 to (6.746*((1*100)^(-1.0661))*100) // A, ergo peak, should be the ablated matrix value. // If the ratio is 0.5, the peak should be twice the ablated matrix value. double peakHeightFactor = ratio * 100; double exp = Math.Exp(1); int timeInCentiseconds = (int)Math.Floor((double)SampleSizeHor * 100 / ScanSpeed); // how much time it takes for one line to complete double[,] finalBackmixedMatrix = new double[ablatedSampleMatrix.GetLength(0), timeInCentiseconds]; // Calculate the number of required calculations, so we can display the percent done in the // progress bar. for (int i = 0; i < (ablatedSampleMatrix.GetLength(0)); i++) { for (int j = 0; j < (ablatedSampleMatrix.GetLength(1)); j++) { int offsetTime = (int)Math.Floor(((double)j / (double)LaserFrequency) * 100); if ((offsetTime + washoutTime) < timeInCentiseconds) { maxTime = offsetTime + washoutTime; } else { maxTime = timeInCentiseconds; } HowManyCalculations += maxTime - offsetTime; } } HowManyCalculations += ablatedSampleMatrix.GetLength(1) * ablatedSampleMatrix.GetLength(0); // Simulate ablation (without backmixing) //// Noise Random random = new Random(); SimpleRNG.SetSeedFromSystemTime(); RNGCryptoServiceProvider secureRandom = new RNGCryptoServiceProvider(); byte[] randBytes = new byte[4]; double RSD = 3.009 * Math.Exp(-0.2293 * LaserBeamSize) + 0.0568; /* Derived from MATLAB function fitting * a = 3.009 (0.9428, 5.076) * b = 0.2293 (0.1363, 0.3223) * c = 0.0568 (0.00159, 0.112) */ while ((verLocation + LaserBeamSize) <= SampleSizeVer) // whole sample { if (CancellationPending) { e.Cancel = true; return; } else { while ((horLocation + LaserBeamSize) <= SampleSizeHor) // one line { localSum = 0; // this double for loop sums up the values within LaserBeamSize (down and right) of horLocation and verLocation int LBS = LaserBeamSize; for (int i = 0; i < LBS; i++) { for (int j = 0; j < LBS; j++) { // Square beam if (BeamShape == false) { // Summation of values in the area of the matrix, that the laser ablates localSum = localSum + SampleMatrix[i + verLocation, j + (int)Math.Floor(horLocation)]; // Ablate the thin sample, so that nothing remains in the impact site if (ThinSample) { SampleMatrix[i + verLocation, j + (int)Math.Floor(horLocation)] = 0; } // Ablate a certain fraction else if (!ThinSample && !ThickSample) { SampleMatrix[i + verLocation, j + (int)Math.Floor(horLocation)] -= (int)Math.Round(SampleOriginal[i + verLocation, j + (int)Math.Floor(horLocation)] * (1 / (double)NumberOfShots)); } } // Round beam. Multiply border areas with calculated fractions. else if (BeamShape == true) { // Summation of values in the area of the matrix, that the laser ablates localSum = localSum + SampleMatrix[i + verLocation, j + (int)Math.Floor(horLocation)] * fractions[i, j]; // Ablate the thin sample, so that nothing remains in the impact site if (ThinSample) { SampleMatrix[i + verLocation, j + (int)Math.Floor(horLocation)] -= (int)Math.Round(SampleMatrix[i + verLocation, j + (int)Math.Floor(horLocation)] * fractions[i, j]); } else if (!ThinSample && !ThickSample) { SampleMatrix[i + verLocation, j + (int)Math.Floor(horLocation)] -= (int)Math.Round(SampleOriginal[i + verLocation, j + (int)Math.Floor(horLocation)] * fractions[i, j] * (1 / (double)NumberOfShots)); } } } } // Introduce ablation noise secureRandom.GetBytes(randBytes); double currentRandom = Math.Abs((double)BitConverter.ToInt32(randBytes, 0)); double noise2 = 0; double noise = 0; // noise = AblationNoise * localSum * ((random.NextDouble() - 0.5) * 2); if (AblationNoise != 0) { noise = localSum * SimpleRNG.GetNormal(0, AblationNoise); noise2 = SimpleRNG.GetNormal(0, AblationNoise); } ablatedSampleMatrix[lineNumber, laserPulseNumber] = Math.Abs(noise2 + localSum); // resultingMatrix[lineNumber, laserPulseNumber, 1] = (int)Math.Floor((double)laserPulseNumber * 100/ LaserFrequency); // time at which the laser beam pulse had happened (in 1/100 seconds) laserPulseNumber++; CurrentCalc++; horLocation = ScanSpeed * laserPulseNumber * (1 / (double)LaserFrequency); // the next horizontal location equals the current one + how much the laser moves in one pulse } } // the next vertical location is the current one + the width of the laser (LaserBeamSize); moves down one width to the next line ReportProgress((int)(CurrentCalc * 100 / HowManyCalculations), "Simulating ... (1/3 - Sample ablation)"); lineNumber++; laserPulseNumber = 0; horLocation = 0; verLocation = verLocation + LineSpacing; // progressBar.Value = (int)(100 * ((double)verLocation / (double)SampleSizeVer)); // progressBar.Update(); } // commented because we are putting everything in a single function // return ablatedSampleMatrix; // } // this is an inherent physical property of the LA (and ICP-MS?) machine, not its technical function // public double[,] SampleBackmix(ref double[,] ablatedSampleMatrix, ref System.Windows.Forms.ProgressBar progressBar, ref System.Windows.Forms.Label timeRemaining) // { //Report second step - sample backmixing // progressReport.Text = "Simulating ... (2/3 - Sample backmixing)"; // form.Refresh(); for (int i = 0; i < (ablatedSampleMatrix.GetLength(0)); i++) { if (CancellationPending) { e.Cancel = true; return; } else { for (int j = 0; j < (ablatedSampleMatrix.GetLength(1)); j++) { int offsetTime = (int)Math.Ceiling(((double)j / (double)LaserFrequency) * 100); if ((offsetTime + washoutTime) < timeInCentiseconds) { maxTime = offsetTime + washoutTime; } else { maxTime = timeInCentiseconds; } for (int k = offsetTime; k < maxTime; k++) { // so it starts the curve at the right point,*100 time precision double noise = 0; noise = TransferNoise * ((random.NextDouble() - 0.5) * 2); finalBackmixedMatrix[i, k] += Math.Abs(noise + (ablatedSampleMatrix[i, j] * peakHeightFactor * Math.Pow(exp, (-(k - offsetTime)) * ratio))); } CurrentCalc += maxTime - offsetTime; } } ReportProgress((int)(CurrentCalc * 100 / HowManyCalculations), "Simulating ... (2/3 - Sample backmixing)"); } double[,] ICPMSMatrix = new double[finalBackmixedMatrix.GetLength(0), (int)Math.Ceiling(finalBackmixedMatrix.GetLength(1) / (AcqTime * 100))]; // time precision ( the * 100 part) // Detector Noise generation // Random detectorNoise = new Random(); // double strength = AmountNoise * 1 / (AdvancedNoiseSettings[10] * AdvancedNoiseSettings[11]); // Complete noise generation Random holisticNoise = new Random(); // ICP-MS Acquisition for (int i = 0; i < ICPMSMatrix.GetLength(0); i++) { int j = 0; // raw data counter int k = 0; // final data counter double localSum1 = 0; while (j < finalBackmixedMatrix.GetLength(1)) { // adds up all the raw data within an acquisition time // localSum1 += finalBackmixedMatrix[i, j] + strength*detectorNoise.NextDouble(); // Trapezoidal rule of approximating integration if ((j + 1 < finalBackmixedMatrix.GetLength(1)) && ((j + 1) % (AcqTime * 100) != 0)) { localSum1 += ((finalBackmixedMatrix[i, j] + finalBackmixedMatrix[i, j + 1]) / 2) * 0.01; } else { localSum1 += finalBackmixedMatrix[i, j] * 0.01; } j++; // writes the data to the final array if ((j % (AcqTime * 100) == 0) || j == finalBackmixedMatrix.GetLength(1)) // time precision (the * 100 part) { double noise = DetectorNoise * ((random.NextDouble() - 0.5) * 2); //ICPMSMatrix[i, k] = localSum1 + holisticNoise.NextDouble()*AmountNoise ; //ICPMSMatrix[i, k] = localSum1; ICPMSMatrix[i, k] = Math.Abs(noise + localSum1); localSum1 = 0; k++; } } } ReportProgress((int)(CurrentCalc * 100 / HowManyCalculations), "Simulating ... (3/3 - ICPMS acquisition)"); e.Result = ICPMSMatrix; }