/*- * Copyright 2015 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.xpdf; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.january.dataset.Dataset; import org.eclipse.january.dataset.DatasetFactory; import org.eclipse.january.dataset.DoubleDataset; import org.eclipse.january.metadata.MetadataType; import uk.ac.diamond.scisoft.xpdf.metadata.XPDFMetadata; /** * Implementation of the XPDF metadata. * @author Timothy Spain timothy.spain@diamond.ac.uk * @since 2015-09-14 */ @SuppressWarnings("serial") public class XPDFMetadataImpl implements XPDFMetadata { private XPDFTargetComponent sampleData; private List<XPDFTargetComponent> containerData; private XPDFBeamData beamData; private XPDFAbsorptionMaps absorptionCorrectionMaps; private XPDFDetector tect; private Map<XPDFTargetComponent, XPDFBeamTrace> containerDataParameters; private XPDFBeamTrace sampleParameters; // no data, that is the input private XPDFBeamTrace emptyDataParameters; private double calCon; private double fluoro; private double lorchCutOff; /** * Empty constructor. */ public XPDFMetadataImpl() { sampleData = null; sampleParameters = null; containerData = new ArrayList<XPDFTargetComponent>(); containerDataParameters = new HashMap<XPDFTargetComponent, XPDFBeamTrace>(); beamData = null; emptyDataParameters = null; absorptionCorrectionMaps = null; tect = null; } /** * Copy constructor. * @param inMeta * metadata object to be copied. */ public XPDFMetadataImpl(XPDFMetadataImpl inMeta) { this.sampleData = (inMeta.sampleData != null) ? new XPDFTargetComponent(inMeta.sampleData) : null; this.sampleParameters = (inMeta.sampleParameters != null) ? new XPDFBeamTrace(inMeta.getSampleTrace()) : null; // Copy the container parameters and the container data this.containerData = new ArrayList<XPDFTargetComponent>(); this.containerDataParameters = new HashMap<XPDFTargetComponent, XPDFBeamTrace>(); for (XPDFTargetComponent container : inMeta.containerData) { if (container != null) { XPDFTargetComponent newContainer = new XPDFTargetComponent(container); this.containerData.add(newContainer); this.containerDataParameters.put(newContainer, new XPDFBeamTrace(inMeta.getContainerTrace(container))); } } this.beamData = (inMeta.beamData != null) ? new XPDFBeamData(inMeta.beamData) : null; this.emptyDataParameters = (inMeta.getEmptyTrace() != null) ? new XPDFBeamTrace(inMeta.getEmptyTrace()) : null; this.absorptionCorrectionMaps = (inMeta.absorptionCorrectionMaps != null) ? new XPDFAbsorptionMaps(inMeta.absorptionCorrectionMaps) : null; this.tect = (inMeta.tect != null) ? new XPDFDetector(inMeta.tect) : null; // Copy the additional parameters this.calCon = inMeta.calCon; this.fluoro = inMeta.fluoro; this.lorchCutOff = inMeta.lorchCutOff; } /** * Clone method. */ @Override public MetadataType clone() { return new XPDFMetadataImpl(this); } /** * Re-orders the containers, from the sample outwards. */ @Override public void reorderContainers(Map<Integer, Integer> newOrder) { // iterate over the keys in order int nKeys = newOrder.size(); List <XPDFTargetComponent> newContainerData = new ArrayList<XPDFTargetComponent>(); for (int iKey = 0; iKey < nKeys; iKey++) { newContainerData.add(iKey, this.containerData.get(newOrder.get(iKey))); } this.containerData = newContainerData; } /** * Gets the containers. */ @Override public List<XPDFTargetComponent> getContainers() { return containerData; } /** * Gets the sample. */ @Override public XPDFTargetComponent getSample() { return sampleData; } /** * Getter for the beam data. */ @Override public XPDFBeamData getBeam() { return beamData; } /** * Getter for the sample properties. * @return */ public XPDFTargetComponent getSampleData() { return sampleData; } /** * Setter for the sample properties. * @param sampleData * sample properties to be set. */ public void setSampleData(XPDFTargetComponent sampleData) { this.sampleData = sampleData; } /** * Getter for all the container data. * @return a list of all the containers's properties. */ public List<XPDFTargetComponent> getContainerData() { return containerData; } /** * Setter for all the container data * @param containerData * list of all the containers's properties. */ public void setContainerData(List<XPDFTargetComponent> containerData) { this.containerData = containerData; } /** * Setter for the beam data. * @param beamData * the beam data to be set. */ public void setBeamData(XPDFBeamData beamData) { this.beamData = beamData; } /** * Adds a container to the container list. * <p> * Add a single container to the end of the container list. If the list is * sorted, this is the outside of the (ordered) set of containers. * @param newContainer * container properies to be added. */ public void addContainer(XPDFTargetComponent newContainer) { if (containerData == null) containerData = new ArrayList<XPDFTargetComponent>(); this.containerData.add(newContainer); } /** * Gets the number of illuminated sample atoms. * <p> * A pass through function to return the number of atoms in the sample * illuminated by the X-ray beam. */ @Override public double getSampleIlluminatedAtoms() { return getSample().getForm().getIlluminatedAtoms(beamData); } /** * Calculates and returns the absorption maps for the target components, as * ordered in the list of containers, with the sample appended in position 0. */ @Override public XPDFAbsorptionMaps getAbsorptionMaps(Dataset delta, Dataset gamma) { // The intention is to be able to recall cached maps, if these are set // in the metadata. They are not, yet, so press on with the direct // calculations. The map uses a pair of integers in a string as an // index, based on the order in which they come in the list of components if (absorptionCorrectionMaps == null) { absorptionCorrectionMaps = new XPDFAbsorptionMaps(); absorptionCorrectionMaps.setGamma(gamma); absorptionCorrectionMaps.setDelta(delta); absorptionCorrectionMaps.setBeamData(beamData); // Add the target component forms, starting with the sample // List<XPDFComponentForm> formsList = new ArrayList<XPDFComponentForm>(); // formsList.add(sampleData.getForm()); // for (XPDFTargetComponent container : containerData) // formsList.add(container.getForm()); // // for (XPDFComponentForm form : formsList) { // absorptionCorrectionMaps.addForm(form); // } // // // for (XPDFComponentForm formScatterer : formsList) { // for (XPDFComponentForm formAttenuator : formsList) { // absorptionCorrectionMaps.setAbsorptionMap(formScatterer, formAttenuator, // formScatterer.getGeom().calculateAbsorptionCorrections(gamma, delta, formAttenuator.getGeom(), formAttenuator.getAttenuationCoefficient(beamData.getBeamEnergy()), beamData, true, true)); // } // } // absorptionCorrectionMaps.addForm(sampleData.getForm()); // for (XPDFTargetComponent container : containerData) // absorptionCorrectionMaps.addForm(container.getForm()); for (XPDFComponentForm targetForm : this.getFormList()) absorptionCorrectionMaps.addForm(new XPDFComponentForm(targetForm)); absorptionCorrectionMaps.calculateAbsorptionMaps(); // Note the less than or equal to // for (int iScatterer = 0; iScatterer <= containerData.size(); iScatterer++) { // XPDFTargetComponent scatterer = (iScatterer == 0) ? sampleData : containerData.get(iScatterer-1); // // for (int iAttenuator = 0; iAttenuator <= containerData.size(); iAttenuator++) { // XPDFTargetComponent attenuator = (iAttenuator == 0) ? sampleData : containerData.get(iAttenuator-1); // absorptionCorrectionMaps.setAbsorptionMap(iScatterer, iAttenuator, // scatterer.getForm().getGeom().calculateAbsorptionCorrections(gamma, delta, attenuator.getForm().getGeom(), attenuator.getForm().getAttenuationCoefficient(beamData.getBeamEnergy()), beamData, true, true)); // } // } } return absorptionCorrectionMaps; } @Override public List<XPDFComponentForm> getFormList() { List<XPDFComponentForm> formList = new ArrayList<XPDFComponentForm>(); formList.add(sampleData.getForm()); for (XPDFTargetComponent container : containerData) formList.add(container.getForm()); return formList; } /** * Setter for the detector object. * @param inTect * the detector to be assigned. */ public void setDetector(XPDFDetector inTect) { tect = inTect; } @Override public XPDFDetector getDetector() { return tect; } /** * Returns the sample fluorescence. */ @Override public Dataset getSampleFluorescence(Dataset gamma, Dataset delta) { XPDFCoordinates coords = new XPDFCoordinates(); coords.setGammaDelta(gamma, delta); return getSampleFluorescence(coords); } @Override public Dataset getSampleFluorescence(XPDFCoordinates coords) { Dataset totalSampleFluorescence = DatasetFactory.zeros(coords.getGamma()); ExecutorService ravager = Executors.newFixedThreadPool(4); // Why not 4? AtomicInteger counter = new AtomicInteger(0); long nLines = 0; Set<Future<Dataset>> futureSet = new HashSet<Future<Dataset>>(); for (XPDFFluorescentLine line : sampleData.getFluorescences(getBeam().getBeamEnergy())) { futureSet.add(ravager.submit(new SampleFluorescenceLineEvaluator(coords, getFormList(), getSampleData(), line, counter))); nLines++; } // Spin, checking if all the threads have completed while (counter.get() < nLines) try { Thread.sleep(100); } catch (InterruptedException iE) { ; // Do nothing: go to check on the results again } // Gather the parallel results for (Future<Dataset> lineFuture: futureSet) try { totalSampleFluorescence.iadd(lineFuture.get()); } catch (ExecutionException eE) { // Do nothing! // FIXME Do something! } catch (InterruptedException iE) { // Do nothing! // FIXME Do something! } ravager.shutdown(); totalSampleFluorescence.imultiply(tect.getSolidAngle()); return totalSampleFluorescence.squeeze(); } @Override public void defineUndefinedSamplesContainers() throws Exception { // Do nothing if the geometry is defined if (sampleData.getForm().getGeom() != null) return; XPDFTargetComponent outwardContainer = null, inwardContainer = null; // TODO: cope with the case where the sample is not the innermost component { inwardContainer = null; outwardContainer = containerData.get(0); } // Assume an outward container exists if (outwardContainer == null) throw new Exception("No outer container defined"); if (outwardContainer.getForm().getGeom() == null) throw new Exception("No geometry defined for the outer container"); if (inwardContainer != null && inwardContainer.getForm().getGeom() == null) throw new Exception("No geometry defined for the inner container"); if (inwardContainer != null && inwardContainer.getForm().getGeom().getShape() != outwardContainer.getForm().getGeom().getShape()) throw new Exception("Bounding container geometries differ") ; // Check both containers have the same geometry XPDFComponentGeometry geomMeta = null; switch (outwardContainer.getForm().getGeom().getShape().toLowerCase()) { case("cylinder") : geomMeta = new XPDFComponentCylinder(); break; case("plate") : geomMeta = new XPDFComponentPlate(); } double inner, outer; if (inwardContainer == null) inner = 0.0; else inner = inwardContainer.getForm().getGeom().getDistances()[1]; outer = outwardContainer.getForm().getGeom().getDistances()[0]; geomMeta.setDistances(inner, outer); // the sample orientation in space should match its container geomMeta.setEulerAngles(outwardContainer.getForm().getGeom().getEulerAngles()); sampleData.getForm().setGeom(geomMeta); } @Override public XPDFBeamTrace getSampleTrace() { return sampleParameters; } /** * Sets the {@link XPDF BeamTrace} object of the sample. * <p> * The object contains the count time and the monitor-relative flux. The trace field should be null. * @param traceParameters * object to set. */ public void setSampleTrace(XPDFBeamTrace traceParameters) { sampleParameters = new XPDFBeamTrace(traceParameters); } @Override public XPDFBeamTrace getEmptyTrace() { return emptyDataParameters; } /** * Sets the {@link XPDF BeamTrace} object of the empty beam. * <p> * The object contains the count time and the monitor-relative flux. * @param traceDataParameters * object to set */ public void setEmptyTrace(XPDFBeamTrace traceDataParameters) { emptyDataParameters = new XPDFBeamTrace(traceDataParameters); } @Override public XPDFBeamTrace getContainerTrace(XPDFTargetComponent container) { if (!containerData.contains(container)) return null; return containerDataParameters.get(container); } /** * Sets the {@link XPDF BeamTrace} object of a specific container. * <p> * The object contains the count time and the monitor-relative flux. The * container doesn't have to be in the List of containers, but probably should be. * @param container * container to bind the data and parameters to. * @param traceDataParameters * object to set. */ public void setContainerTrace(XPDFTargetComponent container, XPDFBeamTrace traceDataParameters) { containerDataParameters.put(container, new XPDFBeamTrace(traceDataParameters)); } @Override public double getCalibrationConstant() { return calCon; } @Override public void setCalibrationConstant(double calCon) { this.calCon = calCon; } @Override public double getFluorescenceScale() { return fluoro; } @Override public void setFluorescenceScale(double scale) { this.fluoro = scale; } @Override public double getLorchCutOff() { return lorchCutOff; } @Override public void setLorchCutOff(double cutOff) { this.lorchCutOff = cutOff; } private class SampleFluorescenceLineEvaluator implements Callable<Dataset>{ private XPDFCoordinates coords; private List<XPDFComponentForm> attenuatorForms; private XPDFTargetComponent sample; private XPDFFluorescentLine line; private AtomicInteger counter; public SampleFluorescenceLineEvaluator(XPDFCoordinates coords, List<XPDFComponentForm> attenuatorForms, XPDFTargetComponent sample, XPDFFluorescentLine line, AtomicInteger counter) { this.coords = coords; this.attenuatorForms = attenuatorForms; this.sample = sample; this.line = line; this.counter = counter; } public Dataset call() { List<XPDFComponentGeometry> attenuators = new ArrayList<XPDFComponentGeometry>(); List<Double> attenuationsIn = new ArrayList<Double>(), attenuationsOut = new ArrayList<Double>(); for (XPDFComponentForm componentForm : attenuatorForms) { attenuators.add(componentForm.getGeom()); attenuationsIn.add(componentForm.getSubstance().getAttenuationCoefficient(beamData.getBeamEnergy())); attenuationsOut.add(componentForm.getSubstance().getAttenuationCoefficient(line.getEnergy())); } Dataset oneLineFluorescence = sampleData.getForm().getGeom().calculateFluorescence(coords.getGamma(), coords.getDelta(), attenuators, attenuationsIn, attenuationsOut, beamData, true, true); double lineXSection = line.getCrossSection(); double lineNumberDensity = sampleData.getNumberDensity(line.getFluorescentZ()); oneLineFluorescence.imultiply(lineXSection*lineNumberDensity); Dataset detectorCorrectedOLF = tect.applyTransmissionCorrection(oneLineFluorescence, coords.getTwoTheta(), line.getEnergy()); counter.incrementAndGet(); return detectorCorrectedOLF; } } }