/** * Execute the MoveInstrumentComponent on all (named) subcomponents * @param toCorrect : Workspace to correct * @param detector : Detector or DetectorGroup * @param sample : Sample Component * @param upOffset : Up offset to apply * @param acrossOffset : Across offset to apply * @param detectorPosition: Actual detector or detector group position. */ void SpecularReflectionPositionCorrect::moveDetectors( API::MatrixWorkspace_sptr toCorrect, IComponent_const_sptr detector, IComponent_const_sptr sample, const double &upOffset, const double &acrossOffset, const V3D &detectorPosition) { auto instrument = toCorrect->getInstrument(); const V3D samplePosition = sample->getPos(); auto referenceFrame = instrument->getReferenceFrame(); if (auto groupDetector = boost::dynamic_pointer_cast<const DetectorGroup>( detector)) // Do we have a group of detectors { const std::vector<IDetector_const_sptr> detectors = groupDetector->getDetectors(); const bool commonParent = hasCommonParent(detectors); if (commonParent) { /* * Same parent component. So lets move that. */ moveDetectors(toCorrect, detectors[0], sample, upOffset, acrossOffset, detectorPosition); // Recursive call } else { /* * We have to move individual components. */ for (size_t i = 0; i < detectors.size(); ++i) { moveDetectors(toCorrect, detectors[i], sample, upOffset, acrossOffset, detectorPosition); // Recursive call } } } else { auto moveComponentAlg = this->createChildAlgorithm("MoveInstrumentComponent"); moveComponentAlg->initialize(); moveComponentAlg->setProperty("Workspace", toCorrect); IComponent_const_sptr root = getParentComponent(detector); const std::string componentName = root->getName(); moveComponentAlg->setProperty("ComponentName", componentName); moveComponentAlg->setProperty("RelativePosition", false); // Movements moveComponentAlg->setProperty( referenceFrame->pointingAlongBeamAxis(), detectorPosition.scalar_prod(referenceFrame->vecPointingAlongBeam())); moveComponentAlg->setProperty(referenceFrame->pointingHorizontalAxis(), acrossOffset); const double detectorVerticalPosition = detectorPosition.scalar_prod(referenceFrame->vecPointingUp()); const double rootVerticalPosition = root->getPos().scalar_prod(referenceFrame->vecPointingUp()); const double dm = rootVerticalPosition - detectorVerticalPosition; moveComponentAlg->setProperty( referenceFrame->pointingUpAxis(), samplePosition.scalar_prod(referenceFrame->vecPointingUp()) + upOffset + dm); // Execute the movement. moveComponentAlg->execute(); } }
void PeaksOnSurface::checkTouchPoint(const V3D& touchPoint,const V3D& normal,const V3D& faceVertex) const { if( normal.scalar_prod(touchPoint - faceVertex) != 0) { throw std::runtime_error("Debugging. Calculation is wrong. touch point should always be on the plane!"); // Remove this line later. Check that geometry is setup properly. } }
bool lineIntersectsSphere(const V3D &line, const V3D &lineStart, const V3D &peakCenter, const double peakRadius) { V3D peakToStart = peakCenter - lineStart; V3D unitLine = line; unitLine.normalize(); double proj = peakToStart.scalar_prod(unitLine); // All we are doing here is // projecting the peak to // segment start vector onto // the segment itself. V3D closestPointOnSegment; if (proj <= 0) // The projection is outside the segment. So use the start // point of the segment. { closestPointOnSegment = lineStart; // Start of line } else if (proj >= line.norm()) // The projection is greater than the segment // length. So use the end point of the // segment. { closestPointOnSegment = lineStart + line; // End of line. } else // The projection falls somewhere between the start and end of the line // segment. { V3D projectionVector = unitLine * proj; closestPointOnSegment = projectionVector + lineStart; } return (peakCenter - closestPointOnSegment).norm() <= peakRadius; }
/** * Correct the position of the detectors based on the input theta value. * @param toCorrect : Workspace to correct detector posisitions on. * @param twoThetaInDeg : 2* Theta in degrees to use in correction calculations. * @param sample : Pointer to the sample * @param detector : Pointer to a given detector */ void SpecularReflectionPositionCorrect::correctPosition( API::MatrixWorkspace_sptr toCorrect, const double &twoThetaInDeg, IComponent_const_sptr sample, IComponent_const_sptr detector) { auto instrument = toCorrect->getInstrument(); const V3D detectorPosition = detector->getPos(); const V3D samplePosition = sample->getPos(); const V3D sampleToDetector = detectorPosition - samplePosition; auto referenceFrame = instrument->getReferenceFrame(); const double twoThetaInRad = twoThetaInDeg * (M_PI / 180.0); double acrossOffset = 0; double beamOffset = sampleToDetector.scalar_prod( referenceFrame ->vecPointingAlongBeam()); // We just recalculate beam offset. double upOffset = (beamOffset * std::tan(0.5 * twoThetaInRad)); // We only correct vertical position // Apply the movements. moveDetectors(toCorrect, detector, sample, upOffset, acrossOffset, detector->getPos()); }
/** Get the two theata angle signed according the quadrant @param observer :: The observer position @param axis :: The axis @param instrumentUp :: instrument up direction. @return The angle */ double Detector::getSignedTwoTheta(const V3D &observer, const V3D &axis, const V3D &instrumentUp) const { const V3D sampleDetVec = this->getPos() - observer; double angle = sampleDetVec.angle(axis); V3D cross = axis.cross_prod(sampleDetVec); V3D normToSurface = axis.cross_prod(instrumentUp); if (normToSurface.scalar_prod(cross) < 0) { angle *= -1; } return angle; }
/** * Change UB to a new matrix corresponding to a unit cell with the sides * in increasing order of magnitude. This is used to arrange the UB matrix * for an orthorhombic cell into a standard order. * * @param UB on input this should correspond to an orthorhombic cell. * On output, it will correspond to an orthorhombic cell with * sides in increasing order. */ void ConventionalCell::SetSidesIncreasing( Kernel::DblMatrix & UB ) { V3D a_dir; V3D b_dir; V3D c_dir; IndexingUtils::GetABC( UB, a_dir, b_dir, c_dir ); std::vector<V3D> edges; edges.push_back( a_dir ); edges.push_back( b_dir ); edges.push_back( c_dir ); std::sort( edges.begin(), edges.end(), IndexingUtils::CompareMagnitude ); V3D a = edges[0]; V3D b = edges[1]; V3D c = edges[2]; V3D acrossb = a.cross_prod( b ); // keep a,b,c right handed if ( acrossb.scalar_prod(c) < 0 ) { c = c * (-1); } IndexingUtils::GetUB( UB, a, b, c ); }
/** Corrects a spectra for the detector efficiency calculated from detector information Gets the detector information and uses this to calculate its efficiency * @param spectraIn :: index of the spectrum to get the efficiency for * @throw invalid_argument if the shape of a detector is isn't a cylinder aligned along one axis * @throw runtime_error if the SpectraDetectorMap has not been filled * @throw NotFoundError if the detector or its gas pressure or wall thickness were not found */ void DetectorEfficiencyCor::correctForEfficiency(int64_t spectraIn) { IDetector_const_sptr det = m_inputWS->getDetector(spectraIn); if( det->isMonitor() || det->isMasked() ) { return; } MantidVec & yout = m_outputWS->dataY(spectraIn); MantidVec & eout = m_outputWS->dataE(spectraIn); // Need the original values so this is not a reference const MantidVec yValues = m_inputWS->readY(spectraIn); const MantidVec eValues = m_inputWS->readE(spectraIn); // get a pointer to the detectors that created the spectrum const std::set<detid_t> dets = m_inputWS->getSpectrum(spectraIn)->getDetectorIDs(); std::set<detid_t>::const_iterator it = dets.begin(); std::set<detid_t>::const_iterator iend = dets.end(); if ( it == iend ) { throw Exception::NotFoundError("No detectors found", spectraIn); } // Storage for the reciprocal wave vectors that are calculated as the //correction proceeds std::vector<double> oneOverWaveVectors(yValues.size()); for( ; it != iend ; ++it ) { IDetector_const_sptr det_member = m_inputWS->getInstrument()->getDetector(*it); Parameter_sptr par = m_paraMap->get(det_member.get(),"3He(atm)"); if ( !par ) { throw Exception::NotFoundError("3He(atm)", spectraIn); } const double atms = par->value<double>(); par = m_paraMap->get(det_member.get(),"wallT(m)"); if ( !par ) { throw Exception::NotFoundError("wallT(m)", spectraIn); } const double wallThickness = par->value<double>(); double detRadius(0.0); V3D detAxis; getDetectorGeometry(det_member, detRadius, detAxis); // now get the sin of the angle, it's the magnitude of the cross product of unit vector along the detector tube axis and a unit vector directed from the sample to the detector centre V3D vectorFromSample = det_member->getPos() - m_samplePos; vectorFromSample.normalize(); Quat rot = det_member->getRotation(); // rotate the original cylinder object axis to get the detector axis in the actual instrument rot.rotate(detAxis); detAxis.normalize(); // Scalar product is quicker than cross product double cosTheta = detAxis.scalar_prod(vectorFromSample); double sinTheta = std::sqrt(1.0 - cosTheta*cosTheta); // Detector constant const double det_const = g_helium_prefactor*(detRadius - wallThickness)*atms/sinTheta; MantidVec::const_iterator yinItr = yValues.begin(); MantidVec::const_iterator einItr = eValues.begin(); MantidVec::iterator youtItr = yout.begin(); MantidVec::iterator eoutItr = eout.begin(); MantidVec::const_iterator xItr = m_inputWS->readX(spectraIn).begin(); std::vector<double>::iterator wavItr = oneOverWaveVectors.begin(); for( ; youtItr != yout.end(); ++youtItr, ++eoutItr) { if( it == dets.begin() ) { *youtItr = 0.0; *eoutItr = 0.0; *wavItr = calculateOneOverK(*xItr, *(xItr + 1 )); } const double oneOverWave = *wavItr; const double factor = 1.0/detectorEfficiency(det_const*oneOverWave); *youtItr += (*yinItr)*factor; *eoutItr += (*einItr)*factor; ++yinItr; ++einItr; ++xItr; ++wavItr; } } }
/** Set the outline of the assembly. Creates an Object and sets m_shape point to * it. * All child components must be detectors and positioned along a straight line * and have the same shape. * The shape can be either a box or a cylinder. * @return The shape of the outline: "cylinder", "box", ... */ boost::shared_ptr<Object> ObjCompAssembly::createOutline() { if (group.empty()) { throw Kernel::Exception::InstrumentDefinitionError("Empty ObjCompAssembly"); } if (nelements() < 2) { g_log.warning("Creating outline with fewer than 2 elements. The outline displayed may be inaccurate."); } // Get information about the shape and size of a detector std::string type; int otype; std::vector<Kernel::V3D> vectors; double radius, height; boost::shared_ptr<const Object> obj = group.front()->shape(); if (!obj) { throw Kernel::Exception::InstrumentDefinitionError( "Found ObjComponent without shape"); } obj->GetObjectGeom(otype, vectors, radius, height); if (otype == 1) { type = "box"; } else if (otype == 3) { type = "cylinder"; } else { throw std::runtime_error( "IDF \"outline\" option is only allowed for assemblies containing " "components of types \"box\" or \"cylinder\"."); } // Calculate the dimensions of the outline object // find the 'moments of inertia' of the assembly double Ixx = 0, Iyy = 0, Izz = 0, Ixy = 0, Ixz = 0, Iyz = 0; V3D Cmass; // 'center of mass' of the assembly for (const_comp_it it = group.begin(); it != group.end(); it++) { V3D p = (**it).getRelativePos(); Cmass += p; } Cmass /= nelements(); for (const_comp_it it = group.begin(); it != group.end(); it++) { V3D p = (**it).getRelativePos(); double x = p.X() - Cmass.X(), x2 = x * x; double y = p.Y() - Cmass.Y(), y2 = y * y; double z = p.Z() - Cmass.Z(), z2 = z * z; Ixx += y2 + z2; Iyy += x2 + z2; Izz += y2 + x2; Ixy -= x * y; Ixz -= x * z; Iyz -= y * z; } // principal axes of the outline shape // vz defines the line through all pixel centres V3D vx, vy, vz; if (Ixx == 0) // pixels along x axis { vx = V3D(0, 1, 0); vy = V3D(0, 0, 1); vz = V3D(1, 0, 0); } else if (Iyy == 0) // pixels along y axis { vx = V3D(0, 0, 1); vy = V3D(1, 0, 0); vz = V3D(0, 1, 0); } else if (Izz == 0) // pixels along z axis { vx = V3D(1, 0, 0); vy = V3D(0, 1, 0); vz = V3D(0, 0, 1); } else { // Either the detectors are not perfectrly aligned or // vz is parallel to neither of the 3 axis x,y,z // This code is unfinished DblMatrix II(3, 3), Vec(3, 3), D(3, 3); II[0][0] = Ixx; II[0][1] = Ixy; II[0][2] = Ixz; II[1][0] = Ixy; II[1][1] = Iyy; II[1][2] = Iyz; II[2][0] = Ixz; II[2][1] = Iyz; II[2][2] = Izz; std::cerr << "Inertia matrix: " << II << II.determinant() << '\n'; II.Diagonalise(Vec, D); std::cerr << "Vectors: " << Vec << '\n'; std::cerr << "Moments: " << D << '\n'; vx = V3D(Vec[0][0], Vec[1][0], Vec[2][0]); vy = V3D(Vec[0][1], Vec[1][1], Vec[2][1]); vz = V3D(Vec[0][2], Vec[1][2], Vec[2][2]); } // find assembly sizes along the principal axes double hx, hy, hz; // sizes along x,y, and z axis // maximum displacements from the mass centre along axis vx,vy, and vz // in positive (p) and negative (n) directions. // positive displacements are positive numbers and negative ones are negative double hxn = 0, hyn = 0, hzn = 0; double hxp = 0, hyp = 0, hzp = 0; for (const_comp_it it = group.begin(); it != group.end(); it++) { // displacement vector of a detector V3D p = (**it).getRelativePos() - Cmass; // its projection on the vx axis double h = p.scalar_prod(vx); if (h > hxp) hxp = h; if (h < hxn) hxn = h; // its projection on the vy axis h = p.scalar_prod(vy); if (h > hyp) hyp = h; if (h < hyn) hyn = h; // its projection on the vz axis h = p.scalar_prod(vz); if (h > hzp) hzp = h; if (h < hzn) hzn = h; } // calc the assembly sizes hx = hxp - hxn; hy = hyp - hyn; hz = hzp - hzn; // hx and hy must be practically zero if (hx > 1e-3 || hy > 1e-3) // arbitrary numbers { throw Kernel::Exception::InstrumentDefinitionError( "Detectors of a ObjCompAssembly do not lie on a staraight line"); } // determine the order of the detectors to make sure that the texture // coordinates are correct bool firstAtBottom; // first detector is at the bottom of the outline shape // the bottom end is the one with the negative displacement from the centre firstAtBottom = ((**group.begin()).getRelativePos() - Cmass).scalar_prod(vz) < 0; // form the input string for the ShapeFactory std::ostringstream obj_str; if (type == "box") { if (hz == 0) hz = 0.1; hx = hy = 0; height = 0; V3D p0 = vectors[0]; for (size_t i = 1; i < vectors.size(); ++i) { V3D p = vectors[i] - p0; double h = fabs(p.scalar_prod(vx)); if (h > hx) hx = h; h = fabs(p.scalar_prod(vy)); if (h > hy) hy = h; height = fabs(p.scalar_prod(vz)); if (h > height) height = h; } vx *= hx / 2; vy *= hy / 2; vz *= hzp + height / 2; if (!firstAtBottom) { vz = vz * (-1); } // define the outline shape as cuboid V3D p_lfb = Cmass - vx - vy - vz; V3D p_lft = Cmass - vx - vy + vz; V3D p_lbb = Cmass - vx + vy - vz; V3D p_rfb = Cmass + vx - vy - vz; obj_str << "<cuboid id=\"shape\">"; obj_str << "<left-front-bottom-point "; obj_str << "x=\"" << p_lfb.X(); obj_str << "\" y=\"" << p_lfb.Y(); obj_str << "\" z=\"" << p_lfb.Z(); obj_str << "\" />"; obj_str << "<left-front-top-point "; obj_str << "x=\"" << p_lft.X(); obj_str << "\" y=\"" << p_lft.Y(); obj_str << "\" z=\"" << p_lft.Z(); obj_str << "\" />"; obj_str << "<left-back-bottom-point "; obj_str << "x=\"" << p_lbb.X(); obj_str << "\" y=\"" << p_lbb.Y(); obj_str << "\" z=\"" << p_lbb.Z(); obj_str << "\" />"; obj_str << "<right-front-bottom-point "; obj_str << "x=\"" << p_rfb.X(); obj_str << "\" y=\"" << p_rfb.Y(); obj_str << "\" z=\"" << p_rfb.Z(); obj_str << "\" />"; obj_str << "</cuboid>"; } else if (type == "cylinder") { // the outline is one detector height short hz += height; // shift Cmass to the end of the cylinder where the first detector is if (firstAtBottom) { Cmass += vz * hzn; } else { hzp += height; Cmass += vz * hzp; // inverse the vz axis vz = vz * (-1); } obj_str << "<segmented-cylinder id=\"stick\">"; obj_str << "<centre-of-bottom-base "; obj_str << "x=\"" << Cmass.X(); obj_str << "\" y=\"" << Cmass.Y(); obj_str << "\" z=\"" << Cmass.Z(); obj_str << "\" />"; obj_str << "<axis x=\"" << vz.X() << "\" y=\"" << vz.Y() << "\" z=\"" << vz.Z() << "\" /> "; obj_str << "<radius val=\"" << radius << "\" />"; obj_str << "<height val=\"" << hz << "\" />"; obj_str << "</segmented-cylinder>"; } if (!obj_str.str().empty()) { boost::shared_ptr<Object> s = ShapeFactory().createShape(obj_str.str()); setOutline(s); return s; } return boost::shared_ptr<Object>(); }
bool NiggliCell::MakeNiggliUB(const DblMatrix &UB, DblMatrix &newUB) { V3D a; V3D b; V3D c; if (!OrientedLattice::GetABC(UB, a, b, c)) { return false; } V3D v1; V3D v2; V3D v3; // first make a list of linear combinations // of vectors a,b,c with coefficients up to 5 std::vector<V3D> directions; int N_coeff = 5; for (int i = -N_coeff; i <= N_coeff; i++) { for (int j = -N_coeff; j <= N_coeff; j++) { for (int k = -N_coeff; k <= N_coeff; k++) { if (i != 0 || j != 0 || k != 0) { v1 = a * i; v2 = b * j; v3 = c * k; V3D sum(v1); sum += v2; sum += v3; directions.push_back(sum); } } } } // next sort the list of linear combinations // in order of increasing length std::sort(directions.begin(), directions.end(), V3D::CompareMagnitude); // next form a list of possible UB matrices // using sides from the list of linear // combinations, using shorter directions first. // Keep trying more until 25 UBs are found. // Only keep UBs corresponding to cells with // at least a minimum cell volume std::vector<DblMatrix> UB_list; size_t num_needed = 25; size_t max_to_try = 5; while (UB_list.size() < num_needed && max_to_try < directions.size()) { max_to_try *= 2; size_t num_to_try = std::min(max_to_try, directions.size()); V3D acrossb; double vol = 0; double min_vol = .1f; // what should this be? 0.1 works OK, but...? for (size_t i = 0; i < num_to_try - 2; i++) { a = directions[i]; for (size_t j = i + 1; j < num_to_try - 1; j++) { b = directions[j]; acrossb = a.cross_prod(b); for (size_t k = j + 1; k < num_to_try; k++) { c = directions[k]; vol = acrossb.scalar_prod(c); if (vol > min_vol && HasNiggliAngles(a, b, c, 0.01)) { Matrix<double> new_tran(3, 3, false); OrientedLattice::GetUB(new_tran, a, b, c); UB_list.push_back(new_tran); } } } } } // if no valid UBs could be formed, return // false and the original UB if (UB_list.empty()) { newUB = UB; return false; } // now sort the UB's in order of increasing // total side length |a|+|b|+|c| std::sort(UB_list.begin(), UB_list.end(), CompareABCsum); // keep only those UB's with total side length // within .1% of the first one. This can't // be much larger or "bad" UBs are made for // some tests with 5% noise double length_tol = 0.001; double total_length; std::vector<DblMatrix> short_list; short_list.push_back(UB_list[0]); OrientedLattice::GetABC(short_list[0], a, b, c); total_length = a.norm() + b.norm() + c.norm(); bool got_short_list = false; size_t i = 1; while (i < UB_list.size() && !got_short_list) { OrientedLattice::GetABC(UB_list[i], v1, v2, v3); double next_length = v1.norm() + v2.norm() + v3.norm(); if (fabs(next_length - total_length) / total_length < length_tol) short_list.push_back(UB_list[i]); else got_short_list = true; i++; } // now sort on the basis of difference of cell // angles from 90 degrees and return the one // with angles most different from 90 std::sort(short_list.begin(), short_list.end(), CompareDiffFrom90); newUB = short_list[0]; return true; }