.. _program_listing_file_dx2_goniometer.cxx: Program Listing for File goniometer.cxx ======================================= |exhale_lsh| :ref:`Return to documentation for file ` (``dx2/goniometer.cxx``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "dx2/goniometer.hpp" #include Matrix3d axis_and_angle_as_matrix(Vector3d axis, double angle, bool deg) { double q0 = 0.0; double q1 = 0.0; double q2 = 0.0; double q3 = 0.0; if (deg) { angle *= M_PI / 180.0; } if (!(std::fmod(angle, 2.0 * M_PI))) { q0 = 1.0; } else { double h = 0.5 * angle; q0 = std::cos(h); double s = std::sin(h); Vector3d n = axis / axis.norm(); q1 = n[0] * s; q2 = n[1] * s; q3 = n[2] * s; } Matrix3d m{{2 * (q0 * q0 + q1 * q1) - 1, 2 * (q1 * q2 - q0 * q3), 2 * (q1 * q3 + q0 * q2)}, {2 * (q1 * q2 + q0 * q3), 2 * (q0 * q0 + q2 * q2) - 1, 2 * (q2 * q3 - q0 * q1)}, {2 * (q1 * q3 - q0 * q2), 2 * (q2 * q3 + q0 * q1), 2 * (q0 * q0 + q3 * q3) - 1}}; return m; } void Goniometer::init() { // Sets the matrices from the axes and angles setting_rotation_ = calculate_setting_rotation(); sample_rotation_ = calculate_sample_rotation(); rotation_axis_ = axes_[scan_axis_]; } Matrix3d Goniometer::calculate_setting_rotation() { Matrix3d setting_rotation{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; for (std::size_t i = scan_axis_ + 1; i < axes_.size(); i++) { Matrix3d R = axis_and_angle_as_matrix(axes_[i], angles_[i], true); setting_rotation = R * setting_rotation; } return setting_rotation; } Matrix3d Goniometer::calculate_sample_rotation() { Matrix3d sample_rotation{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; for (std::size_t i = 0; i < scan_axis_; i++) { Matrix3d R = axis_and_angle_as_matrix(axes_[i], angles_[i], true); sample_rotation = R * sample_rotation; } return sample_rotation; } Goniometer::Goniometer(std::vector axes, std::vector angles, std::vector names, std::size_t scan_axis) : axes_{axes.begin(), axes.end()}, angles_{angles.begin(), angles.end()}, names_{names.begin(), names.end()}, scan_axis_{scan_axis} { if (scan_axis_ >= axes_.size()) { throw std::invalid_argument( "Goniometer scan axis number is out of range of axis length"); } init(); } Goniometer::Goniometer(Matrix3d sample_rotation, Vector3d rotation_axis, Matrix3d setting_rotation) : sample_rotation_{sample_rotation}, rotation_axis_{rotation_axis}, setting_rotation_{setting_rotation} {} Matrix3d Goniometer::get_setting_rotation() const { return setting_rotation_; } Matrix3d Goniometer::get_sample_rotation() const { return sample_rotation_; } Vector3d Goniometer::get_rotation_axis() const { return rotation_axis_; } Goniometer::Goniometer(json goniometer_data) { // The goniometer data can either be single or multi axis form. std::vector multi_axis_keys = {"axes", "angles", "names", "scan_axis"}; std::vector single_axis_keys = { "rotation_axis", "fixed_rotation", "setting_rotation"}; for (const auto &key : multi_axis_keys) { if (goniometer_data.find(key) == goniometer_data.end()) { // Could be a single axis gonio - they only provide rotation, fixed and // setting for (const auto &akey : single_axis_keys) { if (goniometer_data.find(akey) == goniometer_data.end()) { throw std::invalid_argument("Key " + key + " is missing from the input goniometer " "JSON - treating as single axis but" + " key " + akey + " also missing."); } } // We can create from the rotation axis data. rotation_axis_ = Vector3d(goniometer_data["rotation_axis"][0], goniometer_data["rotation_axis"][1], goniometer_data["rotation_axis"][2]); json setting = goniometer_data["setting_rotation"]; setting_rotation_ << setting[0], setting[1], setting[2], setting[3], setting[4], setting[5], setting[6], setting[7], setting[8]; // F json sample = goniometer_data["fixed_rotation"]; sample_rotation_ << sample[0], sample[1], sample[2], sample[3], sample[4], sample[5], sample[6], sample[7], sample[8]; // S return; } } std::vector axes; std::vector names; std::vector angles; for (json::iterator it = goniometer_data["axes"].begin(); it != goniometer_data["axes"].end(); it++) { Vector3d axis; axis[0] = (*it)[0]; axis[1] = (*it)[1]; axis[2] = (*it)[2]; axes.push_back(axis); } axes_ = axes; for (json::iterator it = goniometer_data["angles"].begin(); it != goniometer_data["angles"].end(); it++) { angles.push_back(*it); } angles_ = angles; for (json::iterator it = goniometer_data["names"].begin(); it != goniometer_data["names"].end(); it++) { names.push_back(*it); } names_ = names; scan_axis_ = goniometer_data["scan_axis"]; init(); } json Goniometer::to_json() const { json goniometer_data; if (axes_.size() > 0) { // Multi-axis format goniometer_data["axes"] = axes_; goniometer_data["angles"] = angles_; goniometer_data["names"] = names_; goniometer_data["scan_axis"] = scan_axis_; } else { // Single-axis format goniometer_data["rotation_axis"] = rotation_axis_; goniometer_data["fixed_rotation"] = std::vector{ sample_rotation_(0, 0), sample_rotation_(0, 1), sample_rotation_(0, 2), sample_rotation_(1, 0), sample_rotation_(1, 1), sample_rotation_(1, 2), sample_rotation_(2, 0), sample_rotation_(2, 1), sample_rotation_(2, 2)}; goniometer_data["setting_rotation"] = std::vector{setting_rotation_(0, 0), setting_rotation_(0, 1), setting_rotation_(0, 2), setting_rotation_(1, 0), setting_rotation_(1, 1), setting_rotation_(1, 2), setting_rotation_(2, 0), setting_rotation_(2, 1), setting_rotation_(2, 2)}; } return goniometer_data; }