/*-
* 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.List;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.DoubleDataset;
import org.eclipse.january.dataset.Maths;
/**
* The class for cylindrical components of the experimental target. This class
* is <code>public</code> because it needs to be visible in the
* uk...xpdf.operations package.
*
* Interpretation of Euler angles:
* At zero Euler angles, the plate is perpendicular to the beam, with edges
* vertical and horizontal. Yaw and pitch both tilt the plate non-perpendicular
* to the beam. Roll is the angle of the edges to the lab frame horizontal and
* vertical.
*
* @author Timothy Spain timothy.spain@diamond.ac.uk
* @since 2015-09-11
*
*/
public class XPDFComponentPlate extends XPDFComponentGeometry {
/**
* Empty constructor
*/
public XPDFComponentPlate() {
super();
}
/**
* Copy constructor from another plate.
* @param inPlate
* plate to be copied
*/
public XPDFComponentPlate(XPDFComponentPlate inPlate) {
super(inPlate);
}
/**
* Copy constructor from another geometric object.
* @param inGeom
* geometry to be copied
*/
public XPDFComponentPlate(XPDFComponentGeometry inGeom) {
super(inGeom);
}
/**
* Clone method.
*/
@Override
protected XPDFComponentGeometry clone() {
return new XPDFComponentPlate(this);
}
/**
* Returns the shape of this plate.
*/
@Override
public String getShape() {
return "plate";
}
/**
* Calculates the illuminated volume.
* <p>
* Return the illuminated volume of this plate, given the beam data. The
* beam is assumed to be centred on the plate.
*/
@Override
public double getIlluminatedVolume(XPDFBeamData beamData) {
// Matching DK's python code, even though the dimensions are incorrect
// TODO: Multiply by another length
return beamData.getBeamHeight()*beamData.getBeamWidth()*Math.abs(this.rInner-this.rOuter);
}
/**
* Returns the path length upstream of the given points.
*/
@Override
public Dataset getUpstreamPathLength(Dataset x, Dataset y, Dataset z) {
// Thickness of the plate
return DatasetFactory.zeros(x, DoubleDataset.class).fill(Math.abs(rInner -rOuter));
}
/**
* Returns the path length downstream of the given points.
*/
@Override
public Dataset getDownstreamPathLength(Dataset x, Dataset y, Dataset z,
double gamma, double delta) {
// Thickness of the plate, multiplied by the secant of the scattering angles
return DatasetFactory.zeros(x, DoubleDataset.class).fill(Math.abs(rInner - rOuter)/(Math.cos(gamma)*Math.cos(delta)));
}
/**
* Calculates the absorption correction map when attenuatorGeometry is attenuating.
*/
@Override
public Dataset calculateAbsorptionCorrections(Dataset gamma, Dataset delta,
XPDFComponentGeometry attenuatorGeometry, double attenuationCoefficient,
XPDFBeamData beamData,
boolean doUpstreamAbsorption, boolean doDownstreamAbsorption) {
// Height of the plate that needs to be considered
double windowHeight = Math.max(rOuter*Math.tan((double) delta.max()), beamData.getBeamHeight()/2);
double thickness = rOuter - rInner;
double log2XSteps = Math.round(Math.log((double) gridSize*windowHeight/thickness/2)/2/Math.log(2.0));
int xSteps = (int)Math.round(Math.pow(2.0, log2XSteps));
int zSteps = gridSize/xSteps;
double dX = windowHeight/xSteps;
double dZ = thickness/zSteps;
Dataset x1D = DatasetFactory.createRange(DoubleDataset.class, -windowHeight+dZ/2, windowHeight-dZ/2, dZ);
Dataset z1D = DatasetFactory.createRange(DoubleDataset.class, rInner+dX/2, rOuter-dX/2, dX);
// Account for the streamality of the plate. Can a plate be both upstream and downstream?
if (isUpstream && isDownstream) {
Dataset z1DTemp = z1D;
z1D = DatasetFactory.zeros(DoubleDataset.class, 2*z1D.getSize());
for (int i = 0; i < zSteps; i++) {
z1D.set(z1DTemp.getDouble(i), i);
z1D.set(z1DTemp.getDouble(i), 2*zSteps-1-i);
}
} else if (isUpstream) {
Maths.multiply(z1D, -1);
} else if (isDownstream) {
; // Do nothing, the coordinates are correct
} else {
// No streamality is no attenuation. Return an attenuation array of 0
return DatasetFactory.zeros(gamma, DoubleDataset.class);
}
// TODO: Is this the best way to expand a Dataset?
Dataset xPlate = DatasetFactory.zeros(DoubleDataset.class, x1D.getSize(), z1D.getSize());
Dataset zPlate = DatasetFactory.zeros(DoubleDataset.class, x1D.getSize(), z1D.getSize());
for (int i = 0; i<xSteps; i++) {
for (int k = 0; k<zSteps; k++) {
xPlate.set(x1D.getDouble(i), i, k);
zPlate.set(z1D.getDouble(k), i, k);
}
}
// TODO: There has to be a better way to make a mask Dataset
Dataset illuminationPlate = DatasetFactory.ones(xPlate);
for (int i=0; i<xPlate.getShape()[0]; i++){
for (int k=0; k<zPlate.getShape()[1]; k++) {
if (Math.abs(xPlate.getDouble(i, k)) > beamData.getBeamHeight()/2)
illuminationPlate.set(0.0, i, k);
}
}
Dataset illuminatedVolume = Maths.multiply(illuminationPlate, dX*dZ);
Dataset yPlate = Maths.multiply(xPlate, 0.0);
Dataset upstreamPathLength, downstreamPathLength;
if (doUpstreamAbsorption) {
upstreamPathLength = attenuatorGeometry.getUpstreamPathLength(xPlate, yPlate, zPlate);
} else {
upstreamPathLength = DatasetFactory.zeros(xPlate);
}
Dataset absorptionCorrection = gamma.copy(DoubleDataset.class);
// Loop over all detector angles
for (int i = 0; i<gamma.getShape()[0]; i++) {
for (int k = 0; k<gamma.getShape()[1]; k++) {
if (doDownstreamAbsorption)
downstreamPathLength = attenuatorGeometry.getDownstreamPathLength(xPlate, yPlate, zPlate, gamma.getDouble(i, k), delta.getDouble(i, k));
else
downstreamPathLength = DatasetFactory.zeros(xPlate);
absorptionCorrection.set( (double)
Maths.multiply(
Maths.exp(
Maths.multiply(
-attenuationCoefficient,
Maths.add(
upstreamPathLength,
downstreamPathLength
)
)
),
illuminatedVolume
).sum() / (double) illuminatedVolume.sum(), i, k);
}
}
return absorptionCorrection;
}
@Override
public Dataset calculateFluorescence(Dataset gamma, Dataset delta,
List<XPDFComponentGeometry> attenuators,
List<Double> attenuationsIn, List<Double> attenuationsOut,
XPDFBeamData beamData, boolean doIncomingAbsorption,
boolean doOutgoingAbsorption) {
// TODO Auto-generated method stub
return null;
}
}