/*- * Copyright (c) 2017 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.processing.operations.fitting; // Imports from org.eclipse.january import org.eclipse.january.IMonitor; import org.eclipse.january.dataset.Slice; import org.eclipse.january.dataset.Dataset; import org.eclipse.january.dataset.IDataset; import org.eclipse.january.DatasetException; import org.eclipse.january.MetadataException; import org.eclipse.january.dataset.DatasetUtils; import org.eclipse.january.metadata.AxesMetadata; import org.eclipse.january.dataset.DatasetFactory; import org.eclipse.january.metadata.MetadataFactory; // Imports from org.eclipse.dawnsci import org.eclipse.dawnsci.analysis.api.processing.OperationData; import org.eclipse.dawnsci.analysis.api.processing.OperationRank; import org.eclipse.dawnsci.analysis.api.processing.PlotAdditionalData; import org.eclipse.dawnsci.analysis.api.processing.OperationException; import org.eclipse.dawnsci.analysis.dataset.operations.AbstractOperation; // Imports from uk.ac.diamond import uk.ac.diamond.scisoft.analysis.fitting.Fitter; import uk.ac.diamond.scisoft.analysis.fitting.functions.StraightLine; // @author Tim Snow //A straightforward y = mx + c line fitter @PlotAdditionalData(onInput = false, dataName = "Linear Fit") public class LinearFittingOperation extends AbstractOperation<LinearFittingModel, OperationData>{ // First let's declare our process ID tag @Override public String getId() { return "uk.ac.diamond.scisoft.analysis.processing.operations.fitting.LinearFittingOperation"; } // Then we'll create some placeholders for data to be stored in private Dataset fittedYaxis; // Now, how many dimensions of data are going in... @Override public OperationRank getInputRank() { return OperationRank.ONE; } // ...and out @Override public OperationRank getOutputRank() { return OperationRank.ONE; } // Now let's define the main calculation process @Override public OperationData process(IDataset inputDataset, IMonitor monitor) throws OperationException { // Next, we'll extract out the x axis (q) dataset from the input Dataset xAxis; // Just in case we don't have an x-axis (as we could do with an x axis) try { xAxis = DatasetUtils.convertToDataset(inputDataset.getFirstMetadata(AxesMetadata.class).getAxis(0)[0].getSlice()); } catch (DatasetException xAxisError) { throw new OperationException(this, xAxisError); } // Extract out the y axis (intensity) from the input Dataset yAxis = DatasetUtils.convertToDataset(inputDataset); // Get out the start and end values of the fitting range double[] fittingROI = model.getFittingRange(); // Perform the fitting StraightLine linearFit = this.fitLinearData(xAxis, yAxis, fittingROI, inputDataset.getSize()); // Extract out the fitting parameters double xGradient = linearFit.getParameterValue(0); double constant = linearFit.getParameterValue(1); this.fittedYaxis = DatasetFactory.zeros(yAxis.getSize()); // Assuming there were nice numbers, regenerate from the x-axis if (Double.isFinite(xGradient) && Double.isFinite(constant)) { for (int loopIter = 0; loopIter < yAxis.getSize(); loopIter ++) { // y = (m * x) + c double fitVariable = (xGradient * xAxis.getDouble(loopIter)) + constant; this.fittedYaxis.set(fitVariable, loopIter); } } // Just to see what's going on AxesMetadata xAxisMetadata; // We'll create the xAxis used in the regression for plotting try { xAxisMetadata = MetadataFactory.createMetadata(AxesMetadata.class, 1); xAxisMetadata.setAxis(0, xAxis); } catch (MetadataException xAxisError) { throw new OperationException(this, xAxisError.getMessage()); } // Filling the fit dataset with the processed x axis this.fittedYaxis.setName("Linear Fit"); this.fittedYaxis.setMetadata(xAxisMetadata); // Creating a home for the gradient data Dataset gradientDataset = DatasetFactory.createFromObject(xGradient, 1); gradientDataset.setName("m term from y = mx + c fit"); // Creating a home for the intercept data Dataset constantDataset = DatasetFactory.createFromObject(constant, 1); constantDataset.setName("c term from y = mx + c fit"); // Before creating the OperationData object to save everything in OperationData toReturn = new OperationData(inputDataset); // And all the other variables toReturn.setAuxData(this.fittedYaxis, gradientDataset, constantDataset); // And then returning it return toReturn; } // A method to fit data, within limits, and calculate the linear fit returning the fitting parameters for plotting later public StraightLine fitLinearData(Dataset xAxis, Dataset yAxis, double[] fittingROI, int dataLength) { // Create some placeholders int startIndex = 0; int endIndex = 0; // Assuming that we've been given some values if (fittingROI == null) { startIndex = 0; endIndex = dataLength; } // Go and find them! else { // Just to make sure the indexing is right, lowest number first if (fittingROI[0] < fittingROI[1]) { startIndex = DatasetUtils.findIndexGreaterThanOrEqualTo(xAxis, fittingROI[0]); endIndex = DatasetUtils.findIndexGreaterThanOrEqualTo(xAxis, fittingROI[1]); } // Or we handle for this else { startIndex = DatasetUtils.findIndexGreaterThanOrEqualTo(xAxis, fittingROI[1]); endIndex = DatasetUtils.findIndexGreaterThanOrEqualTo(xAxis, fittingROI[0]); } } // Next up, we'll slice the datasets down to the size of interest Slice regionOfInterest = new Slice(startIndex, endIndex, 1); Dataset xSlice = xAxis.getSlice(regionOfInterest); Dataset ySlice = yAxis.getSlice(regionOfInterest); // Set up a place to place the fitting parameters StraightLine linearFit = new StraightLine(); // Try to do the fitting on the new processed slices try { Fitter.llsqFit(new Dataset[] {xSlice}, ySlice, linearFit); } catch (Exception fittingError) { System.err.println("Exception performing linear fit in LinearFittingOperation(): " + fittingError.toString()); } // Then return it return linearFit; } }