29 std::vector<std::size_t> columns;
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);
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) {
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));
73 if (!(hbar > 0.0f) || !std::isfinite(
static_cast<double>(hbar))) {
74 throw std::invalid_argument(
"DetectorTemporalFlux2D: hbar must be finite and positive");
76 if (!(mass > 0.0f) || !std::isfinite(
static_cast<double>(mass))) {
77 throw std::invalid_argument(
"DetectorTemporalFlux2D: mass must be finite and positive");
79 if (!(dt > 0.0f) || !std::isfinite(
static_cast<double>(dt))) {
80 throw std::invalid_argument(
"DetectorTemporalFlux2D: dt must be finite and positive");
92 const std::vector<std::size_t> columns =
94 if (columns.empty() || field.
height() < 3u) {
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;
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);
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;
125 stepProbability += positiveCurrent *
static_cast<double>(field.
dy()) * stepDt;
138 std::uint32_t seed)
const
146 bool deterministic)
const
148 std::vector<PositionMeasurement2DPoint> result;
149 if (shots == 0u ||
empty()) {
154 for (std::size_t i = 0u; i <
profile_.size(); ++i) {
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);
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);
181 point.
y =
static_cast<float>((
static_cast<double>(yIndex) - cy) *
static_cast<double>(
dy_));
182 result.push_back(point);
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.
std::vector< double > profile_
bool empty() const noexcept
Performs empty.
float dy() const noexcept
Performs dy.
void clear()
Performs clear.
double totalPositiveCrossingProbability_
std::vector< PositionMeasurement2DPoint > sample(std::size_t shots) const
Performs sample.
void accumulate(const ComplexField2D &field, float hbar, float mass, float dt, const DetectorTemporalFlux2DParams ¶ms)
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.