/* * Copyright (c) 2012 Diamond Light Source Ltd. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package uk.ac.diamond.scisoft.analysis.fitting; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.List; import org.eclipse.dawnsci.analysis.api.fitting.functions.IPeak; import org.eclipse.january.dataset.Dataset; import org.eclipse.january.dataset.DatasetFactory; import org.eclipse.january.dataset.DoubleDataset; import org.eclipse.january.dataset.Maths; import org.eclipse.january.dataset.Random; import org.junit.Before; import org.junit.Test; import uk.ac.diamond.scisoft.analysis.fitting.functions.CompositeFunction; import uk.ac.diamond.scisoft.analysis.fitting.functions.FunctionFactory; import uk.ac.diamond.scisoft.analysis.fitting.functions.Gaussian; import uk.ac.diamond.scisoft.analysis.fitting.functions.Lorentzian; import uk.ac.diamond.scisoft.analysis.fitting.functions.PearsonVII; import uk.ac.diamond.scisoft.analysis.fitting.functions.PseudoVoigt; import uk.ac.diamond.scisoft.analysis.optimize.GeneticAlg; import uk.ac.diamond.scisoft.analysis.optimize.IOptimizer; public abstract class Generic1DFitterTestBase { static final int dataRange = 550; static int[] defaultPeakPos = new int[] { 100, 200, 300, 400, 500, 150, 250, 350, 450 }; static final int defaultFWHM = 20; static final int defaultArea = 50; static final double delta = 2; static final double lambda = 0.1; static final boolean backgroundDominated = true; static final boolean autoStopping = true; static final double threshold = 0.10; static final int numPeaks = -1; static final int smoothing = 5; static final DoubleDataset xAxis = (DoubleDataset) DatasetFactory.createRange(0, dataRange, 1, Dataset.FLOAT64); static final long seed = 12357L; protected static String name; @Before public void setup() { FunctionFactory.registerFunctions(true, Gaussian.class, Lorentzian.class, PseudoVoigt.class, PearsonVII.class); } @Test public void testPearsonVIIFitting() { int i = defaultPeakPos.length; int[] peakPos = new int[i]; for (int j = 0; j < i; j++) { peakPos[j] = defaultPeakPos[j]; } DoubleDataset testingPeaks = generatePearsonVII(i); try { fittingTest(peakPos, testingPeaks, FunctionFactory.getPeakFunctionClass("PearsonVII")); } catch (Exception e) { System.out.println(e); fail("The number of generated peaks did not match the number of peaks found using " + name); } } @Test public void testGaussianFitting() { int i = defaultPeakPos.length; int[] peakPos = new int[i]; for (int j = 0; j < i; j++) { peakPos[j] = defaultPeakPos[j]; } DoubleDataset testingPeaks = generateGaussianPeaks(i); try { fittingTest(peakPos, testingPeaks, FunctionFactory.getPeakFunctionClass("Gaussian")); } catch (Exception e) { System.out.println(e); fail("The number of generated peaks did not match the number of peaks found using " + name); } } @Test public void testPseudoVoigt() { int i = defaultPeakPos.length; int[] peakPos = new int[i]; for (int j = 0; j < i; j++) { peakPos[j] = defaultPeakPos[j]; } DoubleDataset testingPeaks = generatePseudoVoigt(i); try { fittingTest(peakPos, testingPeaks, FunctionFactory.getPeakFunctionClass("PseudoVoigt")); } catch (Exception e) { System.out.println(e); fail("The number of generated peaks did not match the number of peaks found using " + name); } } @Test public void testLorentzianFitting() { int i = defaultPeakPos.length; int[] peakPos = new int[i]; for (int j = 0; j < i; j++) { peakPos[j] = defaultPeakPos[j]; } DoubleDataset testingPeaks = generateLorentzianPeaks(i); try { fittingTest(peakPos, testingPeaks, FunctionFactory.getPeakFunctionClass("Lorentzian")); } catch (Exception e) { System.out.println(e); fail("The number of generated peaks did not match the number of peaks found using " + name); } } private DoubleDataset generatePseudoVoigt(int numPeaks) { CompositeFunction function = new CompositeFunction(); if (numPeaks > defaultPeakPos.length) numPeaks = defaultPeakPos.length; for (int i = 0; i < numPeaks; i++) { function.addFunction(new PseudoVoigt(defaultPeakPos[i] - 20, defaultPeakPos[i] + 20, defaultFWHM, defaultArea)); } DoubleDataset data = function.calculateValues(xAxis); return (DoubleDataset) Maths.add(data, generateNoisePlusBackground()); } private DoubleDataset generatePearsonVII(int numPeaks) { CompositeFunction function = new CompositeFunction(); if (numPeaks > defaultPeakPos.length) numPeaks = defaultPeakPos.length; for (int i = 0; i < numPeaks; i++) { function.addFunction(new PearsonVII(defaultPeakPos[i] - 20, defaultPeakPos[i] + 20, defaultFWHM, defaultArea)); } DoubleDataset data = function.calculateValues(xAxis); return (DoubleDataset) Maths.add(data, generateNoisePlusBackground()); } private DoubleDataset generateLorentzianPeaks(int numPeaks) { CompositeFunction function = new CompositeFunction(); if (numPeaks > defaultPeakPos.length) numPeaks = defaultPeakPos.length; for (int i = 0; i < numPeaks; i++) { function.addFunction(new Lorentzian(defaultPeakPos[i] - 20, defaultPeakPos[i] + 20, defaultArea, defaultFWHM/2)); } DoubleDataset data = function.calculateValues(xAxis); return (DoubleDataset) Maths.add(data, generateNoisePlusBackground()); } private DoubleDataset generateGaussianPeaks(int numPeaks) { CompositeFunction function = new CompositeFunction(); if (numPeaks > defaultPeakPos.length) numPeaks = defaultPeakPos.length; for (int i = 0; i < numPeaks; i++) { function.addFunction(new Gaussian(defaultPeakPos[i] - 20, defaultPeakPos[i] + 20, defaultFWHM, defaultArea)); } DoubleDataset data = function.calculateValues(xAxis); return (DoubleDataset) Maths.add(data, generateNoisePlusBackground()); } private DoubleDataset generateNoisePlusBackground() { return generateBackground(); } private DoubleDataset generateBackground() { CompositeFunction comp = new CompositeFunction(); comp.addFunction(new Gaussian(-10, 10, dataRange / 4, dataRange / 2)); return comp.calculateValues(DatasetFactory.createRange(DoubleDataset.class, dataRange)); } @SuppressWarnings("unused") private DoubleDataset addNoiseToDataSet(DoubleDataset data) { Random.seed(123568); DoubleDataset noise = (DoubleDataset) Random.poisson(lambda, dataRange).cast(Dataset.FLOAT64); noise = (DoubleDataset) Maths.multiply(noise, 0.01); return (DoubleDataset) Maths.add(data, noise); } private void fittingTest(int[] peakPos, DoubleDataset data, Class<? extends IPeak> mypeakClass) { //TODO FIXME Once Generic1DFitter updated, remove this cast. List<CompositeFunction> fittedPeakList = Generic1DFitter.fitPeakFunctions(xAxis, data,mypeakClass, new GeneticAlg(0.0001, seed), smoothing, numPeaks, threshold, autoStopping, backgroundDominated); double[] fittedPeakPos = new double[fittedPeakList.size()]; int i = 0; for (CompositeFunction p : fittedPeakList) { fittedPeakPos[i++] = p.getPeak(0).getPosition(); } Arrays.sort(fittedPeakPos); Arrays.sort(peakPos); assertEquals("The number of peaks found was not the same as generated", peakPos.length, fittedPeakPos.length); for (int k = 0; k < fittedPeakPos.length; k++) { assertEquals(peakPos[k], fittedPeakPos[k], delta); } } abstract public IOptimizer createOptimizer(); }