package uk.ac.diamond.scisoft.xpdf.operations;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.dawnsci.analysis.api.processing.Atomic;
import org.eclipse.dawnsci.analysis.api.processing.OperationData;
import org.eclipse.dawnsci.analysis.api.processing.OperationException;
import org.eclipse.dawnsci.analysis.api.processing.OperationRank;
import org.eclipse.dawnsci.analysis.dataset.operations.AbstractOperation;
import org.eclipse.january.IMonitor;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetUtils;
import org.eclipse.january.dataset.IDataset;
import uk.ac.diamond.scisoft.analysis.io.DiffractionMetadata;
import uk.ac.diamond.scisoft.xpdf.XPDFAbsorptionMaps;
import uk.ac.diamond.scisoft.xpdf.XPDFCalibration;
import uk.ac.diamond.scisoft.xpdf.XPDFCalibrationBase;
import uk.ac.diamond.scisoft.xpdf.XPDFCoordinates;
import uk.ac.diamond.scisoft.xpdf.XPDFQSquaredIntegrator;
import uk.ac.diamond.scisoft.xpdf.XPDFTargetComponent;
import uk.ac.diamond.scisoft.xpdf.metadata.XPDFMetadata;
/**
* Iterate the calibration constant for the XPDF data.
* @author Timothy Spain timothy.spain@diamond.ac.uk
* @since 2015-09-14
*
*/
@Atomic
public class XPDFIterateCalibrationConstantOperation extends
AbstractOperation<XPDFIterateCalibrationConstantModel, OperationData> {
private XPDFAbsorptionMaps cachedAbsorptionMaps;
private boolean isCachedMapsSorted;
@SuppressWarnings("unused")
private Dataset cachedSampleFluorescence;
private XPDFCalibrationBase cachedCalibration;
private DiffractionMetadata cacheKeyMetadata;
protected OperationData process(IDataset input, IMonitor monitor)
throws OperationException {
XPDFOperationChecker.checkXPDFMetadata(this, input, true, true, true);
Dataset absCor = null;
XPDFMetadata theXPDFMetadata = null;
// Get the metadata
theXPDFMetadata = input.getFirstMetadata(XPDFMetadata.class);
if (theXPDFMetadata == null) throw new OperationException(this, "XPDFMetadata not found.");
XPDFCalibrationBase theBase = getCachedCalibration(input);
// The per-data calibration derived from the cached base
XPDFCalibration theCalibration = new XPDFCalibration(theBase);
// Set the fluorescence parameters for the calibration.
if (model.isDoingFluorescence()) {
theCalibration.setDoFluorescence(true);
// Check for fixed scale fluorescence in the model, and set the fixed scale if necessary
if (model.isCalculatingFluorescence())
theCalibration.performFullFluorescence();
else
theCalibration.setFixedFluorescence(model.getFluorescenceScale());
} else {
theCalibration.setDoFluorescence(false);
}
int nIterations = model.getnIterations();
List<Dataset> backgroundSubtracted = new ArrayList<Dataset>();
// The 0th element is the sample
backgroundSubtracted.add(DatasetUtils.convertToDataset(input));
// Add the containers in order, innermost to outermost
for (XPDFTargetComponent container : theXPDFMetadata.getContainers()) {
backgroundSubtracted.add(theXPDFMetadata.getContainerTrace(container).getTrace());
}
theCalibration.setBackgroundSubtracted(backgroundSubtracted);
// for (int i = 0; i < nIterations; i++)
// absCor = theCalibration.iterate(true);
absCor = theCalibration.calibrate(nIterations, model.getNThreads(),this);
// Copy metadata, but preserve the errors, if they exist.
Dataset absCorError = (absCor.getErrors() != null) ? absCor.getErrors() : null;
copyMetadata(input, absCor);
if (absCorError != null)
absCor.setErrors(absCorError);
theXPDFMetadata = absCor.getFirstMetadata(XPDFMetadata.class);
// assign the calibration values to the XPDF metadata object
theXPDFMetadata.setCalibrationConstant(theCalibration.getCalibrationConstant());
theXPDFMetadata.setFluorescenceScale(theCalibration.getFluorescenceScale());
absCor.setName("Absorption Corrected");
return new OperationData(absCor);
}
/**
* Orders the list of containers.
* <p>
* Given a list of container XPDFTargetComponents, orders them by their
* larger distance (external radius). Matches with the logic of the python
* version.
* @param containers
* the list of containers to order
* @return a map keyed by the position in the new list, with a value of the
* position in the old list.
*/
static private Map<Integer, Integer> orderContainers(
List<XPDFTargetComponent> containers) {
final List<Double> outerRadii = new ArrayList<Double>();
// Populate a list of outer radii of the containers
for (XPDFTargetComponent aContainer : containers) {
outerRadii.add(aContainer.getForm().getGeom().getDistances()[1]);
}
// Java offers no way of getting the sorted indices from a Collection,
// so we have to do it ourselves
List<Integer> indices = new ArrayList<Integer>();
for (int i = 0; i<outerRadii.size(); i++) {
indices.add(i, i);
}
indices.sort(new Comparator<Integer>() {
@Override public int compare(final Integer i1, final Integer i2) {
return Double.compare(outerRadii.get(i1), outerRadii.get(i2));
}
});
Map<Integer, Integer> newOrder = new HashMap<Integer, Integer>();
for (int i = 0; i < indices.size(); i++) {
newOrder.put(i, indices.get(i));
}
return newOrder;
}
private XPDFCalibrationBase getCachedCalibration(IDataset input) {
DiffractionMetadata currentKeyMetadata = input.getFirstMetadata(DiffractionMetadata.class);
// Invalidate the cache, if the diffraction metadata on the input has changed
if (cacheKeyMetadata != null && (
!cacheKeyMetadata.getDetector2DProperties().equals(currentKeyMetadata.getDetector2DProperties()) ||
!cacheKeyMetadata.getDiffractionCrystalEnvironment().equals(currentKeyMetadata.getDiffractionCrystalEnvironment())
)) {
synchronized (this) {
if (cacheKeyMetadata != null && (
!cacheKeyMetadata.getDetector2DProperties().equals(currentKeyMetadata.getDetector2DProperties()) ||
!cacheKeyMetadata.getDiffractionCrystalEnvironment().equals(currentKeyMetadata.getDiffractionCrystalEnvironment())
)) {
cachedCalibration = null;
cacheKeyMetadata = null;
}
}
}
XPDFCalibrationBase theBase;
if (cachedCalibration == null) {
theBase = new XPDFCalibrationBase();
// The initial value of the calibration constant is 1e-16
theBase.initializeCalibrationConstant(1e-16);
XPDFMetadata theXPDFMetadata = null;
// Get the metadata
theXPDFMetadata = input.getFirstMetadata(XPDFMetadata.class);
if (theXPDFMetadata == null) throw new OperationException(this, "XPDFMetadata not found.");
// Sort the containers if requested
if (model.isSortContainers()) {
theXPDFMetadata.reorderContainers(orderContainers(theXPDFMetadata.getContainers()));
}
// Nullify the absorption map cache if the container sorting setting
// has been changed.
if (model.isSortContainers() != isCachedMapsSorted) {
synchronized (this) {
if (model.isSortContainers() != isCachedMapsSorted) {
cachedAbsorptionMaps = null;
isCachedMapsSorted = model.isSortContainers();
}
}
}
// Define the geometry of any components defined by their container(s).
try {
theXPDFMetadata.defineUndefinedSamplesContainers();
} catch (Exception e) {
throw new OperationException(this, "Could not define sample geometry: " + e.toString());
}
theBase.setSampleIlluminatedAtoms(theXPDFMetadata.getSampleIlluminatedAtoms());
// Get 2θ, the axis variable
if (XPDFCoordinates.coordinateMetadataProblems(DatasetUtils.convertToDataset(input)) != null)
throw new OperationException(this, XPDFCoordinates.coordinateMetadataProblems(DatasetUtils.convertToDataset(input)));
XPDFCoordinates coordinates = new XPDFCoordinates(DatasetUtils.convertToDataset(input));
// Dataset twoTheta = coordinates.getTwoTheta();
// Set up the q² integrator class
theBase.setqSquaredIntegrator(new XPDFQSquaredIntegrator(coordinates));//twoTheta, theXPDFMetadata.getBeam()));
theBase.setCoordinates(coordinates);
theBase.setSelfScattering(theXPDFMetadata.getSample());
theBase.setSelfScatteringDenominatorFromSample(theXPDFMetadata.getSample(), coordinates);
// localized cache with a double null check with sprinkles on top
XPDFAbsorptionMaps localAbsMaps = cachedAbsorptionMaps;
if (localAbsMaps == null || !(localAbsMaps.checkFormList(theXPDFMetadata.getFormList())) || model.getRegenerateAbsorptionMaps() == true) {
synchronized (this) {
localAbsMaps = cachedAbsorptionMaps;
if (localAbsMaps == null || !(localAbsMaps.checkFormList(theXPDFMetadata.getFormList())) || model.getRegenerateAbsorptionMaps() == true) {
// cachedAbsorptionMaps = localAbsMaps = theXPDFMetadata.getAbsorptionMaps(twoTheta.reshape(twoTheta.getSize(), 1), DatasetFactory.zeros(DoubleDataset.class, twoTheta.reshape(twoTheta.getSize(), 1)));
cachedAbsorptionMaps = localAbsMaps = theXPDFMetadata.getAbsorptionMaps(coordinates.getDelta(), coordinates.getGamma());
}
}
}
theBase.setBeamData(theXPDFMetadata.getBeam());
theBase.setDetector(theXPDFMetadata.getDetector());
// theCalibration.setAbsorptionMaps(theXPDFMetadata.getAbsorptionMaps(twoTheta.reshape(twoTheta.getSize(), 1), DatasetFactory.zeros(DoubleDataset.class, twoTheta.reshape(twoTheta.getSize(), 1))));
theBase.setAbsorptionMaps(localAbsMaps);
// Calculate the sample fluorescence, regardless of whether it is needed.
theBase.setSampleFluorescence(theXPDFMetadata.getSampleFluorescence(coordinates));
synchronized (this) {
if (cachedCalibration == null) {
cachedCalibration = theBase;
cacheKeyMetadata = currentKeyMetadata;
}
}
} else {
theBase = cachedCalibration;
}
return theBase;
}
@Override
public String getId() {
return "uk.ac.diamond.scisoft.xpdf.operations.XPDFIterateCalibrationConstantOperation";
}
@Override
public OperationRank getInputRank() {
return OperationRank.ANY;
}
@Override
public OperationRank getOutputRank() {
return OperationRank.SAME;
}
}