Commit b33605b7 authored by Simon Spannagel's avatar Simon Spannagel
Browse files

Merge branch 'master' into col_row

parents 49445031 f7261b60
# - Try to find the EUDAQ2 framework
# Once done this will define
# EUDAQ2_FOUND - System has EUDAQ2
# EUDAQ2_INCLUDE_DIR - The EUDAQ2 main include directories
# EUDAQ2_LIBRARY - The libraries needed to use EUDAQ2
MESSAGE(STATUS "Looking for EUDAQ2...")
FIND_PATH(EUDAQ2_INCLUDE_DIR NAMES "eudaq/FileReader.hh" PATHS "$ENV{EUDAQ2PATH}/include")
MESSAGE(STATUS "FileReader.hh => ${EUDAQ2_INCLUDE_DIR}")
IF(EUDAQ2_INCLUDE_DIR)
SET(EUDAQ2_INC_FOUND TRUE)
MESSAGE(STATUS "Found EUDAQ2 headers: ${EUDAQ2_INCLUDE_DIR}")
ENDIF()
FIND_LIBRARY(EUDAQ2_LIBRARY NAMES "libeudaq_core.so" HINTS "$ENV{EUDAQ2PATH}/lib")
MESSAGE(STATUS "libeudaq_core.so => ${EUDAQ2_LIBRARY}")
IF(EUDAQ2_LIBRARY)
SET(EUDAQ2_LIB_FOUND TRUE)
MESSAGE(STATUS "Found EUDAQ2 library: ${EUDAQ2_LIBRARY} ")
GET_FILENAME_COMPONENT(EUDAQ2_LIB_PATH ${EUDAQ2_LIBRARY} DIRECTORY)
MESSAGE(STATUS "EUDAQ2_LIB_PATH = ${EUDAQ2_LIB_PATH}")
FILE(GLOB EUDAQ2_LIBRARY "${EUDAQ2_LIB_PATH}/*.so")
MESSAGE(STATUS "EUDAQ2_LIBRARY list = ${EUDAQ2_LIBRARY}")
ENDIF()
IF(EUDAQ2_LIB_FOUND AND EUDAQ2_INC_FOUND)
SET(EUDAQ2_FOUND TRUE)
ENDIF()
#mark_as_advanced(EUDAQ2_LIBRARY EUDAQ2_INCLUDE_DIR)
mark_as_advanced(EUDAQ2_LIBRARY EUDAQ2_INCLUDE_DIR)
# - Try to find the MuPix telescope DAQ
# Once done this will define
# MUPIX8DAQ_FOUND - System has MuPixDAQ
# MUPIX8DAQ_INCLUDE_DIR - The MuPixDAQ main include directories
# MUPIX8DAQ_LIBRARY - The libraries needed to use MuPixDAQ
MESSAGE(STATUS "Looking for MuPix Telescope Readout...")
FIND_PATH(MUPIX8DAQ_INCLUDE_DIR NAMES "telescope_frame.hpp" "*.hpp" "*.h" PATHS "$ENV{MUPIX8DAQ}/library")
MESSAGE(STATUS "telescope_frame.hpp => ${MUPIX8DAQ_INCLUDE_DIR}")
IF(MUPIX8DAQ_INCLUDE_DIR)
SET(MUPIX8DAQ_INC_FOUND TRUE)
MESSAGE(STATUS "Found MUPIX8DAQ headers: ${MUPIX8DAQ_INCLUDE_DIR}")
ENDIF()
FIND_LIBRARY(MUPIX8DAQ_LIBRARY NAMES "libtelescope" HINTS "$ENV{MUPIX8DAQ}/build/library")
MESSAGE(STATUS "libMuPix8 => ${MUPIX8DAQ_LIBRARY}")
IF(MUPIX8DAQ_LIBRARY)
SET(MUPIX8DAQ_LIB_FOUND TRUE)
MESSAGE(STATUS "Found MuPix8DAQ library: ${MUPIX8DAQ_LIBRARY}")
ENDIF()
IF(MUPIX8DAQ_LIB_FOUND AND MUPIX8DAQ_INC_FOUND)
SET(MUPIX8DAQ_FOUND TRUE)
MESSAGE(STATUS "MUPIX8DAQ Found true")
ENDIF()
mark_as_advanced(MUPIX8DAQ_LIBRARY MUPIX8DAQ_INCLUDE_DIR)
......@@ -15,14 +15,14 @@ MACRO(_corryvreckan_module_define_common name)
# Build all modules by default if not specified otherwise
OPTION(BUILD_${_corryvreckan_module_dir} "Build module in directory ${_corryvreckan_module_dir}?" ON)
# Put message
MESSAGE(STATUS "Building module " ${BUILD_${_corryvreckan_module_dir}} "\t- " ${_corryvreckan_module_dir})
# Quit the file if not building this file or all modules
IF(NOT (BUILD_${_corryvreckan_module_dir} OR BUILD_ALL_MODULES))
RETURN()
ENDIF()
# Put message
MESSAGE( STATUS "Building module: " ${_corryvreckan_module_dir} )
# Prepend with the module prefix to create the name of the module
SET(${name} "CorryvreckanModule${_corryvreckan_module_dir}")
......
#include "Clipboard.hpp"
#include "core/utils/log.h"
#include "exceptions.h"
#include "objects/Object.hpp"
......
......@@ -15,6 +15,7 @@ using namespace std;
AlignmentMillepede::AlignmentMillepede(Configuration config, std::vector<std::shared_ptr<Detector>> detectors)
: Module(std::move(config), std::move(detectors)) {
m_excludeDUT = m_config.get<bool>("exclude_dut", false);
m_numberOfTracksForAlignment = m_config.get<size_t>("number_of_tracks", 20000);
m_dofs = m_config.getArray<bool>("dofs", {});
m_nIterations = m_config.get<size_t>("iterations", 5);
......@@ -99,11 +100,11 @@ void AlignmentMillepede::finalise() {
size_t nPlanes = num_detectors();
for(const auto& det : get_detectors()) {
if(det->isDUT()) {
if(det->isDUT() && m_excludeDUT) {
nPlanes--;
}
}
LOG(INFO) << "Aligning " << nPlanes << "planes";
const size_t nParameters = 6 * nPlanes;
for(unsigned int iteration = 0; iteration < m_nIterations; ++iteration) {
// Define the constraint equations.
......
......@@ -153,6 +153,8 @@ namespace corryvreckan {
std::vector<unsigned int> m_fixedPlanes;
/// Flag to fix all degrees of freedom or only the translations.
bool m_fix_all;
/// It can be also reasonable to include the DUT in the alignemnt
bool m_excludeDUT;
};
} // namespace corryvreckan
......
......@@ -11,6 +11,8 @@ The Millepede algorthm allows a simultaneous fit of both the tracks and the alig
The modules stops if the convergence, i.e. the absolute sum of all corrections over the total number of parameters, is smaller than the configured value.
### Parameters
* `exclude_dut` : Exclude the DUT from the alignment procedure. Default value
is `false`.
* `number_of_tracks`: Number of tracks used in the alignment method chosen. Default value is `20000`.
* `iterations`: Number of times the chosen alignment method is to be iterated. Default value is `3`.
* `dofs`: Degrees of freedom to be aligned. This parameter should be given as vector of six boolean values for the parameters "Translation X", "Translation Y", "Translation Z", "Rotation X", "Rotation Y" and "Rotation Z". The default setting is an alignment of all parameters except for "Translation Z", i.e. `dofs = true, true, false, true, true, true`.
......@@ -25,4 +27,5 @@ The modules stops if the convergence, i.e. the absolute sum of all corrections o
[Millepede]
iterations = 10
dofs = true, true, false, true, true, true
exclude_dut = false
```
......@@ -87,6 +87,7 @@ void AnalysisDUT::initialise() {
clusterTotAssoc = new TH1F("clusterTotAssociated", "clusterTotAssociated", 10000, 0, 10000);
clusterTotAssocNorm = new TH1F("clusterTotAssociatedNormalized", "clusterTotAssociatedNormalized", 10000, 0, 10000);
clusterSizeAssoc = new TH1F("clusterSizeAssociated", "clusterSizeAssociated", 30, 0, 30);
clusterSizeAssocNorm = new TH1F("clusterSizeAssociatedNormalized", "clusterSizeAssociatedNormalized", 30, 0, 30);
// In-pixel studies:
auto pitch_x = static_cast<double>(Units::convert(m_detector->pitch().X(), "um"));
......@@ -188,6 +189,14 @@ void AnalysisDUT::initialise() {
m_detector->nPixels().Y(),
0,
m_detector->nPixels().Y());
hUnassociatedTracksGlobalPosition = new TH2F("hUnassociatedTracksGlobalPosition",
"hUnassociatedTracksGlobalPosition; x / mm; y / mm",
200,
-10,
10,
200,
-10,
10);
}
StatusCode AnalysisDUT::run(std::shared_ptr<Clipboard> clipboard) {
......@@ -277,6 +286,7 @@ StatusCode AnalysisDUT::run(std::shared_ptr<Clipboard> clipboard) {
auto associated_clusters = track->associatedClusters();
if(std::find(associated_clusters.begin(), associated_clusters.end(), cluster) == associated_clusters.end()) {
LOG(DEBUG) << "No associated cluster found";
hUnassociatedTracksGlobalPosition->Fill(globalIntercept.X(), globalIntercept.Y());
continue;
}
......@@ -336,6 +346,7 @@ StatusCode AnalysisDUT::run(std::shared_ptr<Clipboard> clipboard) {
residualsTimeVsSignal->Fill(tdistance, cluster->tot());
clusterSizeAssoc->Fill(static_cast<double>(cluster->size()));
clusterSizeAssocNorm->Fill(static_cast<double>(cluster->size()));
// Fill in-pixel plots: (all as function of track position within pixel cell)
if(is_within_roi) {
......@@ -382,7 +393,10 @@ StatusCode AnalysisDUT::run(std::shared_ptr<Clipboard> clipboard) {
hPixelEfficiencyMap->Fill(xmod, ymod, has_associated_cluster);
}
}
// Return value telling analysis to keep running
return StatusCode::Success;
}
void AnalysisDUT::finalise() {
clusterSizeAssocNorm->Scale(1 / clusterSizeAssoc->Integral());
}
......@@ -21,6 +21,7 @@ namespace corryvreckan {
// Functions
void initialise();
StatusCode run(std::shared_ptr<Clipboard> clipboard);
void finalise();
private:
std::shared_ptr<Detector> m_detector;
......@@ -40,6 +41,7 @@ namespace corryvreckan {
TH1F *clusterTotAssoc, *clusterTotAssocNorm;
TH1F* clusterSizeAssoc;
TH1F* clusterSizeAssocNorm;
TProfile2D *rmsxvsxmym, *rmsyvsxmym, *rmsxyvsxmym;
TProfile2D *qvsxmym, *qMoyalvsxmym, *pxqvsxmym;
......@@ -59,7 +61,7 @@ namespace corryvreckan {
TH2F* hAssociatedTracksGlobalPosition;
TH2F* hAssociatedTracksLocalPosition;
TH2F* hUnassociatedTracksGlobalPosition;
// Member variables
double spatialCut, m_timeCutFrameEdge;
double chi2ndofCut;
......
......@@ -17,6 +17,7 @@ Analysis module for CLICpix2 prototypes. This module is still work in progress,
* 2D Map of cluster sizes for associated clusters
* 2D Map of cluster ToT values from associated clusters
* 2D Map of associated hits
* 2D Map of tracks not associated to a cluster
* 2D Map of associated hits within the defined region-of-interest
* Distribution of pixel ToT values from associated clusters
* 2D Map of pixel ToT values from associated clusters
......@@ -25,6 +26,7 @@ Analysis module for CLICpix2 prototypes. This module is still work in progress,
* Track residuals for 2-pixel-clusters in X and Y
* Distribution of cluster Tot values from associated clusters
* Distribution of sizes from associated clusters
* Normalised distribution of sizes from associated clusters
* 2D Map of in-pixel efficiency
* 2D Map of the chip efficiency in local coordinates
* 2D Map of the chip efficiency on global coordinates
......
......@@ -20,7 +20,9 @@ AnalysisEfficiency::AnalysisEfficiency(Configuration config, std::shared_ptr<Det
m_detector = detector;
m_timeCutFrameEdge = m_config.get<double>("time_cut_frameedge", Units::get<double>(20, "ns"));
m_pixelTolerance = m_config.get<double>("pixel_tolerance", 1.);
m_chi2ndofCut = m_config.get<double>("chi2ndof_cut", 3.);
m_inpixelBinSize = m_config.get<double>("inpixel_bin_size", Units::get<double>(1.0, "um"));
}
void AnalysisEfficiency::initialise() {
......@@ -30,38 +32,60 @@ void AnalysisEfficiency::initialise() {
std::string title = m_detector->name() + " Pixel efficiency map;x_{track} mod " + std::to_string(pitch_x) +
"#mum;y_{track} mod " + std::to_string(pitch_y) + "#mum;efficiency";
hPixelEfficiencyMap = new TProfile2D("pixelEfficiencyMap",
title.c_str(),
static_cast<int>(pitch_x),
0,
pitch_x,
static_cast<int>(pitch_y),
0,
pitch_y,
0,
1);
hPixelEfficiencyMap_trackPos = new TProfile2D("pixelEfficiencyMap_trackPos",
title.c_str(),
static_cast<int>(ceil(pitch_x / m_inpixelBinSize)),
0,
pitch_x,
static_cast<int>(ceil(pitch_y / m_inpixelBinSize)),
0,
pitch_y,
0,
1);
title = m_detector->name() + " Chip efficiency map;x [px];y [px];efficiency";
hChipEfficiencyMap = new TProfile2D("chipEfficiencyMap",
title.c_str(),
m_detector->nPixels().X(),
0,
m_detector->nPixels().X(),
m_detector->nPixels().Y(),
0,
m_detector->nPixels().Y(),
0,
1);
hChipEfficiencyMap_trackPos = new TProfile2D("chipEfficiencyMap_trackPos",
title.c_str(),
m_detector->nPixels().X(),
0,
m_detector->nPixels().X(),
m_detector->nPixels().Y(),
0,
m_detector->nPixels().Y(),
0,
1);
title = m_detector->name() + " Global efficiency map;x [mm];y [mm];efficiency";
hGlobalEfficiencyMap = new TProfile2D("globalEfficiencyMap",
title.c_str(),
300,
-1.5 * m_detector->size().X(),
1.5 * m_detector->size().X(),
300,
-1.5 * m_detector->size().Y(),
1.5 * m_detector->size().Y(),
0,
1);
hGlobalEfficiencyMap_trackPos = new TProfile2D("globalEfficiencyMap_trackPos",
title.c_str(),
300,
-1.5 * m_detector->size().X(),
1.5 * m_detector->size().X(),
300,
-1.5 * m_detector->size().Y(),
1.5 * m_detector->size().Y(),
0,
1);
title = m_detector->name() + " Chip efficiency map;x [px];y [px];efficiency";
hChipEfficiencyMap_clustPos = new TProfile2D("chipEfficiencyMap_clustPos",
title.c_str(),
m_detector->nPixels().X(),
0,
m_detector->nPixels().X(),
m_detector->nPixels().Y(),
0,
m_detector->nPixels().Y(),
0,
1);
title = m_detector->name() + " Global efficiency map;x [mm];y [mm];efficiency";
hGlobalEfficiencyMap_clustPos = new TProfile2D("globalEfficiencyMap_clustPos",
title.c_str(),
300,
-1.5 * m_detector->size().X(),
1.5 * m_detector->size().X(),
300,
-1.5 * m_detector->size().Y(),
1.5 * m_detector->size().Y(),
0,
1);
}
StatusCode AnalysisEfficiency::run(std::shared_ptr<Clipboard> clipboard) {
......@@ -89,8 +113,8 @@ StatusCode AnalysisEfficiency::run(std::shared_ptr<Clipboard> clipboard) {
auto globalIntercept = m_detector->getIntercept(track);
auto localIntercept = m_detector->globalToLocal(globalIntercept);
if(!m_detector->hasIntercept(track, 0.5)) {
LOG(DEBUG) << " - track outside DUT area";
if(!m_detector->hasIntercept(track, m_pixelTolerance)) {
LOG(DEBUG) << " - track outside DUT area: " << localIntercept;
continue;
}
......@@ -147,18 +171,26 @@ StatusCode AnalysisEfficiency::run(std::shared_ptr<Clipboard> clipboard) {
LOG(DEBUG) << "Found associated cluster " << (*cluster);
has_associated_cluster = true;
matched_tracks++;
auto clusterLocal = m_detector->globalToLocal(cluster->global());
hGlobalEfficiencyMap_clustPos->Fill(
cluster->global().x(), cluster->global().y(), has_associated_cluster);
hChipEfficiencyMap_clustPos->Fill(
m_detector->getColumn(clusterLocal), m_detector->getRow(clusterLocal), has_associated_cluster);
break;
}
}
}
hGlobalEfficiencyMap->Fill(globalIntercept.X(), globalIntercept.Y(), has_associated_cluster);
hChipEfficiencyMap->Fill(
hGlobalEfficiencyMap_trackPos->Fill(globalIntercept.X(), globalIntercept.Y(), has_associated_cluster);
hChipEfficiencyMap_trackPos->Fill(
m_detector->getColumn(localIntercept), m_detector->getRow(localIntercept), has_associated_cluster);
// For pixels, only look at the ROI:
if(is_within_roi) {
hPixelEfficiencyMap->Fill(xmod, ymod, has_associated_cluster);
hPixelEfficiencyMap_trackPos->Fill(xmod, ymod, has_associated_cluster);
}
if(has_associated_cluster == false) {
hGlobalEfficiencyMap_clustPos->Fill(globalIntercept.X(), globalIntercept.Y(), has_associated_cluster);
hChipEfficiencyMap_clustPos->Fill(
m_detector->getColumn(localIntercept), m_detector->getRow(localIntercept), has_associated_cluster);
}
}
......@@ -166,6 +198,8 @@ StatusCode AnalysisEfficiency::run(std::shared_ptr<Clipboard> clipboard) {
}
void AnalysisEfficiency::finalise() {
LOG(INFO) << "No. matched tracks=" << matched_tracks;
LOG(INFO) << "Total no. tracks=" << total_tracks;
LOG(STATUS) << "Total efficiency of detector " << m_detector->name() << ": "
<< (100 * matched_tracks / (total_tracks > 0 ? total_tracks : 1)) << "%, measured with " << total_tracks
<< " tracks";
......
......@@ -39,11 +39,13 @@ namespace corryvreckan {
private:
std::shared_ptr<Detector> m_detector;
TProfile2D* hPixelEfficiencyMap;
TProfile2D* hChipEfficiencyMap;
TProfile2D* hGlobalEfficiencyMap;
TProfile2D* hPixelEfficiencyMap_trackPos;
TProfile2D* hChipEfficiencyMap_trackPos;
TProfile2D* hGlobalEfficiencyMap_trackPos;
TProfile2D* hChipEfficiencyMap_clustPos;
TProfile2D* hGlobalEfficiencyMap_clustPos;
double m_chi2ndofCut, m_timeCutFrameEdge;
double m_chi2ndofCut, m_timeCutFrameEdge, m_pixelTolerance, m_inpixelBinSize;
int total_tracks, matched_tracks;
};
......
......@@ -8,13 +8,18 @@
This module measures the efficiency of the device under test by comparing its cluster positions with the interpolated track position at the DUT.
### Parameters
* `pixel_tolerance`: Parameter to discard tracks, which are extrapolated to
the edge of the DUT. Defaults to `1.`, which excludes column/row zero and max.
* `time_cut_frameedge`: Parameter to discard telescope tracks at the frame edges (start and end of the current event window). Defaults to `20ns`.
* `chi2ndof_cut`: Acceptance criterion for telescope tracks, defaults to a value of `3`.
* `inpixel_bin_size`: Parameter to set the bin size of the in-pixel 2D efficiency histogram. This should be given in units of distance and the same value is used in both axes. Defaults to `1.0um`.
### Plots produced
* 2D Map of in-pixel efficiency
* 2D Map of the chip efficiency in local coordinates
* 2D Map of the chip efficiency on global coordinates
* 2D Map of the chip efficiency in local coordinates, filled at the position of the track intercept point
* 2D Map of the chip efficiency on global coordinates, filled at the position of the track intercept point
* 2D Map of the chip efficiency in local coordinates, filled at the position of the associated cluster centre
* 2D Map of the chip efficiency on global coordinates, filled at the position of the associated cluster centre
### Usage
```toml
......
......@@ -37,6 +37,16 @@ void ClusteringSpatial::initialise() {
400,
-m_detector->size().Y() / 1.5,
m_detector->size().Y() / 1.5);
title = m_detector->name() + " Cluster Position (Local);x [px];y [px];events";
clusterPositionLocal = new TH2F("clusterPositionLocal",
title.c_str(),
m_detector->nPixels().X(),
-m_detector->nPixels().X() / 2.,
m_detector->nPixels().X() / 2.,
m_detector->nPixels().Y(),
-m_detector->nPixels().Y() / 2.,
m_detector->nPixels().Y() / 2.);
}
StatusCode ClusteringSpatial::run(std::shared_ptr<Clipboard> clipboard) {
......@@ -127,7 +137,8 @@ StatusCode ClusteringSpatial::run(std::shared_ptr<Clipboard> clipboard) {
clusterWidthColumn->Fill(cluster->columnWidth());
clusterTot->Fill(cluster->tot() * 1e-3);
clusterPositionGlobal->Fill(cluster->global().x(), cluster->global().y());
clusterPositionLocal->Fill(cluster->local().x(), cluster->local().y());
LOG(DEBUG) << "cluster local: " << cluster->local();
deviceClusters->push_back(cluster);
}
......
......@@ -33,6 +33,7 @@ namespace corryvreckan {
TH1F* clusterWidthColumn;
TH1F* clusterTot;
TH2F* clusterPositionGlobal;
TH2F* clusterPositionLocal;
};
} // namespace corryvreckan
#endif // ClusteringSpatial_H
......@@ -18,6 +18,7 @@ For each detector the following plots are produced:
* Cluster width (columns, in Y) histogram
* Cluster ToT histogram
* 2D cluster positions in global coordinates
* 2D cluster positions in local coordinates
### Usage
```toml
......
......@@ -35,20 +35,29 @@ StatusCode DUTAssociation::run(std::shared_ptr<Clipboard> clipboard) {
double xdistance = intercept.X() - cluster->global().x();
double ydistance = intercept.Y() - cluster->global().y();
if(abs(xdistance) > spatialCut.x() || abs(ydistance) > spatialCut.y()) {
LOG(DEBUG) << "Discarding DUT cluster with distance (" << abs(xdistance) << "," << abs(ydistance) << ")";
LOG(DEBUG) << "Discarding DUT cluster with distance (" << Units::display(abs(xdistance), {"um", "mm"}) << ","
<< Units::display(abs(ydistance), {"um", "mm"}) << ")";
continue;
}
// Check if the cluster is close in time
if(std::abs(cluster->timestamp() - track->timestamp()) > timingCut) {
LOG(DEBUG) << "Discarding DUT cluster with time difference "
<< Units::display(std::abs(cluster->timestamp() - track->timestamp()), {"ms", "s"});
continue;
}
LOG(DEBUG) << "Found associated cluster with distance (" << abs(xdistance) << "," << abs(ydistance) << ")";
track->addAssociatedCluster(cluster);
assoc_cluster_counter++;
}
}
// Return value telling analysis to keep running
return StatusCode::Success;
}
void DUTAssociation::finalise() {
LOG(INFO) << "In total, " << assoc_cluster_counter << " clusters are associated to tracks.";
return;
}
......@@ -22,11 +22,14 @@ namespace corryvreckan {
// Functions
StatusCode run(std::shared_ptr<Clipboard> clipboard);
void finalise();
private:
std::shared_ptr<Detector> m_detector;
double timingCut;
ROOT::Math::XYVector spatialCut;
int assoc_cluster_counter = 0;
};
} // namespace corryvreckan
#endif // DUTAssociation_H
/**
* @file
* @brief Implementation of [Dummy] module
* @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
* Copyright (c) 2019 CERN and the Corryvreckan 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.
......
/**
* @file
* @brief Definition of [Dummy] module
* @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
* Copyright (c) 2019 CERN and the Corryvreckan 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.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment