/** 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; }
/** Set the U rotation matrix, to provide the transformation, which translate *an * arbitrary vector V expressed in RLU (hkl) * into another coordinate system defined by vectors u and v, expressed in RLU *(hkl) * Author: Alex Buts * @param u :: first vector of new coordinate system (in hkl units) * @param v :: second vector of the new coordinate system * @return the U matrix calculated * The transformation from old coordinate system to new coordinate system is *performed by * the whole UB matrix **/ const DblMatrix &OrientedLattice::setUFromVectors(const V3D &u, const V3D &v) { const DblMatrix &BMatrix = this->getB(); V3D buVec = BMatrix * u; V3D bvVec = BMatrix * v; // try to make an orthonormal system if (buVec.norm2() < 1e-10) throw std::invalid_argument("|B.u|~0"); if (bvVec.norm2() < 1e-10) throw std::invalid_argument("|B.v|~0"); buVec.normalize(); // 1st unit vector, along Bu V3D bwVec = buVec.cross_prod(bvVec); if (bwVec.normalize() < 1e-5) throw std::invalid_argument( "u and v are parallel"); // 3rd unit vector, perpendicular to Bu,Bv bvVec = bwVec.cross_prod( buVec); // 2nd unit vector, perpendicular to Bu, in the Bu,Bv plane DblMatrix tau(3, 3), lab(3, 3), U(3, 3); /*lab = U tau / 0 1 0 \ /bu[0] bv[0] bw[0]\ | 0 0 1 | = U |bu[1] bv[1] bw[1]| \ 1 0 0 / \bu[2] bv[2] bw[2]/ */ lab[0][1] = 1.; lab[1][2] = 1.; lab[2][0] = 1.; tau[0][0] = buVec[0]; tau[0][1] = bvVec[0]; tau[0][2] = bwVec[0]; tau[1][0] = buVec[1]; tau[1][1] = bvVec[1]; tau[1][2] = bwVec[1]; tau[2][0] = buVec[2]; tau[2][1] = bvVec[2]; tau[2][2] = bwVec[2]; tau.Invert(); U = lab * tau; this->setU(U); return getU(); }
/** * 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 ); }
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; }