/*-
* Copyright (c) 2016 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.saxs;
// Imports from java
import java.util.Arrays;
// Imports from org.apache
import org.apache.commons.lang.ArrayUtils;
// Imports from org.eclipse.january
import org.eclipse.january.IMonitor;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.IDataset;
import org.eclipse.january.MetadataException;
import org.eclipse.january.metadata.AxesMetadata;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.metadata.MetadataFactory;
import uk.ac.diamond.scisoft.analysis.processing.operations.saxs.HermanOrientation1DModel.NumberOfPis;
// 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.OperationException;
import org.eclipse.dawnsci.analysis.dataset.operations.AbstractOperation;
// More information and the equation for the Herman Orientation Factor can be found in:
// Crystallization and orientation studies in polypropylene/single wall carbon nanotube composite
// A. R. Bhattacharyya, T. Sreekumar, T. Liu, S. Kumar, L. M. Ericson, R. H. Hauge and R. E. Smalley, Polymer, 2003, 44, 2373-2377.
// DOI: 10.1016/S0032-3861(03)00073-9
// @author Tim Snow
// The operation for a DAWN process to perform a Herman Orientation calculation on a given reduced dataset
public class HermanOrientation1DOperation extends AbstractOperation<HermanOrientation1DModel, OperationData> {
// Let's give this process an ID tag
@Override
public String getId() {
return "uk.ac.diamond.scisoft.analysis.processing.operations.saxs.HermanOrientation1DOperation";
}
// Before we start, let's make sure we know 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 originalDataset, IMonitor monitor) throws OperationException {
// Remove a dimension of the dataset and get the axes attached to it too
IDataset dataDataset = originalDataset.squeeze();
IDataset axesDataset = (IDataset) dataDataset.getFirstMetadata(AxesMetadata.class).getAxes()[0];
// Get the length of the dataset
int dataLength = dataDataset.getSize();
// Let's consider how much of the ring we're going to be evaluating as a function of pi
NumberOfPis piEnum = model.getIntegrationRange();
double piMultiplier = ((double) piEnum.getNumberOfPis()) / 2;
double hermanPiRange = piMultiplier * Math.PI;
double integrationRadianStep = 0.00;
// Before setting the angle to investigate
double integrationStartInDegrees = model.getIntegrationStartAngle();
double originalIntegrationStopInDegrees = ((180 / Math.PI) * hermanPiRange) + integrationStartInDegrees;
double correctedIntegrationStopInDegrees = originalIntegrationStopInDegrees;
// Prepare to move out of the dataset and into an array
float axes[] = new float[dataLength];
float data[] = new float[dataLength];
int startIndex = 0;
int endIndex = 0;
// Get the data into the array
for (int loopIter = 0; loopIter < dataLength; loopIter ++) {
axes[loopIter] = axesDataset.getFloat(loopIter);
data[loopIter] = dataDataset.getFloat(loopIter);
}
// Find the lower limit
for (int loopIter = 0; loopIter < dataLength; loopIter ++) {
if (axes[loopIter] >= integrationStartInDegrees) {
startIndex = loopIter;
break;
}
}
// Find the upper limit
if (originalIntegrationStopInDegrees >= 180.0) {
correctedIntegrationStopInDegrees = originalIntegrationStopInDegrees - 360;
}
for (int loopIter = 0; loopIter < dataLength; loopIter ++) {
if (axes[loopIter] >= correctedIntegrationStopInDegrees) {
endIndex = loopIter;
break;
}
}
// Now let's calculate the Herman Orientation Factor
double hermanOrientationFactor = 0;
if (originalIntegrationStopInDegrees <= 180.0) {
// Either if both limits are within 180 degrees
data = Arrays.copyOfRange(data, startIndex, endIndex);
integrationRadianStep = hermanPiRange / data.length;
hermanOrientationFactor = hermanIntegrator(data, integrationRadianStep);
}
else {
// Or if we have to loop around encompassing two ranges
data = ArrayUtils.addAll(Arrays.copyOfRange(data, startIndex, dataLength), Arrays.copyOfRange(data, 0, endIndex));
integrationRadianStep = hermanPiRange / data.length;
hermanOrientationFactor = hermanIntegrator(data, integrationRadianStep);
}
// Must move the HoF into a dataset for DAWN
// First up, let's create a one element dataset of a zero
int[] datasetSize = {1};
Dataset hermanOrientationDataset = DatasetFactory.zeros(1, datasetSize, Dataset.FLOAT64);
hermanOrientationDataset.setName("Herman Orientation Factor");
// Now we can stick in the calculated factor
hermanOrientationDataset.set(hermanOrientationFactor, 0);
// Let's create the new axis data for the HoF plot, in radians
AxesMetadata metadata;
// A try catch clause is needed for some reason...
try {
metadata = MetadataFactory.createMetadata(AxesMetadata.class, 1);
} catch (MetadataException metadataError) {
throw new OperationException(this, metadataError);
}
// Create the axis as a fixed length tied to the data array
metadata.setAxis(0, DatasetFactory.createRange(0.00, ((180 / Math.PI) * hermanPiRange), ((180 / Math.PI) * integrationRadianStep), Dataset.FLOAT64));
// Create the data dataset...
Dataset hermanRadialData = DatasetFactory.createFromObject(Dataset.FLOAT64, data);
// and stick in the axis metadata
hermanRadialData.setMetadata(metadata);
// Finally, we can create a new OperationData object for DAWN and return the Herman Orientation Factor
OperationData toReturn = new OperationData();
// Fill it
toReturn.setData(hermanRadialData);
toReturn.setAuxData(hermanOrientationDataset);
// And then return it
return toReturn;
}
private double hermanIntegrator(float[] data, double integrationRadianStep){
// Set up the data value array
double fractionNumerator = 0.00;
double fractionDenominator = 0.00;
double loopStepRadianValue = 0.00;
// Now let's set up the final variables required for the calculation
double hermanCReciprocal = 1 / model.getHermanCValue();
// Do the HoF mathematics
// Level two of the loop, this is to loop through the data points in the frame
for(int loopIter = 0; loopIter < data.length; loopIter++) {
// To save this from being calculated many times
// The component parts of the fraction
fractionNumerator += Math.pow(Math.cos(loopStepRadianValue), 2) * Math.sin(loopStepRadianValue) * data[loopIter];
fractionDenominator += data[loopIter] * Math.sin(loopStepRadianValue);
loopStepRadianValue += integrationRadianStep;
}
// Perform the calculation for this frame
return (hermanCReciprocal * (((3 * (fractionNumerator / fractionDenominator)) - 1) / 2));
}
}