19#include "TGeoMatrix.h"
40constexpr Int_t kIact = 3;
49 std::uniform_real_distribution<double> ux(lo[0], hi[0]);
50 std::uniform_real_distribution<double> uy(lo[1], hi[1]);
51 std::uniform_real_distribution<double> uz(lo[2], hi[2]);
52 return {ux(rng), uy(rng), uz(rng)};
55Point3D isotropicDir(std::mt19937_64& rng)
57 std::uniform_real_distribution<double> uCos(-1., 1.);
58 std::uniform_real_distribution<double> uPhi(0., 2. * M_PI);
59 const double cosTheta = uCos(rng);
60 const double sinTheta = std::sqrt(std::max(0., 1. - cosTheta * cosTheta));
61 const double phi = uPhi(rng);
65bool isBig(
double d) {
return d >= 0.9 * TGeoShape::Big(); }
75 bits += 0x9e3779b97f4a7c15ULL + (acc << 6) + (acc >> 2);
88 const Point3D halfExtent = scale(sub(bboxMax, bboxMin), 0.5);
94 const double diag = std::sqrt(normSq(sub(bboxMax, bboxMin)));
98 std::mt19937_64 rng(cfg.
seed);
101 for (
int i = 0;
i < cfg.
nBulk; ++
i) {
102 out.
bulkPoints.push_back(sampleUniform(rng, inflatedLo, inflatedHi));
108 long long attempts = 0;
111 const Point3D p = sampleUniform(rng, bboxMin, bboxMax);
112 const bool in =
reference->Contains(p.data());
113 const double s =
reference->Safety(p.data(), in);
123 long long attempts = 0;
126 const Point3D p = sampleUniform(rng, bboxMin, bboxMax);
135 std::uniform_real_distribution<double> u01(0., 1.);
137 long long attempts = 0;
140 const Point3D origin = sampleUniform(rng, inflatedLo, inflatedHi);
148 double len = std::sqrt(normSq(delta));
150 dir = isotropicDir(rng);
152 dir = scale(delta, 1. /
len);
155 dir = isotropicDir(rng);
164 long long attempts = 0;
183enum class MismatchClass { WithinBand,
187void recordOffender(ValidationResult&
result,
const ValidationOptions& opt, Offender&& off,
188 MismatchClass mismatchClass)
190 switch (mismatchClass) {
191 case MismatchClass::WithinBand:
192 ++
result.nMismatchWithinBand;
194 case MismatchClass::MissedSurface:
195 ++
result.nMismatchMissedSurface;
197 case MismatchClass::Unexplained:
198 ++
result.nMismatchUnexplained;
201 result.worstDeviation = std::max(
result.worstDeviation, std::fabs(off.deviation));
202 result.worstOffenders.push_back(std::move(off));
203 std::sort(
result.worstOffenders.begin(),
result.worstOffenders.end(),
204 [](
const Offender&
a,
const Offender&
b) { return std::fabs(a.deviation) > std::fabs(b.deviation); });
205 if (
result.worstOffenders.size() > opt.maxOffenders) {
206 result.worstOffenders.resize(opt.maxOffenders);
211double allowedCrossingShift(
const TGeoShape* normalSource,
const Point3D& probePoint,
212 const Point3D& dir,
const ValidationOptions& opt,
double& cosIncidence)
215 if (normalSource !=
nullptr) {
216 double normal[3] = {0., 0., 0.};
217 normalSource->ComputeNormal(probePoint.data(), dir.data(), normal);
218 const double normalNorm =
219 std::sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
220 if (normalNorm > 0.) {
221 const double dotProduct =
222 (normal[0] * dir[0] + normal[1] * dir[1] + normal[2] * dir[2]) / normalNorm;
223 cosIncidence = std::fabs(dotProduct);
226 const double effectiveCosine = std::max(cosIncidence, opt.minIncidenceCosine);
227 return std::max(opt.distanceTolerance, opt.meshBand / effectiveCosine);
232MismatchClass classifyDistanceMismatch(
const TGeoShape*
reference,
const Ray& ray,
double dc,
233 double dr,
bool dcBig,
bool drBig,
234 const ValidationOptions& opt,
double& cosIncidence)
239 if (dcBig != drBig) {
240 return MismatchClass::MissedSurface;
242 const Point3D probePoint = add(ray.origin, scale(ray.dir, dr));
243 const double allowed = allowedCrossingShift(
reference, probePoint, ray.dir, opt, cosIncidence);
244 return std::fabs(dc - dr) <= allowed ? MismatchClass::WithinBand : MismatchClass::Unexplained;
254 for (
const auto& p : points) {
255 const bool bc = candidate->Contains(p.data());
256 const bool br =
reference->Contains(p.data());
261 const double refSafety =
reference->Safety(p.data(), br);
270 recordOffender(
result, opt, std::move(off),
271 refSafety < opt.
meshBand ? MismatchClass::WithinBand : MismatchClass::Unexplained);
279ValidationResult validateDistance(
const TGeoShape* candidate,
const TGeoShape*
reference,
280 const std::vector<Ray>& rays,
const ValidationOptions& opt,
bool inside)
282 const auto distance = [&](
const TGeoShape* shape,
const Ray&
r) {
283 return inside ? shape->DistFromInside(
r.origin.data(),
r.dir.data(), kIact, opt.stepmax)
284 : shape->DistFromOutside(
r.
origin.
data(),
r.dir.
data(), kIact, opt.stepmax);
287 result.nSamples = rays.size();
288 for (
const auto&
r : rays) {
289 const double dc =
distance(candidate,
r);
291 const bool dcBig = isBig(dc);
292 const bool drBig = isBig(dr);
293 if (dcBig && drBig) {
297 if (!dcBig && !drBig && std::fabs(dc - dr) <= opt.distanceTolerance) {
301 double cosIncidence = 1.;
302 const MismatchClass mismatchClass =
303 classifyDistanceMismatch(
reference,
r, dc, dr, dcBig, drBig, opt, cosIncidence);
305 off.point =
r.origin;
307 off.candidateValue = dcBig ? opt.stepmax : dc;
308 off.referenceValue = drBig ? opt.stepmax : dr;
309 off.deviation = off.candidateValue - off.referenceValue;
310 off.incidenceCosine = cosIncidence;
311 recordOffender(
result, opt, std::move(off), mismatchClass);
320 return validateDistance(candidate,
reference, rays, opt,
false);
326 return validateDistance(candidate,
reference, rays, opt,
true);
332 static const std::array<Point3D, 6> kProbeDirs = {
338 for (
const auto& p : points) {
339 const bool in = shape->Contains(p.data());
340 const double s = shape->Safety(p.data(), in);
342 double minProbed = TGeoShape::Big();
343 for (
const auto&
d : kProbeDirs) {
344 const double dist = in ? shape->DistFromInside(p.data(),
d.data(), kIact, opt.
stepmax)
345 : shape->DistFromOutside(p.data(),
d.data(), kIact, opt.
stepmax);
346 minProbed = std::min(minProbed, isBig(dist) ? opt.
stepmax : dist);
351 if (!violatesLowerBound && !violatesUpperBound) {
361 recordOffender(
result, opt, std::move(off), MismatchClass::Unexplained);
371constexpr double kUnknownDistance = -1.;
373double oracleDistanceAt(
const std::vector<double>& distances,
size_t index)
375 return index < distances.size() ? distances[
index] : kUnknownDistance;
380 const std::vector<Point3D>& points,
381 const std::vector<int>& oracleState,
382 const std::vector<double>& oracleBoundaryDistance,
388 const int state =
index < oracleState.size() ? oracleState[
index] : -1;
389 const double boundaryDistance = oracleDistanceAt(oracleBoundaryDistance,
index);
391 if (
state < 0 || (boundaryDistance >= 0. && boundaryDistance < opt.
meshBand)) {
395 const bool candidateInside = candidate->Contains(points[
index].
data());
396 if (candidateInside == (
state == 1)) {
406 off.
deviation = boundaryDistance >= 0. ? boundaryDistance : 0.;
408 recordOffender(
result, opt, std::move(off), MismatchClass::Unexplained);
414 const std::vector<Ray>& rays,
415 const std::vector<double>& oracleDistance,
417 const std::vector<int>& oracleOriginState)
422 if (
index >= oracleDistance.size()) {
427 bool askInside = wantInside;
428 if (
index < oracleOriginState.size()) {
434 askInside =
state == 1;
435 if (askInside != wantInside) {
439 const auto& ray = rays[
index];
440 const double dc = askInside
441 ? candidate->DistFromInside(ray.origin.data(), ray.dir.data(), kIact, opt.
stepmax)
442 : candidate->DistFromOutside(ray.origin.data(), ray.dir.data(), kIact, opt.
stepmax);
443 const double dr = oracleDistance[
index];
444 const bool dcBig = isBig(dc);
445 const bool drBig = isBig(dr);
446 if (dcBig && drBig) {
455 double cosIncidence = 1.;
456 const MismatchClass mismatchClass =
457 classifyDistanceMismatch(
nullptr, ray, dc, dr, dcBig, drBig, opt, cosIncidence);
459 off.
point = ray.origin;
465 recordOffender(
result, opt, std::move(off), mismatchClass);
471 const std::vector<Point3D>& points,
472 const std::vector<double>& oracleBoundaryDistance,
478 const double trueDistance = oracleDistanceAt(oracleBoundaryDistance,
index);
479 if (trueDistance < 0.) {
483 const bool inside = candidate->Contains(points[
index].
data());
484 const double safety = candidate->Safety(points[
index].
data(), inside);
488 if (!violatesLowerBound && !violatesUpperBound) {
498 recordOffender(
result, opt, std::move(off), MismatchClass::Unexplained);
508template <
typename Po
intKernel>
509TimingResult timePointKernel(
const std::vector<Point3D>& points,
int warmupRepeats,
int timedRepeats,
510 PointKernel&& kernel)
512 for (
int warmup = 0; warmup < warmupRepeats; ++warmup) {
513 for (
const auto& point : points) {
514 volatile double sink = kernel(point);
519 const auto start = std::chrono::steady_clock::now();
520 for (
int repeat = 0; repeat < timedRepeats; ++repeat) {
521 for (
const auto& point : points) {
525 const auto stop = std::chrono::steady_clock::now();
527 result.nCalls = points.size() *
static_cast<size_t>(timedRepeats);
528 const double nanoseconds = std::chrono::duration<double, std::nano>(stop -
start).count();
529 result.nsPerCall =
result.nCalls > 0 ? nanoseconds /
static_cast<double>(
result.nCalls) : 0.;
530 result.checksum = checksum;
538 return timePointKernel(points, warmupRepeats, timedRepeats,
539 [&](
const Point3D& p) {
return shape->Contains(p.data()) ? 1. : 0.; });
543 int timedRepeats,
double stepmax)
546 return shape->DistFromOutside(
origin.data(), dir.data(), kIact, stepmax);
554 return shape->DistFromInside(
origin.data(), dir.data(), kIact, TGeoShape::Big());
561 return timePointKernel(points, warmupRepeats, timedRepeats,
562 [&](
const Point3D& p) {
return shape->Safety(p.data(), shape->Contains(p.data())); });
571constexpr const char* kShapeKeyName =
"shape";
574constexpr const char* kPlacementKeyName =
"placement";
579 const auto fail = [
error](
const std::string& why) -> TGeoShape* {
580 if (
error !=
nullptr) {
585 std::unique_ptr<TFile>
file(TFile::Open(
path.c_str(),
"READ"));
587 return fail(
path +
": cannot be opened as a ROOT file");
590 if (
object ==
nullptr) {
592 TIter next(
file->GetListOfKeys());
593 while (
auto*
key =
static_cast<TKey*
>(next())) {
594 TClass* cl = TClass::GetClass(
key->GetClassName());
595 if (cl !=
nullptr && cl->InheritsFrom(TGeoShape::Class())) {
596 object =
key->ReadObj();
601 if (
object ==
nullptr) {
602 return fail(
path +
": holds no object inheriting from TGeoShape (expected key \"" +
603 kShapeKeyName +
"\")");
605 auto* shape =
dynamic_cast<TGeoShape*
>(
object);
606 if (shape ==
nullptr) {
607 const std::string
className =
object->ClassName();
609 return fail(
path +
": key \"" + kShapeKeyName +
"\" holds a " +
className +
610 ", which does not inherit from TGeoShape");
614 if (
auto* flat =
dynamic_cast<O2FlatCSG*
>(shape); flat !=
nullptr && !flat->
IsClosed()) {
617 ": the O2FlatCSG it holds refused to close, so its sub-cell boxes could "
618 "not be rebuilt (see the Error above)");
627 std::unique_ptr<TFile>
file(TFile::Open(
path.c_str(),
"READ"));
631 auto* stored =
file->Get<TGeoHMatrix>(kPlacementKeyName);
632 if (stored ==
nullptr) {
636 auto* placement =
new TGeoHMatrix(*stored);
646 const TGeoMatrix* placement, std::string*
error)
648 std::unique_ptr<TFile>
file(TFile::Open(
path.c_str(),
"RECREATE"));
650 if (
error !=
nullptr) {
651 *
error =
path +
": cannot be opened for writing";
655 const int written =
file->WriteTObject(&shape, kShapeKeyName);
657 if (placement !=
nullptr && !placement->IsIdentity()) {
658 TGeoHMatrix stored(*placement);
659 stored.SetName(kPlacementKeyName);
660 file->WriteTObject(&stored, kPlacementKeyName);
664 if (
error !=
nullptr) {
665 *
error =
path +
": WriteTObject wrote 0 bytes";
header::DataOrigin origin
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines the history is shortened to this when it reaches twice the size Time in seconds an info message fades out after setInfoTime h OpenGL display display Font size Do not use Freetype for font Renderer for event Frontend to use for event ROOT macros used as track filter Console script file
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events Show standalone event Run event generator maximum time bin of continuous for triggered for automatic continuous for automatic continuous triggered uint64_t
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events d
char error[CURL_ERROR_SIZE]
Validation and timing harness for TGeoShape navigation, typed on plain TGeoShape*.
GLboolean GLboolean GLboolean b
GLsizei GLsizei GLfloat distance
GLsizei const GLfloat * value
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLsizei const GLchar *const * path
GLenum GLenum GLsizei len
GLboolean GLboolean GLboolean GLboolean a
uint64_t mixDouble(uint64_t acc, double value)
bool saveShapeToRootFile(const std::string &path, const TGeoShape &shape, std::string *error=nullptr)
Write a shape sidecar, with placement under "placement" unless it is null or the identity.
ValidationResult validateContainsAgainstOracle(const TGeoShape *candidate, const std::vector< Point3D > &points, const std::vector< int > &oracleState, const std::vector< double > &oracleBoundaryDistance, const ValidationOptions &opt={})
oracleState: 1 inside, 0 outside, -1 declined; oracleBoundaryDistance may cover only a prefix of poin...
SampleSet generateSamples(const TGeoShape *reference, const Point3D &bboxMin, const Point3D &bboxMax, const SampleConfig &cfg={})
A deterministic sample set from cfg.seed and the bbox; reference, the trusted mesh,...
ValidationResult validateSafetyAgainstOracle(const TGeoShape *candidate, const std::vector< Point3D > &points, const std::vector< double > &oracleBoundaryDistance, const ValidationOptions &opt={})
Safety's contract against the oracle's exact distance: 0 <= safety <= trueDistance.
TimingResult timeDistFromInside(const TGeoShape *shape, const std::vector< Ray > &rays, int warmupRepeats, int timedRepeats)
TimingResult timeDistFromOutside(const TGeoShape *shape, const std::vector< Ray > &rays, int warmupRepeats, int timedRepeats, double stepmax=TGeoShape::Big())
ValidationResult validateSafety(const TGeoShape *shape, const std::vector< Point3D > &points, const ValidationOptions &opt={})
Check one shape's Safety() lower-bound contract against its own DistFrom* along six probe directions;...
ValidationResult validateDistFromInside(const TGeoShape *candidate, const TGeoShape *reference, const std::vector< Ray > &rays, const ValidationOptions &opt={})
ValidationResult validateDistanceAgainstOracle(const TGeoShape *candidate, const std::vector< Ray > &rays, const std::vector< double > &oracleDistance, bool wantInside, const ValidationOptions &opt={}, const std::vector< int > &oracleOriginState={})
TimingResult timeContains(const TGeoShape *shape, const std::vector< Point3D > &points, int warmupRepeats, int timedRepeats)
TGeoHMatrix * loadShapePlacementFromRootFile(const std::string &path)
Read the shape's placement, or nullptr when there is none, meaning the identity. The caller owns it.
TGeoShape * loadShapeFromRootFile(const std::string &path, std::string *error=nullptr)
Read the single TGeoShape of a shape_<part>.root sidecar; nullptr on failure, with the reason in *err...
ValidationResult validateContains(const TGeoShape *candidate, const TGeoShape *reference, const std::vector< Point3D > &points, const ValidationOptions &opt={})
TimingResult timeRayKernel(const std::vector< Ray > &rays, int warmupRepeats, int timedRepeats, RayKernel &&kernel)
Time a per-ray kernel kernel(origin, dir) exactly like the timeDistFrom* functions,...
ValidationResult validateDistFromOutside(const TGeoShape *candidate, const TGeoShape *reference, const std::vector< Ray > &rays, const ValidationOptions &opt={})
TimingResult timeSafety(const TGeoShape *shape, const std::vector< Point3D > &points, int warmupRepeats, int timedRepeats)
std::array< double, 3 > Point3D
double normSq(const Vec3 &vector)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
Parameters of generateSamples; the counts are targets, and a category may come back short.
int nInsideRays
rays from inside origins, for DistFromInside
int nOutsideRays
rays from outside origins, for DistFromOutside
int nBulk
uniform points over the inflated bbox
double boundaryBand
absolute distance (cm); <0 auto-picks 1e-3 * bbox diagonal
int nBoundary
points within boundaryBand of the reference surface
int maxRejectionAttempts
attempts per accepted sample before giving up on that category
uint64_t seed
every SampleSet is fully determined by this and the bbox
int nInside
points accepted by the reference Contains()
double bboxInflate
fractional bbox half-extent padding for bulk/outside sampling
std::vector< Point3D > boundaryPoints
std::vector< Point3D > bulkPoints
std::vector< Ray > outsideRays
std::vector< Ray > insideRays
std::vector< Point3D > insidePoints
double distanceTolerance
absolute agreement tolerance for distances (cm)