Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
detectortemporalflux2d.cpp
Go to the documentation of this file.
1/**
2 * @file detectortemporalflux2d.cpp
3 * @brief Implements the detectortemporalflux2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/detectortemporalflux2d.cpp. This file is part of Quantum Phenomena Explorer (QPE),
7 * maintained by Officina Turini. See LICENSE_IT.txt / LICENSE.txt for the
8 * QPE freeware source-available terms and THIRD_PARTY_NOTICES.txt for
9 * third-party components.
10 */
11
13
15
16#include <algorithm>
17#include <cmath>
18#include <complex>
19#include <random>
20#include <stdexcept>
21
22namespace qpe {
23namespace {
24
25std::vector<std::size_t> detectorColumns(const ComplexField2D &field,
26 float detectorX,
27 float thickness)
28{
29 std::vector<std::size_t> columns;
30 if (field.empty()) {
31 return columns;
32 }
33
34 const double cx = 0.5 * static_cast<double>(field.width() - 1u);
35 const double dx = static_cast<double>(field.dx());
36 const double halfThickness = 0.5 * std::max<double>(static_cast<double>(thickness), dx);
37
38 for (std::size_t x = 1u; x + 1u < field.width(); ++x) {
39 const double physicalX = (static_cast<double>(x) - cx) * dx;
40 if (std::fabs(physicalX - static_cast<double>(detectorX)) <= halfThickness) {
41 columns.push_back(x);
42 }
43 }
44
45 if (columns.empty() && field.width() >= 3u) {
46 long nearest = static_cast<long>(std::lround(static_cast<double>(detectorX) / dx + cx));
47 nearest = std::max<long>(1, std::min<long>(static_cast<long>(field.width()) - 2, nearest));
48 columns.push_back(static_cast<std::size_t>(nearest));
49 }
50 return columns;
51}
52
53} // namespace
54
56{
57 profile_.clear();
58 height_ = 0u;
59 dy_ = 0.0f;
60 detectorX_ = 0.0f;
62}
63
65 float hbar,
66 float mass,
67 float dt,
68 const DetectorTemporalFlux2DParams &params)
69{
70 if (field.empty()) {
71 return;
72 }
73 if (!(hbar > 0.0f) || !std::isfinite(static_cast<double>(hbar))) {
74 throw std::invalid_argument("DetectorTemporalFlux2D: hbar must be finite and positive");
75 }
76 if (!(mass > 0.0f) || !std::isfinite(static_cast<double>(mass))) {
77 throw std::invalid_argument("DetectorTemporalFlux2D: mass must be finite and positive");
78 }
79 if (!(dt > 0.0f) || !std::isfinite(static_cast<double>(dt))) {
80 throw std::invalid_argument("DetectorTemporalFlux2D: dt must be finite and positive");
81 }
82
83 if (height_ != field.height() || dy_ != field.dy() ||
84 std::fabs(static_cast<double>(detectorX_ - params.detectorX)) > 1.0e-6) {
85 profile_.assign(field.height(), 0.0);
86 height_ = field.height();
87 dy_ = field.dy();
88 detectorX_ = params.detectorX;
90 }
91
92 const std::vector<std::size_t> columns =
93 detectorColumns(field, params.detectorX, params.thickness);
94 if (columns.empty() || field.height() < 3u) {
95 return;
96 }
97
98 const double inv2Dx = 1.0 / (2.0 * static_cast<double>(field.dx()));
99 const double prefactor = static_cast<double>(hbar) / static_cast<double>(mass);
100 const double stepDt = static_cast<double>(dt);
101 const double invColumns = 1.0 / static_cast<double>(columns.size());
102 double stepProbability = 0.0;
103
104 for (std::size_t y = 1u; y + 1u < field.height(); ++y) {
105 double currentAverage = 0.0;
106 for (std::size_t i = 0u; i < columns.size(); ++i) {
107 const std::size_t x = columns[i];
108 const std::complex<double> psi(
109 static_cast<double>(field(x, y).real()),
110 static_cast<double>(field(x, y).imag()));
111 const std::complex<double> psiXp(
112 static_cast<double>(field(x + 1u, y).real()),
113 static_cast<double>(field(x + 1u, y).imag()));
114 const std::complex<double> psiXm(
115 static_cast<double>(field(x - 1u, y).real()),
116 static_cast<double>(field(x - 1u, y).imag()));
117 const std::complex<double> dPsiDx = (psiXp - psiXm) * inv2Dx;
118 currentAverage += prefactor * std::imag(std::conj(psi) * dPsiDx);
119 }
120 currentAverage *= invColumns;
121 const double positiveCurrent = std::max(0.0, currentAverage);
122 if (positiveCurrent > 0.0 && std::isfinite(positiveCurrent)) {
123 const double weightIncrement = positiveCurrent * stepDt;
124 profile_[y] += weightIncrement;
125 stepProbability += positiveCurrent * static_cast<double>(field.dy()) * stepDt;
126 }
127 }
128
129 totalPositiveCrossingProbability_ += stepProbability;
130}
131
132std::vector<PositionMeasurement2DPoint> DetectorTemporalFlux2D::sample(std::size_t shots) const
133{
134 return sampleWithSeed(shots, 0u, false);
135}
136
137std::vector<PositionMeasurement2DPoint> DetectorTemporalFlux2D::sampleSeeded(std::size_t shots,
138 std::uint32_t seed) const
139{
140 return sampleWithSeed(shots, seed, true);
141}
142
143std::vector<PositionMeasurement2DPoint> DetectorTemporalFlux2D::sampleWithSeed(
144 std::size_t shots,
145 std::uint32_t seed,
146 bool deterministic) const
147{
148 std::vector<PositionMeasurement2DPoint> result;
149 if (shots == 0u || empty()) {
150 return result;
151 }
152
153 double sum = 0.0;
154 for (std::size_t i = 0u; i < profile_.size(); ++i) {
155 if (std::isfinite(profile_[i]) && profile_[i] > 0.0) {
156 sum += profile_[i];
157 }
158 }
159 if (!(sum > 0.0)) {
160 return result;
161 }
162
163 std::mt19937 engine;
164 if (deterministic) {
165 engine.seed(seed);
166 } else {
167 std::random_device rd;
168 std::seed_seq sequence{rd(), rd(), rd(), rd(),
169 static_cast<unsigned int>(shots),
170 static_cast<unsigned int>(profile_.size())};
171 engine.seed(sequence);
172 }
173
174 std::discrete_distribution<std::size_t> distribution(profile_.begin(), profile_.end());
175 result.reserve(shots);
176 const double cy = 0.5 * static_cast<double>(height_ - 1u);
177 for (std::size_t i = 0u; i < shots; ++i) {
178 const std::size_t yIndex = distribution(engine);
180 point.x = detectorX_;
181 point.y = static_cast<float>((static_cast<double>(yIndex) - cy) * static_cast<double>(dy_));
182 result.push_back(point);
183 }
184 return result;
185}
186
187bool DetectorTemporalFlux2D::empty() const noexcept
188{
189 return profile_.empty() || height_ == 0u || !(dy_ > 0.0f) ||
191}
192
197
199{
200 return detectorX_;
201}
202
203float DetectorTemporalFlux2D::dy() const noexcept
204{
205 return dy_;
206}
207
208std::size_t DetectorTemporalFlux2D::sampleCountY() const noexcept
209{
210 return height_;
211}
212
213const std::vector<double> &DetectorTemporalFlux2D::profile() const noexcept
214{
215 return profile_;
216}
217
218} // namespace qpe
Owning Cartesian grid of complex scalar samples.
bool empty() const noexcept
Returns whether the field contains no samples.
float dx() const noexcept
Returns the X grid spacing.
std::size_t height() const noexcept
Returns the number of samples along Y.
float dy() const noexcept
Returns the Y grid spacing.
std::size_t width() const noexcept
Returns the number of samples along X.
double totalPositiveCrossingProbability() const noexcept
Performs total positive crossing probability.
bool empty() const noexcept
Performs empty.
float dy() const noexcept
Performs dy.
std::vector< PositionMeasurement2DPoint > sample(std::size_t shots) const
Performs sample.
void accumulate(const ComplexField2D &field, float hbar, float mass, float dt, const DetectorTemporalFlux2DParams &params)
Performs accumulate.
std::size_t sampleCountY() const noexcept
Samples count y.
std::vector< PositionMeasurement2DPoint > sampleWithSeed(std::size_t shots, std::uint32_t seed, bool deterministic) const
Samples with seed.
float detectorX() const noexcept
Performs detector x.
const std::vector< double > & profile() const noexcept
Performs profile.
std::vector< PositionMeasurement2DPoint > sampleSeeded(std::size_t shots, std::uint32_t seed) const
Samples seeded.
Declares the complex scalar field container representing two-dimensional wave functions.
Declares the detectortemporalflux2d physics/numerical component used by QPE experiments.
std::vector< std::size_t > detectorColumns(const ComplexField2D &field, float detectorX, float thickness)
Configuration values for Detector Temporal Flux2D Params.
Data record containing Position Measurement2D Point.