/** Constructor with normal and point * * @param normal :: normal to the plane. Points that are in the direction of the *normal of the plane are considered to be bounded by it. * @param point :: any point that is on the plane */ MDPlane::MDPlane(const Mantid::Kernel::VMD &normal, const Mantid::Kernel::VMD &point) { m_nd = normal.getNumDims(); if ((m_nd < 1) || (m_nd > 100)) throw std::invalid_argument( "MDPlane::ctor(): Invalid number of dimensions in the normal vector !"); if (point.getNumDims() != normal.getNumDims()) throw std::invalid_argument("MDPlane::ctor(): Inconsistent number of " "dimensions in the normal/point vectors!"); construct(normal, point); }
/** Apply the transformation to an input vector (as a VMD type). * This wraps the apply(in,out) method (and will be slower!) * * @param inputVector :: an inD-length vector * @return the output vector as VMD */ Mantid::Kernel::VMD CoordTransform::applyVMD(const Mantid::Kernel::VMD &inputVector) const { if (inputVector.getNumDims() != inD) throw std::runtime_error("CoordTransform::apply(): inputVector has the " "wrong number of coordinates!"); coord_t *outArray = new coord_t[outD]; this->apply(inputVector.getBareArray(), outArray); VMD out(outD, outArray); delete[] outArray; return out; }
/** Constructor * * @param workspace :: IMDWorkspace to plot * @param logScale :: true to plot Y in log scale * @param start :: start point in N-dimensions of the line * @param end :: end point in N-dimensions of the line * @param normalize :: method for normalizing the line * @param isDistribution :: is this a distribution (divide by bin width?) * @return */ MantidQwtIMDWorkspaceData::MantidQwtIMDWorkspaceData(Mantid::API::IMDWorkspace_const_sptr workspace, const bool logScale, Mantid::Kernel::VMD start, Mantid::Kernel::VMD end, Mantid::API::MDNormalization normalize, bool isDistribution) : m_workspace(workspace), m_logScale(logScale), m_minPositive(0), m_preview(false), m_start(start), m_end(end), m_normalization(normalize), m_isDistribution(isDistribution), m_transform(NULL), m_plotAxis(PlotDistance), m_currentPlotAxis(PlotDistance) { if (start.getNumDims() == 1 && end.getNumDims() == 1) { if (start[0] == 0.0 && end[0] == 0.0) { // Default start and end. Find the limits Mantid::Geometry::VecIMDDimension_const_sptr nonIntegDims = m_workspace->getNonIntegratedDimensions(); std::string alongDim = ""; if (!nonIntegDims.empty()) alongDim = nonIntegDims[0]->getName(); else alongDim = m_workspace->getDimension(0)->getName(); size_t nd = m_workspace->getNumDims(); m_start = VMD(nd); m_end = VMD(nd); for (size_t d=0; d<nd; d++) { IMDDimension_const_sptr dim = m_workspace->getDimension(d); if (dim->getDimensionId() == alongDim) { // All the way through in the single dimension m_start[d] = dim->getMinimum(); m_end[d] = dim->getMaximum(); } else { // Mid point along each dimension m_start[d] = (dim->getMaximum() + dim->getMinimum()) / 2.0f; m_end[d] = m_start[d]; } } } } // Unit direction of the line m_dir = m_end - m_start; m_dir.normalize(); // And cache the X/Y values this->cacheLinePlot(); }
/** Set the width of the line in each dimensions * @param width :: vector for the width in each dimension. X dimension stands in for the XY plane width */ void LineViewer::setThickness(Mantid::Kernel::VMD width) { if (m_ws && width.getNumDims() != m_ws->getNumDims()) throw std::runtime_error("LineViewer::setThickness(): Invalid number of dimensions in the width vector."); m_thickness = width; updateStartEnd(); }
/** Set the end point of the line to integrate * @param end :: vector for the end point */ void LineViewer::setEnd(Mantid::Kernel::VMD end) { if (m_ws && end.getNumDims() != m_ws->getNumDims()) throw std::runtime_error("LineViewer::setEnd(): Invalid number of dimensions in the end vector."); m_end = end; updateStartEnd(); }
/** Set the start point of the line to integrate * @param start :: vector for the start point */ void LineViewer::setStart(Mantid::Kernel::VMD start) { if (m_ws && start.getNumDims() != m_ws->getNumDims()) throw std::runtime_error("LineViewer::setStart(): Invalid number of dimensions in the start vector."); m_start = start; updateStartEnd(); }
/** Constructor helper method * @param min :: nd-sized vector of the minimum edge of the box in each * dimension * @param max :: nd-sized vector of the maximum edge of the box * */ void MDBoxImplicitFunction::construct(const Mantid::Kernel::VMD &min, const Mantid::Kernel::VMD &max) { size_t nd = min.size(); if (max.size() != nd) throw std::invalid_argument( "MDBoxImplicitFunction::ctor(): Min and max vector sizes must match!"); if (nd == 0 || nd > 100) throw std::invalid_argument( "MDBoxImplicitFunction::ctor(): Invalid number of dimensions!"); double volume = 1; for (size_t d = 0; d < nd; d++) { volume *= (max[d] - min[d]); // Make two parallel planes per dimension // Normal on the min side, so it faces towards +X std::vector<coord_t> normal_min(nd, 0); normal_min[d] = +1.0; // Origin just needs to have its X set to the value. Other coords are // irrelevant std::vector<coord_t> origin_min(nd, 0); origin_min[d] = static_cast<coord_t>(min[d]); // Build the plane MDPlane p_min(normal_min, origin_min); this->addPlane(p_min); // Normal on the max side, so it faces towards -X std::vector<coord_t> normal_max(nd, 0); normal_max[d] = -1.0; // Origin just needs to have its X set to the value. Other coords are // irrelevant std::vector<coord_t> origin_max(nd, 0); origin_max[d] = static_cast<coord_t>(max[d]); // Build the plane MDPlane p_max(normal_max, origin_max); this->addPlane(p_max); } m_volume = volume; }
/** Build a coordinate transformation based on an origin and orthogonal basis vectors. * This can reduce the number of dimensions. For example: * * - The input position is X=(x,y,z) * - The origin is X0=(x0,y0,z0) * - The basis vectors are U and V (reducing from 3 to 2D) * - The output position u = (X-X0).U = X.U - X0.U = x*Ux + y*Uy + z*Uz + (X0.U) * - The output position v = (X-X0).V = X.V - X0.V = x*Vx + y*Vy + z*Vz + (X0.V) * * And this allows us to create the affine matrix: * * | Ux Uy Uz X0.U | | x | | u | * | Vx Vy Vz X0.V | | y | = | v | * | 0 0 0 1 | | z | | 1 | * | 1 | * * @param origin :: origin (in the inDimension), which corresponds to (0,0,...) in outD * @param axes :: a list of basis vectors. There must be outD vectors (one for each output dimension) * and each vector must be of length inD (all coordinates in the input dimension). * The vectors must be properly orthogonal: not coplanar or collinear. This is not checked! * @param scaling :: a vector of size outD of the scaling to perform in each of the * OUTPUT dimensions. * @throw if inconsistent vector sizes are received, or zero-length */ void CoordTransformAffine::buildOrthogonal(const Mantid::Kernel::VMD & origin, const std::vector< Mantid::Kernel::VMD> & axes, const Mantid::Kernel::VMD & scaling) { if (origin.size() != inD) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): the origin must be in the dimensions of the input workspace (length inD)."); if (axes.size() != outD) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): you must give as many basis vectors as there are dimensions in the output workspace."); if (scaling.size() != outD) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): the size of the scaling vector must be the same as the number of dimensions in the output workspace."); // Start with identity affineMatrix.identityMatrix(); for (size_t i=0; i<axes.size(); i++) { if (axes[i].length() == 0.0) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): one of the basis vector was of zero length."); if (axes[i].size() != inD) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): one of the basis vectors had the wrong number of dimensions (must be inD)."); // Normalize each axis to unity VMD basis = axes[i]; basis.normalize(); // The row of the affine matrix = the unit vector for (size_t j=0; j<basis.size(); j++) affineMatrix[i][j] = basis[j] * scaling[i]; // Now account for the translation coord_t transl = 0; for (size_t j=0; j<basis.size(); j++) transl += origin[j] * basis[j]; // dot product of origin * basis aka ( X0 . U ) // The last column of the matrix = the translation movement affineMatrix[i][inD] = -transl * scaling[i]; } // Copy into the raw matrix (for speed) copyRawMatrix(); }
/** Returns the signal (normalized by volume) at a given coordinates * or 0 if masked * * @param coords :: coordinate as a VMD vector * @param normalization :: how to normalize the signal returned * @return normalized signal */ signal_t IMDWorkspace::getSignalWithMaskAtVMD( const Mantid::Kernel::VMD &coords, const Mantid::API::MDNormalization &normalization) const { return this->getSignalWithMaskAtCoord(coords.getBareArray(), normalization); }
/** * Make 1D MatrixWorkspace */ void ConvertMDHistoToMatrixWorkspace::make1DWorkspace() { IMDHistoWorkspace_sptr inputWorkspace = getProperty("InputWorkspace"); // This code is copied from MantidQwtIMDWorkspaceData Mantid::Geometry::VecIMDDimension_const_sptr nonIntegDims = inputWorkspace->getNonIntegratedDimensions(); std::string alongDim = ""; if (!nonIntegDims.empty()) alongDim = nonIntegDims[0]->getDimensionId(); else alongDim = inputWorkspace->getDimension(0)->getDimensionId(); size_t nd = inputWorkspace->getNumDims(); Mantid::Kernel::VMD start = VMD(nd); Mantid::Kernel::VMD end = VMD(nd); size_t id = 0; for (size_t d = 0; d < nd; d++) { Mantid::Geometry::IMDDimension_const_sptr dim = inputWorkspace->getDimension(d); if (dim->getDimensionId() == alongDim) { // All the way through in the single dimension start[d] = dim->getMinimum(); end[d] = dim->getMaximum(); id = d; // We take the first non integrated dimension to be the diemnsion // of interest. } else { // Mid point along each dimension start[d] = (dim->getMaximum() + dim->getMinimum()) / 2.0f; end[d] = start[d]; } } // Unit direction of the line Mantid::Kernel::VMD dir = end - start; dir.normalize(); std::string normProp = getPropertyValue("Normalization"); Mantid::API::MDNormalization normalization; if (normProp == "NoNormalization") { normalization = NoNormalization; } else if (normProp == "VolumeNormalization") { normalization = VolumeNormalization; } else if (normProp == "NumEventsNormalization") { normalization = NumEventsNormalization; } else { normalization = NoNormalization; } auto line = inputWorkspace->getLineData(start, end, normalization); MatrixWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().create( "Workspace2D", 1, line.x.size(), line.y.size()); outputWorkspace->dataY(0).assign(line.y.begin(), line.y.end()); outputWorkspace->dataE(0).assign(line.e.begin(), line.e.end()); const size_t numberTransformsToOriginal = inputWorkspace->getNumberTransformsToOriginal(); CoordTransform_const_sptr transform = boost::make_shared<NullCoordTransform>(inputWorkspace->getNumDims()); if (numberTransformsToOriginal > 0) { const size_t indexToLastTransform = numberTransformsToOriginal - 1; transform = CoordTransform_const_sptr( inputWorkspace->getTransformToOriginal(indexToLastTransform), NullDeleter()); } assert(line.x.size() == outputWorkspace->dataX(0).size()); std::string xAxisLabel = inputWorkspace->getDimension(id)->getName(); const bool autoFind = this->getProperty("FindXAxis"); if (autoFind) { // We look to the original workspace if possbible to find the dimension of // interest to plot against. id = findXAxis(start, end, transform.get(), inputWorkspace.get(), g_log, id, xAxisLabel); } for (size_t i = 0; i < line.x.size(); ++i) { // Coordinates in the workspace being plotted VMD wsCoord = start + dir * line.x[i]; VMD inTargetCoord = transform->applyVMD(wsCoord); outputWorkspace->dataX(0)[i] = inTargetCoord[id]; } boost::shared_ptr<Kernel::Units::Label> labelX = boost::dynamic_pointer_cast<Kernel::Units::Label>( Kernel::UnitFactory::Instance().create("Label")); labelX->setLabel(xAxisLabel); outputWorkspace->getAxis(0)->unit() = labelX; outputWorkspace->setYUnitLabel("Signal"); setProperty("OutputWorkspace", outputWorkspace); }
/** Obtain coordinates for a line plot through a MDWorkspace. * Cross the workspace from start to end points, recording the signal along the *lin at either bin boundaries, or halfway between bin boundaries (which is bin *centres if the line is dimension aligned). If recording halfway values then *omit points in masked bins. * * @param start :: coordinates of the start point of the line * @param end :: coordinates of the end point of the line * @param normalize :: how to normalize the signal * @returns :: LinePlot with x as the boundaries of the bins, relative * to start of the line, y set to the normalized signal for each bin with * Length = length(x) - 1 and e as the error vector for each bin. * @param bin_centres :: if true then record points halfway between bin *boundaries, otherwise record on bin boundaries */ IMDWorkspace::LinePlot MDHistoWorkspace::getLinePoints( const Mantid::Kernel::VMD &start, const Mantid::Kernel::VMD &end, Mantid::API::MDNormalization normalize, const bool bin_centres) const { LinePlot line; size_t nd = this->getNumDims(); if (start.getNumDims() != nd) throw std::runtime_error("Start point must have the same number of " "dimensions as the workspace."); if (end.getNumDims() != nd) throw std::runtime_error( "End point must have the same number of dimensions as the workspace."); // Unit-vector of the direction VMD dir = end - start; const auto length = dir.normalize(); // Vector with +1 where direction is positive, -1 where negative #define sgn(x) ((x < 0) ? -1.0f : ((x > 0.) ? 1.0f : 0.0f)) VMD dirSign(nd); for (size_t d = 0; d < nd; d++) { dirSign[d] = sgn(dir[d]); } const size_t BADINDEX = size_t(-1); // Dimensions of the workspace boost::scoped_array<size_t> index(new size_t[nd]); boost::scoped_array<size_t> numBins(new size_t[nd]); for (size_t d = 0; d < nd; d++) { IMDDimension_const_sptr dim = this->getDimension(d); index[d] = BADINDEX; numBins[d] = dim->getNBins(); } const std::set<coord_t> boundaries = getBinBoundariesOnLine(start, end, nd, dir, length); if (boundaries.empty()) { this->makeSinglePointWithNaN(line.x, line.y, line.e); // Require x.size() = y.size()+1 if recording bin boundaries if (!bin_centres) line.x.push_back(length); return line; } else { // Get the first point std::set<coord_t>::iterator it; it = boundaries.cbegin(); coord_t lastLinePos = *it; VMD lastPos = start + (dir * lastLinePos); if (!bin_centres) { line.x.push_back(lastLinePos); } ++it; coord_t linePos = 0; for (; it != boundaries.cend(); ++it) { // This is our current position along the line linePos = *it; // This is the full position at this boundary VMD pos = start + (dir * linePos); // Position in the middle of the bin VMD middle = (pos + lastPos) * 0.5; // Find the signal in this bin const auto linearIndex = this->getLinearIndexAtCoord(middle.getBareArray()); if (bin_centres && !this->getIsMaskedAt(linearIndex)) { coord_t bin_centrePos = static_cast<coord_t>((linePos + lastLinePos) * 0.5); line.x.push_back(bin_centrePos); } else if (!bin_centres) line.x.push_back(linePos); if (linearIndex < m_length) { auto normalizer = getNormalizationFactor(normalize, linearIndex); // And add the normalized signal/error to the list too auto signal = this->getSignalAt(linearIndex) * normalizer; if (boost::math::isinf(signal)) { // The plotting library (qwt) doesn't like infs. signal = std::numeric_limits<signal_t>::quiet_NaN(); } if (!bin_centres || !this->getIsMaskedAt(linearIndex)) { line.y.push_back(signal); line.e.push_back(this->getErrorAt(linearIndex) * normalizer); } // Save the position for next bin lastPos = pos; } else { // Invalid index. This shouldn't happen line.y.push_back(std::numeric_limits<signal_t>::quiet_NaN()); line.e.push_back(std::numeric_limits<signal_t>::quiet_NaN()); } lastLinePos = linePos; } // for each unique boundary // If all bins were masked if (line.x.size() == 0) { this->makeSinglePointWithNaN(line.x, line.y, line.e); } } return line; }