Commit 0725d9a1 authored by Simon Spannagel's avatar Simon Spannagel
Browse files

Merge branch 'master' into metronome

parents cff5ac7c efc7b9a6
Pipeline #434762 passed with stages
in 5 minutes and 8 seconds
......@@ -7,6 +7,7 @@ ADD_LIBRARY(CorryvreckanCore SHARED
detector/Detector.cpp
utils/log.cpp
utils/unit.cpp
utils/text.cpp
clipboard/Clipboard.cpp
config/ConfigManager.cpp
config/ConfigReader.cpp
......
......@@ -16,7 +16,7 @@
#include <string>
#include <vector>
#include "core/utils/string.h"
#include "core/utils/text.h"
#include "exceptions.h"
namespace corryvreckan {
......@@ -142,12 +142,21 @@ namespace corryvreckan {
// TODO [doc] Provide second template parameter to specify the vector type to return it in
std::vector<std::string> getPathArray(const std::string& key, bool check_exists = false) const;
/**
* @brief Set value for a key in a given type with units
* @param key Key to set value of
* @param val Value to assign to the key
* @param units List of possible output units
*/
template <typename T> void set(const std::string& key, const T& val, std::initializer_list<std::string> units);
/**
* @brief Set value for a key in a given type
* @param key Key to set value of
* @param val Value to assign to the key
*/
template <typename T> void set(const std::string& key, const T& val);
/**
* @brief Set list of values for a key in a given type
* @param key Key to set values of
......@@ -156,6 +165,13 @@ namespace corryvreckan {
// TODO [doc] Provide second template parameter to specify the vector type to return it in
template <typename T> void setArray(const std::string& key, const std::vector<T>& val);
/**
* @brief Set matrix of values for a key in a given type
* @param key Key to set values of
* @param val List of values to assign to the key
*/
template <typename T> void setMatrix(const std::string& key, const Matrix<T>& val);
/**
* @brief Set default value for a key only if it is not defined yet
* @param key Key to possible set value of
......
......@@ -90,6 +90,10 @@ namespace corryvreckan {
Matrix<T> matrix;
auto node = parse_value(str);
for(auto& child : node->children) {
if(child->children.empty()) {
throw std::invalid_argument("matrix has less than two dimensions");
}
std::vector<T> array;
// Create subarray of matrix
for(auto& subchild : child->children) {
......@@ -99,10 +103,6 @@ namespace corryvreckan {
throw InvalidKeyError(key, getName(), subchild->value, typeid(T), e.what());
}
}
if(!child->value.empty()) {
throw std::invalid_argument("matrix has less than two dimensions");
}
matrix.push_back(array);
}
return matrix;
......@@ -129,14 +129,50 @@ namespace corryvreckan {
template <typename T> void Configuration::set(const std::string& key, const T& val) {
config_[key] = corryvreckan::to_string(val);
}
template <typename T>
void Configuration::set(const std::string& key, const T& val, std::initializer_list<std::string> units) {
auto split = corryvreckan::split<Units::UnitType>(corryvreckan::to_string(val));
std::string ret_str;
for(auto& element : split) {
ret_str += Units::display(element, units);
ret_str += ",";
}
ret_str.pop_back();
config_[key] = ret_str;
}
template <typename T> void Configuration::setArray(const std::string& key, const std::vector<T>& val) {
// NOTE: not the most elegant way to support arrays
std::string str;
for(auto& el : val) {
str += corryvreckan::to_string(el);
str += ",";
str += corryvreckan::to_string(val);
}
set(key, str);
str.pop_back();
config_[key] = str;
}
template <typename T> void Configuration::setMatrix(const std::string& key, const Matrix<T>& val) {
// NOTE: not the most elegant way to support arrays
if(val.empty()) {
return;
}
std::string str = "[";
for(auto& col : val) {
str += "[";
for(auto& el : col) {
str += corryvreckan::to_string(el);
str += ",";
}
str.pop_back();
str += "],";
}
str.pop_back();
str += "]";
config_[key] = str;
}
template <typename T> void Configuration::setDefault(const std::string& key, const T& val) {
......
......@@ -31,6 +31,10 @@ Detector::Detector(const Configuration& config) {
auto npixels = config.get<ROOT::Math::DisplacementVector2D<Cartesian2D<int>>>("number_of_pixels");
// Size of the pixels
m_pitch = config.get<ROOT::Math::XYVector>("pixel_pitch");
// Intrinsic position resolution, defaults to 4um:
m_resolution = config.get<ROOT::Math::XYVector>("resolution", ROOT::Math::XYVector(0.004, 0.004));
m_detectorName = config.getName();
if(Units::convert(m_pitch.X(), "mm") >= 1 or Units::convert(m_pitch.Y(), "mm") >= 1 or
......@@ -44,13 +48,14 @@ Detector::Detector(const Configuration& config) {
m_nPixelsX = npixels.x();
m_nPixelsY = npixels.y();
m_timingOffset = config.get<double>("time_offset", 0.0);
m_roi = config.getMatrix<int>("roi", std::vector<std::vector<int>>());
this->initialise();
LOG(TRACE) << "Initialized \"" << m_detectorType << "\": " << m_nPixelsX << "x" << m_nPixelsY << " px, pitch of "
<< display_vector(m_pitch, {"mm", "um"});
LOG(TRACE) << " Position: " << display_vector(m_displacement, {"mm", "um"});
LOG(TRACE) << " Orientation: " << display_vector(m_orientation, {"deg"}) << " (" << m_orientation_mode << ")";
<< Units::display(m_pitch, {"mm", "um"});
LOG(TRACE) << " Position: " << Units::display(m_displacement, {"mm", "um"});
LOG(TRACE) << " Orientation: " << Units::display(m_orientation, {"deg"}) << " (" << m_orientation_mode << ")";
if(m_timingOffset > 0.) {
LOG(TRACE) << "Timing offset: " << m_timingOffset;
}
......@@ -155,28 +160,33 @@ Configuration Detector::getConfiguration() {
Configuration config(name());
config.set("type", m_detectorType);
config.set("position", m_displacement);
config.set("position", m_displacement, {"um", "mm"});
config.set("orientation_mode", m_orientation_mode);
config.set("orientation", m_orientation);
config.set("orientation", m_orientation, {"deg"});
auto npixels = ROOT::Math::DisplacementVector2D<Cartesian2D<int>>(m_nPixelsX, m_nPixelsY);
config.set("number_of_pixels", npixels);
// Size of the pixels
config.set("pixel_pitch", m_pitch);
config.set("pixel_pitch", m_pitch, {"um"});
// Intrinsic resolution:
config.set("resolution", m_resolution, {"um"});
if(m_timingOffset != 0.) {
config.set("time_offset", m_timingOffset);
config.set("time_offset", m_timingOffset, {"ns", "us", "ms", "s"});
}
if(!m_maskfile_name.empty()) {
config.set("mask_file", m_maskfile_name);
}
config.setMatrix("roi", m_roi);
return config;
}
// Function to get global intercept with a track
PositionVector3D<Cartesian3D<double>> Detector::getIntercept(Track* track) {
PositionVector3D<Cartesian3D<double>> Detector::getIntercept(const Track* track) {
// Get the distance from the plane to the track initial state
double distance = (m_origin.X() - track->m_state.X()) * m_normal.X();
......@@ -192,8 +202,12 @@ PositionVector3D<Cartesian3D<double>> Detector::getIntercept(Track* track) {
return globalIntercept;
}
PositionVector3D<Cartesian3D<double>> Detector::getLocalIntercept(const Track* track) {
return globalToLocal(getIntercept(track));
}
// Function to check if a track intercepts with a plane
bool Detector::hasIntercept(Track* track, double pixelTolerance) {
bool Detector::hasIntercept(const Track* track, double pixelTolerance) {
// First, get the track intercept in global co-ordinates with the plane
PositionVector3D<Cartesian3D<double>> globalIntercept = this->getIntercept(track);
......@@ -240,11 +254,11 @@ bool Detector::hitMasked(Track* track, int tolerance) {
}
// Functions to get row and column from local position
double Detector::getRow(PositionVector3D<Cartesian3D<double>> localPosition) {
double Detector::getRow(const PositionVector3D<Cartesian3D<double>> localPosition) {
double row = ((localPosition.Y() + m_pitch.Y() / 2.) / m_pitch.Y()) + m_nPixelsY / 2.;
return row;
}
double Detector::getColumn(PositionVector3D<Cartesian3D<double>> localPosition) {
double Detector::getColumn(const PositionVector3D<Cartesian3D<double>> localPosition) {
double column = ((localPosition.X() + m_pitch.X() / 2.) / m_pitch.X()) + m_nPixelsX / 2.;
return column;
}
......@@ -257,13 +271,110 @@ PositionVector3D<Cartesian3D<double>> Detector::getLocalPosition(double row, dou
}
// Function to get in-pixel position
double Detector::inPixelX(PositionVector3D<Cartesian3D<double>> localPosition) {
double Detector::inPixelX(const PositionVector3D<Cartesian3D<double>> localPosition) {
double column = getColumn(localPosition);
double inPixelX = m_pitch.X() * (column - floor(column));
return inPixelX;
}
double Detector::inPixelY(PositionVector3D<Cartesian3D<double>> localPosition) {
double Detector::inPixelY(const PositionVector3D<Cartesian3D<double>> localPosition) {
double row = getRow(localPosition);
double inPixelY = m_pitch.Y() * (row - floor(row));
return inPixelY;
}
// Check if track position is within ROI:
bool Detector::isWithinROI(const Track* track) {
// Empty region of interest:
if(m_roi.empty()) {
return true;
}
// Check that track is within region of interest using winding number algorithm
auto localIntercept = this->getLocalIntercept(track);
auto coordinates = std::make_pair(this->getColumn(localIntercept), this->getRow(localIntercept));
if(winding_number(coordinates, m_roi) != 0) {
return true;
}
// Outside ROI:
return false;
}
// Check if cluster is within ROI and/or touches ROI border:
bool Detector::isWithinROI(Cluster* cluster) {
// Empty region of interest:
if(m_roi.empty()) {
return true;
}
// Loop over all pixels of the cluster
for(auto& pixel : (*cluster->pixels())) {
if(winding_number(pixel->coordinates(), m_roi) == 0) {
return false;
}
}
return true;
}
/* isLeft(): tests if a point is Left|On|Right of an infinite line.
* via: http://geomalgorithms.com/a03-_inclusion.html
* Input: three points P0, P1, and P2
* Return: >0 for P2 left of the line through P0 and P1
* =0 for P2 on the line
* <0 for P2 right of the line
* See: Algorithm 1 "Area of Triangles and Polygons"
*/
int Detector::isLeft(std::pair<int, int> pt0, std::pair<int, int> pt1, std::pair<int, int> pt2) {
return ((pt1.first - pt0.first) * (pt2.second - pt0.second) - (pt2.first - pt0.first) * (pt1.second - pt0.second));
}
/* Winding number test for a point in a polygon
* via: http://geomalgorithms.com/a03-_inclusion.html
* Input: x, y = a point,
* polygon = vector of vertex points of a polygon V[n+1] with V[n]=V[0]
* Return: wn = the winding number (=0 only when P is outside)
*/
int Detector::winding_number(std::pair<int, int> probe, std::vector<std::vector<int>> polygon) {
// Two points don't make an area
if(polygon.size() < 3) {
LOG(DEBUG) << "No ROI given.";
return 0;
}
int wn = 0; // the winding number counter
// loop through all edges of the polygon
// edge from V[i] to V[i+1]
for(int i = 0; i < polygon.size(); i++) {
auto point_this = std::make_pair(polygon.at(i).at(0), polygon.at(i).at(1));
auto point_next = (i + 1 < polygon.size() ? std::make_pair(polygon.at(i + 1).at(0), polygon.at(i + 1).at(1))
: std::make_pair(polygon.at(0).at(0), polygon.at(0).at(1)));
// start y <= P.y
if(point_this.second <= probe.second) {
// an upward crossing
if(point_next.second > probe.second) {
// P left of edge
if(isLeft(point_this, point_next, probe) > 0) {
// have a valid up intersect
++wn;
}
}
} else {
// start y > P.y (no test needed)
// a downward crossing
if(point_next.second <= probe.second) {
// P right of edge
if(isLeft(point_this, point_next, probe) < 0) {
// have a valid down intersect
--wn;
}
}
}
}
return wn;
}
......@@ -43,6 +43,7 @@ namespace corryvreckan {
ROOT::Math::XYVector size() { return ROOT::Math::XYVector(m_pitch.X() * m_nPixelsX, m_pitch.Y() * m_nPixelsY); }
ROOT::Math::XYVector pitch() { return m_pitch; }
ROOT::Math::XYVector resolution() { return m_resolution; }
int nPixelsX() { return m_nPixelsX; }
int nPixelsY() { return m_nPixelsY; }
......@@ -79,10 +80,12 @@ namespace corryvreckan {
void update();
// Function to get global intercept with a track
PositionVector3D<Cartesian3D<double>> getIntercept(Track* track);
PositionVector3D<Cartesian3D<double>> getIntercept(const Track* track);
// Function to get local intercept with a track
PositionVector3D<Cartesian3D<double>> getLocalIntercept(const Track* track);
// Function to check if a track intercepts with a plane
bool hasIntercept(Track* track, double pixelTolerance = 0.);
bool hasIntercept(const Track* track, double pixelTolerance = 0.);
// Function to check if a track goes through/near a masked pixel
bool hitMasked(Track* track, int tolerance = 0.);
......@@ -101,16 +104,24 @@ namespace corryvreckan {
ROOT::Math::XYZPoint localToGlobal(ROOT::Math::XYZPoint local) { return m_localToGlobal * local; };
ROOT::Math::XYZPoint globalToLocal(ROOT::Math::XYZPoint global) { return m_globalToLocal * global; };
bool isWithinROI(const Track* track);
bool isWithinROI(Cluster* cluster);
private:
// Member variables
// Detector information
std::string m_detectorType;
std::string m_detectorName;
ROOT::Math::XYVector m_pitch;
ROOT::Math::XYVector m_resolution;
int m_nPixelsX;
int m_nPixelsY;
double m_timingOffset;
std::vector<std::vector<int>> m_roi;
static int winding_number(std::pair<int, int> probe, std::vector<std::vector<int>> polygon);
inline static int isLeft(std::pair<int, int> pt0, std::pair<int, int> pt1, std::pair<int, int> pt2);
// Displacement and rotation in x,y,z
ROOT::Math::XYZPoint m_displacement;
ROOT::Math::XYZVector m_orientation;
......
......@@ -22,7 +22,7 @@
#include <Math/PositionVector3D.h>
#include <TString.h>
#include "core/utils/string.h"
#include "core/utils/text.h"
#include "core/utils/type.h"
namespace corryvreckan {
......@@ -146,21 +146,6 @@ namespace corryvreckan {
inline std::ostream& operator<<(std::ostream& os, const ROOT::Math::PositionVector2D<T, U>& vec) {
return os << "(" << vec.x() << "," << vec.y() << ")";
}
/**
* @brief Utility function to display vector types with units
* @note Works for all vector types that can be converted to string using \ref StringConversions "the string utilities".
*/
template <typename T> inline std::string display_vector(T inp, std::initializer_list<std::string> units) {
auto split = corryvreckan::split<Units::UnitType>(corryvreckan::to_string(inp));
std::string ret_str = "(";
for(auto& element : split) {
ret_str += Units::display(element, units);
ret_str += ",";
}
ret_str[ret_str.size() - 1] = ')';
return ret_str;
}
} // namespace corryvreckan
#endif /* CORRYVRECKAN_ROOT_H */
/**
* @file
* @brief Implementation of string utilities
*
* @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*
* Used extensively for parsing the configuration in the \ref corryvreckan::ConfigReader.
*/
#include "text.h"
#include "unit.h"
using namespace corryvreckan;
std::string corryvreckan::trim(const std::string& str, const std::string& delims) {
size_t b = str.find_first_not_of(delims);
size_t e = str.find_last_not_of(delims);
if(b == std::string::npos || e == std::string::npos) {
return "";
}
return std::string(str, b, e - b + 1);
}
std::string corryvreckan::from_string_helper(std::string str) {
// Check if string is not empty
str = trim(str);
if(str.empty()) {
throw std::invalid_argument("string is empty");
}
// Check if there is whitespace in the string
size_t white_space = str.find_first_of(" \t\n\r\v");
if(white_space != std::string::npos) {
throw std::invalid_argument("remaining data at end");
}
return str;
}
/**
* If a pair of enclosing double quotation marks is found, the whole string within the quotation marks is returned.
* Otherwise only the first part is read until whitespace is encountered.
*/
std::string corryvreckan::from_string_impl(std::string str, type_tag<std::string>) {
str = trim(str);
// If there are "" then we should take the whole string
if(!str.empty() && (str.front() == '\"' || str.front() == '\'')) {
if(str.find(str.front(), 1) != str.size() - 1) {
throw std::invalid_argument("remaining data at end");
}
return str.substr(1, str.size() - 2);
}
// Otherwise read a single string
return from_string_helper(str);
}
/**
* Both numerical (0, 1) and textual representations ("false", "true") are supported for booleans. No enclosing quotation
* marks should be used.
*/
bool corryvreckan::from_string_impl(std::string str, type_tag<bool>) {
str = from_string_helper(str);
std::istringstream sstream(str);
bool ret_value = false;
if(isalpha(str.back()) != 0) {
sstream >> std::boolalpha >> ret_value;
} else {
sstream >> ret_value;
}
// Check if the reading was succesfull and everything was read
if(sstream.fail() || sstream.peek() != EOF) {
throw std::invalid_argument("conversion not possible");
}
return ret_value;
}
......@@ -6,7 +6,7 @@
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*
* Used extensively for parsing the configuration in the \ref allpix::ConfigReader.
* Used extensively for parsing the configuration in the \ref corryvreckan::ConfigReader.
*/
/**
......@@ -14,8 +14,8 @@
* @brief Collection of all the overloads of string conversions
*/
#ifndef CORRYVRECKAN_STRING_H
#define CORRYVRECKAN_STRING_H
#ifndef CORRYVRECKAN_TEXT_H
#define CORRYVRECKAN_TEXT_H
#include <cctype>
#include <sstream>
......@@ -24,7 +24,6 @@
#include <vector>
#include "type.h"
#include "unit.h"
// TODO [doc]: should possible be put in a separate namespace
......@@ -35,182 +34,69 @@ namespace corryvreckan {
* @param str String that should be trimmed
* @param delims List of delimiters to trim from the string (defaults to all whitespace)
*/
inline std::string trim(const std::string& str, const std::string& delims = " \t\n\r\v") {
size_t b = str.find_first_not_of(delims);
size_t e = str.find_last_not_of(delims);
if(b == std::string::npos || e == std::string::npos) {
return "";
}
return std::string(str, b, e - b + 1);
}
std::string trim(const std::string& str, const std::string& delims = " \t\n\r\v");
/**
* @brief Converts a string to any supported type
* @param str String to convert
* @see StringConversions
*
* The matching converter function is automatically found if available. To add a new conversion the \ref from_string_impl
* function should be overloaded. The string is passed as first argument to this function, the second argument should be
* an \ref corryvreckan::type_tag with the type to convert to.
*
*/
template <typename T> T from_string(std::string str) {
// Use tag dispatch to select the correct helper function
return from_string_impl(str, type_tag<T>());
}
// TODO [doc] This should move to a source file
// FIXME: include exceptions better
// helper functions to do cleaning and checks for string reading
static std::string _from_string_helper(std::string str) {
// Check if string is not empty
str = trim(str);
if(str.empty()) {
throw std::invalid_argument("string is empty");
}
template <typename T> T from_string(std::string str);
// Check if there is whitespace in the string
size_t white_space = str.find_first_of(" \t\n\r\v");
if(white_space != std::string::npos) {
throw std::invalid_argument("remaining data at end");
}
return str;
}
/**
* @brief Internal helper method for checking and trimming conversions from string
* @param str Input string to check and trim
* @return Trimmed string
*/
std::string from_string_helper(std::string str);
/**
* @ingroup StringConversions
* @brief Conversion handler for all non implemented conversions
*
* Function does not return but will raise an static assertion.
* @warning Function does not return but will raise an static assertion.
*/
template <typename T, typename = std::enable_if_t<!std::is_arithmetic<T>::value>, typename = void>
constexpr T from_string_impl(const std::string&, type_tag<T>) {
static_assert(std::is_same<T, void>::value,
"Conversion to this type is not implemented: an overload should be added to support this conversion");
return T();
}
constexpr T from_string_impl(const std::string&, type_tag<T>);
/**
* @ingroup StringConversions
* @brief Conversion handler for all arithmetic types
* @throws std::invalid_argument If the string cannot be converted to the required arithmetic type
*
* The unit system is used through \ref Units::get to parse unit suffixes and convert the values to the appropriate
* standard framework unit.