Commit 02ccd90c authored by Morag Jean Williams's avatar Morag Jean Williams
Browse files
parents 0ca9c2b9 a9270b43
......@@ -47,7 +47,7 @@ Corryvreckan has been developed and is maintained by
The following authors, in alphabetical order, have contributed to Corryvreckan:
* Matthew Daniel Buckland, University of Glasgow, @mbucklan
* Matthew Daniel Buckland, University of Liverpool, @mbucklan
* Adrian Fiergolski, CERN, @afiergol
* Andreas Matthias Nürnberg, CERN, @nurnberg
* Florian Pitters, CERN, @fpipper
......
......@@ -6,17 +6,21 @@
MESSAGE(STATUS "Looking for Peary...")
FIND_PATH(Peary_INCLUDE_DIRS NAMES "peary/device/device.hpp" PATHS "$ENV{PEARYPATH}")
FIND_PATH(Peary_INCLUDE_DIR NAMES "peary/device/device.hpp" PATHS "$ENV{PEARYPATH}")
FIND_LIBRARY(Peary_LIBRARIES NAMES "peary" HINTS "$ENV{PEARYPATH}/lib")
LIST(APPEND Peary_INCLUDE_DIRS "${Peary_INCLUDE_DIR}/peary/utils")
LIST(APPEND Peary_INCLUDE_DIRS "${Peary_INCLUDE_DIR}/devices")
IF(Peary_FIND_COMPONENTS)
FOREACH(component ${Peary_FIND_COMPONENTS})
STRING(TOLOWER "${component}" _COMP)
FIND_PATH(Peary_COMP_INCLUDE NAMES "devices/${_COMP}/${_COMP}.hpp" PATHS "$ENV{PEARYPATH}")
FIND_LIBRARY(Peary_COMP_LIB NAMES "${component}" HINTS "$ENV{PEARYPATH}/lib")
FIND_PATH(Peary_COMP_INCLUDE NAMES "devices/${component}/${component}Device.hpp" PATHS "$ENV{PEARYPATH}")
FIND_LIBRARY(Peary_COMP_LIB NAMES "PearyDevice${component}" HINTS "$ENV{PEARYPATH}/lib")
IF(Peary_COMP_INCLUDE AND Peary_COMP_LIB)
LIST(APPEND Peary_LIBRARIES "${Peary_COMP_LIB}")
LIST(APPEND Peary_INCLUDE_DIRS "${Peary_COMP_INCLUDE}/devices/${component}")
SET(Peary_${component}_FOUND TRUE)
MESSAGE(STATUS "Looking for Peary component: ${component} -- Found")
ELSE()
MESSAGE(STATUS "Looking for Peary component: ${component} -- NOTFOUND")
ENDIF()
......
# Additional targets to perform clang-format/clang-tidy
# Additional targets to perform clang-format/clang-tidy/cppcheck
# Get all project files - FIXME: this should also use the list of generated targets
IF(NOT CHECK_CXX_SOURCE_FILES)
......@@ -7,8 +7,9 @@ IF(NOT CHECK_CXX_SOURCE_FILES)
ENDIF()
# Adding clang-format check and formatter if found
FIND_PROGRAM(CLANG_FORMAT "clang-format")
FIND_PROGRAM(CLANG_FORMAT NAMES "clang-format-6.0" "clang-format-5.0" "clang-format-4.0" "clang-format")
IF(CLANG_FORMAT)
MESSAGE(STATUS "Found ${CLANG_FORMAT}, adding formatting targets")
ADD_CUSTOM_TARGET(
format
COMMAND
......@@ -34,6 +35,8 @@ IF(CLANG_FORMAT)
${CMAKE_BINARY_DIR}/check_format_file.txt > /dev/null
COMMENT "Checking format compliance"
)
ELSE()
MESSAGE(STATUS "Could NOT find clang-format")
ENDIF()
# Adding clang-tidy target if executable is found
......@@ -42,7 +45,7 @@ IF(${CMAKE_CXX_STANDARD})
SET(CXX_STD ${CMAKE_CXX_STANDARD})
ENDIF()
FIND_PROGRAM(CLANG_TIDY "clang-tidy")
FIND_PROGRAM(CLANG_TIDY NAMES "clang-tidy-6.0" "clang-tidy-5.0" "clang-tidy-4.0" "clang-tidy")
# Enable clang tidy only if using a clang compiler
IF(CLANG_TIDY AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# If debug build enabled do automatic clang tidy
......@@ -52,9 +55,12 @@ IF(CLANG_TIDY AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Enable checking and formatting through run-clang-tidy if available
# FIXME Make finding this program more portable
GET_FILENAME_COMPONENT(CLANG_TIDY ${CLANG_TIDY} REALPATH)
GET_FILENAME_COMPONENT(CLANG_DIR ${CLANG_TIDY} DIRECTORY)
FIND_PROGRAM(RUN_CLANG_TIDY NAMES "run-clang-tidy.py" "run-clang-tidy-4.0.py" HINTS /usr/share/clang/ ${CLANG_DIR}/../share/clang/ /usr/bin/)
IF(RUN_CLANG_TIDY)
MESSAGE(STATUS "Found ${CLANG_TIDY}, adding linting targets")
# Set export commands on
SET (CMAKE_EXPORT_COMPILE_COMMANDS ON)
......@@ -79,5 +85,49 @@ IF(CLANG_TIDY AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
COMMAND ! grep -c ": error: " ${CMAKE_BINARY_DIR}/check_lint_file.txt > /dev/null
COMMENT "Checking for problems in source files"
)
ELSE()
MESSAGE(STATUS "Could NOT find run-clang-tidy script")
ENDIF()
ELSE()
IF(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
MESSAGE(STATUS "Could NOT find clang-tidy")
ELSE()
MESSAGE(STATUS "Could NOT check for clang-tidy, wrong compiler: ${CMAKE_CXX_COMPILER_ID}")
ENDIF()
ENDIF()
FIND_PROGRAM(CPPCHECK "cppcheck")
IF(CPPCHECK)
# Set export commands on
SET (CMAKE_EXPORT_COMPILE_COMMANDS ON)
ADD_CUSTOM_TARGET(
cppcheck
COMMAND ${CPPCHECK}
--enable=all
--project=${CMAKE_BINARY_DIR}/compile_commands.json
--std=c++11
--verbose
--quiet
--xml-version=2
--language=c++
--suppress=missingIncludeSystem
--output-file=${CMAKE_BINARY_DIR}/cppcheck_results.xml
${CHECK_CXX_SOURCE_FILES}
COMMENT "Generate cppcheck report for the project"
)
FIND_PROGRAM(CPPCHECK_HTML "cppcheck-htmlreport")
IF(CPPCHECK_HTML)
ADD_CUSTOM_TARGET(
cppcheck-html
COMMAND ${CPPCHECK_HTML}
--title=${CMAKE_PROJECT_NAME}
--file=${CMAKE_BINARY_DIR}/cppcheck_results.xml
--report-dir=${CMAKE_BINARY_DIR}/cppcheck_results
--source-dir=${CMAKE_SOURCE_DIR}
COMMENT "Convert cppcheck report to HTML output"
)
ADD_DEPENDENCIES(cppcheck-html cppcheck)
ENDIF()
ENDIF()
......@@ -42,13 +42,17 @@
% Command to add all modules
\newcommand{\includemodulesmd}{\def\temp{@CORRYVRECKAN_MODULE_FILES@}\ifx\temp\empty
\textit{Module documentation not added because Markdown to LaTex conversion was not possible}
\textit{Module documentation not added because Markdown to \LaTeX~conversion was not possible. Pandoc is required for the conversion.}
\else
\foreach \n in @CORRYVRECKAN_MODULE_FILES@ {\input{\n}}
\fi}
% Command to add a single converted markdown file
\newcommand{\inputmd}[1]{\input{md/#1}}
\newcommand{\inputmd}[1]{\def\temp{@other_tex_files@}\ifx\temp\empty
\textit{This section is missing Markdown to \LaTeX~conversion was not possible. Pandoc is required for the conversion.}
\else
\input{md/#1}
\fi}
% Set bibliography
\addbibresource{usermanual/references.bib}
......
......@@ -8,7 +8,12 @@
##################################################################
# SETTINGS
# set path to clang-format binary
CLANG_FORMAT=$(which clang-format)
for bin in "clang-format-6.0" "clang-format-5.0" "clang-format-4.0" "clang-format";
do
CLANG_FORMAT=$(which "$bin")
[ -n "$CLANG_FORMAT" ] && break
done
# set file extensions to handle
FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .tpp .C"
......
......@@ -145,7 +145,7 @@ void Analysis::load_modules() {
// Create histogram output file
std::string histogramFile = global_config.getPath("histogramFile");
m_histogramFile = new TFile(histogramFile.c_str(), "RECREATE");
m_histogramFile = std::make_unique<TFile>(histogramFile.c_str(), "RECREATE");
m_directory = m_histogramFile->mkdir("corryvreckan");
if(m_histogramFile->IsZombie()) {
throw RuntimeError("Cannot create main ROOT file " + histogramFile);
......@@ -358,7 +358,7 @@ void Analysis::run() {
if(number_of_events > -1 && m_events >= number_of_events)
break;
if(run_time > 0.0 && (m_clipboard->get_persistent("currentTime")) >= run_time)
if(run_time > 0.0 && (m_clipboard->get_persistent("eventStart")) >= run_time)
break;
// Check if we have reached the maximum number of tracks
......@@ -415,8 +415,10 @@ void Analysis::run() {
events_prev = m_events;
LOG_PROGRESS(STATUS, "event_loop")
<< "Ev: +" << m_events << " \\" << skipped << " Tr: " << m_tracks << " (" << std::setprecision(3)
<< ((double)m_tracks / m_events)
<< "/ev) t = " << Units::display(m_clipboard->get_persistent("currentTime"), {"ns", "us", "s"});
<< ((double)m_tracks / m_events) << "/ev)"
<< (m_clipboard->has_persistent("eventStart")
? " t = " + Units::display(m_clipboard->get_persistent("eventStart"), {"ns", "us", "ms", "s"})
: "");
}
// Clear objects from this iteration from the clipboard
......@@ -496,7 +498,9 @@ void Analysis::finaliseAll() {
// Write the output histogram file
m_directory->cd();
m_directory->Write();
m_histogramFile->Close();
LOG(STATUS) << "Wrote histogram output file to " << global_config.getPath("histogramFile");
// Write out update detectors file:
if(global_config.has("detectors_file_updated")) {
......
......@@ -66,7 +66,7 @@ namespace corryvreckan {
// Log file if specified
std::ofstream log_file_;
TFile* m_histogramFile;
std::unique_ptr<TFile> m_histogramFile;
TDirectory* m_directory;
int m_events;
int m_tracks;
......
......@@ -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
......
#include "Clipboard.hpp"
#include "exceptions.h"
#include "objects/Object.hpp"
using namespace corryvreckan;
......@@ -24,7 +25,15 @@ Objects* Clipboard::get(std::string name, std::string type) {
}
double Clipboard::get_persistent(std::string name) {
return m_persistent_data[name];
try {
return m_persistent_data.at(name);
} catch(std::out_of_range&) {
throw MissingDataError(name);
}
}
bool Clipboard::has_persistent(std::string name) {
return m_persistent_data.find(name) != m_persistent_data.end();
}
void Clipboard::clear() {
......
......@@ -11,8 +11,8 @@
#define CORRYVRECKAN_CLIPBOARD_H
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
#include "core/utils/log.h"
#include "objects/Object.hpp"
......@@ -75,9 +75,17 @@ namespace corryvreckan {
* @brief Retrieve variable from the persistent clipboard storage
* @param name Name of the variable
* @return Stored value from the persistent clipboard storage
* @throws MissingKeyError in case the key is not found.
*/
double get_persistent(std::string name);
/**
* @brief Check if variable exists on the persistent clipboard storage
* @param name Name of the variable
* @return True if value exists, false if it does not exist.
*/
bool has_persistent(std::string name);
/**
* @brief Clear the event storage of the clipboard
*/
......@@ -97,7 +105,7 @@ namespace corryvreckan {
std::vector<std::string> m_dataID;
// Persistent clipboard storage
std::map<std::string, double> m_persistent_data;
std::unordered_map<std::string, double> m_persistent_data;
};
} // namespace corryvreckan
......
......@@ -16,7 +16,7 @@
#include <string>
#include <vector>
#include "core/utils/string.h"
#include "core/utils/text.h"
#include "exceptions.h"
namespace corryvreckan {
......@@ -102,6 +102,14 @@ namespace corryvreckan {
*/
template <typename T> Matrix<T> getMatrix(const std::string& key) const;
/**
* @brief Get values for a key containing a 2D matrix
* @param key Key to get values of
* @param def Default value matrix to use if key is not defined
* @return Matrix of values from the requested template parameter
*/
template <typename T> Matrix<T> getMatrix(const std::string& key, const Matrix<T> def) const;
/**
* @brief Get literal value of a key as string
* @param key Key to get values of
......@@ -134,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
......@@ -148,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;
......@@ -115,17 +115,64 @@ namespace corryvreckan {
}
}
/**
* @throws InvalidKeyError If the conversion to the requested type did not succeed
* @throws InvalidKeyError If an overflow happened while converting the key
*/
template <typename T> Matrix<T> Configuration::getMatrix(const std::string& key, const Matrix<T> def) const {
if(has(key)) {
return getMatrix<T>(key);
}
return def;
}
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;
}
......@@ -256,14 +270,111 @@ PositionVector3D<Cartesian3D<double>> Detector::getLocalPosition(double row, dou
m_pitch.X() * (column - m_nPixelsX / 2.), m_pitch.Y() * (row - m_nPixelsY / 2.), 0.);
}
// Function to get in-pixel position (value returned in microns)
double Detector::inPixelX(PositionVector3D<Cartesian3D<double>> localPosition) {
// Function to get in-pixel position
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) {