Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
quantumbouncer2d.cpp
Go to the documentation of this file.
1/**
2 * @file quantumbouncer2d.cpp
3 * @brief Implements the quantumbouncer2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/quantumbouncer2d.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
12#include "quantumbouncer2d.h"
13
16
17#include <algorithm>
18#include <cmath>
19#include <stdexcept>
20
21namespace qpe {
22namespace {
23
24/**
25 * @brief Performs fail.
26 * @param reason The reason value used by the operation.
27 * @param message The message value used by the operation.
28 */
29void fail(std::string *reason, const char *message) noexcept
30{
31 if (reason) *reason = message;
32}
33
34const double kAiryZeros[] = {
35 2.338107410459767,
36 4.087949444130970,
37 5.520559828095551,
38 6.786708090071759,
39 7.944133587120853,
40 9.022650853340981,
41 10.04017434155809,
42 11.00852430373326
43};
44
45} // namespace
46
49 std::string *reason) noexcept
50{
51 geometry = QuantumBouncerGeometry2D();
52 if (reason) reason->clear();
53
54 const PotentialObject2D *floor = nullptr;
55 const PotentialObject2D *linear = nullptr;
56 for (const PotentialObject2D &object : scene.objects()) {
57 if (!object.enabled) continue;
58 if (object.type == PotentialObjectType2D::Rectangle && object.value > 0.0f &&
59 object.width > 4.0f * object.height) {
60 if (floor != nullptr) {
61 fail(reason, "Quantum bouncer expects exactly one horizontal positive Rectangle floor.");
62 return false;
63 }
64 floor = &object;
65 } else if (object.type == PotentialObjectType2D::Linear &&
66 std::fabs(object.slopeX) < 1.0e-7f && object.slopeY > 0.0f) {
67 if (linear != nullptr) {
68 fail(reason, "Quantum bouncer expects exactly one downward Linear-Y force term.");
69 return false;
70 }
71 linear = &object;
72 } else {
73 fail(reason, "Quantum bouncer scene must contain only one floor Rectangle and one Linear-Y term.");
74 return false;
75 }
76 }
77
78 if (floor == nullptr || linear == nullptr) {
79 fail(reason, "Quantum bouncer requires one floor Rectangle and one positive Linear-Y slope.");
80 return false;
81 }
82
83 geometry.valid = true;
84 geometry.floorY = static_cast<double>(floor->centerY) + 0.5 * floor->height;
85 geometry.floorPotential = floor->value;
86 geometry.force = linear->slopeY;
87 return true;
88}
89
91 double mass,
92 double hbar) noexcept
93{
94 if (!geometry.valid || !(geometry.force > 0.0) || !(mass > 0.0) || !(hbar > 0.0) ||
95 !std::isfinite(mass) || !std::isfinite(hbar)) {
96 return 0.0;
97 }
98 return std::cbrt((hbar * hbar) / (2.0 * mass * geometry.force));
99}
100
101double QuantumBouncer2D::airyEnergy(int stateIndex,
102 const QuantumBouncerGeometry2D &geometry,
103 double mass,
104 double hbar) noexcept
105{
106 if (stateIndex < 0 || stateIndex >= static_cast<int>(sizeof(kAiryZeros) / sizeof(kAiryZeros[0]))) {
107 return 0.0;
108 }
109 const double ell = airyLength(geometry, mass, hbar);
110 return ell > 0.0 ? geometry.force * ell * kAiryZeros[stateIndex] : 0.0;
111}
112
114 const QuantumBouncerGeometry2D &geometry,
115 double mass,
116 double hbar) noexcept
117{
118 const double ell = airyLength(geometry, mass, hbar);
119 if (!(ell > 0.0) || stateIndex < 0 ||
120 stateIndex >= static_cast<int>(sizeof(kAiryZeros) / sizeof(kAiryZeros[0]))) {
121 return geometry.floorY;
122 }
123 return geometry.floorY + ell * kAiryZeros[stateIndex];
124}
125
127{
128 std::vector<float> result(field.height(), 0.0f);
129 if (field.empty()) return result;
130
131 const double dx = static_cast<double>(field.dx());
132 for (std::size_t y = 0u; y < field.height(); ++y) {
133 double sum = 0.0;
134 for (std::size_t x = 0u; x < field.width(); ++x) {
135 const ComplexField2D::Value value = field(x, y);
136 const double re = value.real();
137 const double im = value.imag();
138 sum += re * re + im * im;
139 }
140 result[y] = static_cast<float>(sum * dx);
141 }
142 return result;
143}
144
146 const QuantumBouncerGeometry2D &geometry,
147 double bandHeight) noexcept
148{
149 if (field.empty() || !geometry.valid || !(bandHeight > 0.0)) return 0.0;
150 const double cellArea = static_cast<double>(field.dx()) * field.dy();
151 double probability = 0.0;
152 for (std::size_t y = 0u; y < field.height(); ++y) {
153 const double py = GaussianPacket2D::yCoordinate(field, y);
154 if (py < geometry.floorY || py > geometry.floorY + bandHeight) continue;
155 for (std::size_t x = 0u; x < field.width(); ++x) {
156 const ComplexField2D::Value value = field(x, y);
157 probability += (static_cast<double>(value.real()) * value.real() +
158 static_cast<double>(value.imag()) * value.imag()) * cellArea;
159 }
160 }
161 return probability;
162}
163
165 const QuantumBouncerGeometry2D &geometry,
166 double mass,
167 double initialY,
168 double initialMomentumY) noexcept
169{
170 if (!geometry.valid || !(geometry.force > 0.0) || !(mass > 0.0) || !(time >= 0.0)) {
171 return initialY;
172 }
173
174 const double a = geometry.force / mass;
175 double y = std::max(initialY, geometry.floorY);
176 double v = initialMomentumY / mass;
177 double remaining = time;
178
179 for (int impact = 0; impact < 128 && remaining > 1.0e-12; ++impact) {
180 const double height = std::max(0.0, y - geometry.floorY);
181 const double discriminant = std::max(0.0, v * v + 2.0 * a * height);
182 const double hit = (v + std::sqrt(discriminant)) / a;
183 if (!(hit > 1.0e-12) || hit > remaining) {
184 return std::max(geometry.floorY, y + v * remaining - 0.5 * a * remaining * remaining);
185 }
186 y = geometry.floorY;
187 v = -(v - a * hit); // elastic reflection
188 remaining -= hit;
189 }
190 return std::max(geometry.floorY, y);
191}
192
194 : maximumSamples_(maximumSamples > 0u ? maximumSamples : 1u)
195{
196 samples_.reserve(std::min<std::size_t>(maximumSamples_, 1200u));
197}
198
200{
202 initialY_ = 0.0;
203 initialMomentumY_ = 0.0;
204 dy_ = 0.0f;
205 gridHeight_ = 0u;
206 samples_.clear();
207}
208
210 double initialY,
211 double initialMomentumY)
212{
213 clear();
214 if (!geometry.valid || !std::isfinite(initialY) || !std::isfinite(initialMomentumY)) return false;
218 return true;
219}
220
221void QuantumBouncerHistory2D::setMaximumSamples(std::size_t maximumSamples)
222{
223 if (maximumSamples == 0u) throw std::invalid_argument("QuantumBouncerHistory2D maximumSamples must be > 0");
225 trimToLimit();
226}
227
228bool QuantumBouncerHistory2D::append(double time, const ComplexField2D &field,
229 double mass, double hbar)
230{
231 if (!configured() || field.empty() || !(mass > 0.0) || !(hbar > 0.0)) return false;
233 sample.time = time;
235 sample.meanY = spatial.meanY;
236 sample.sigmaY = spatial.sigmaY;
237 sample.norm = spatial.norm2;
238 const double ell = QuantumBouncer2D::airyLength(geometry_, mass, hbar);
239 sample.floorProbability = QuantumBouncer2D::probabilityNearFloor(field, geometry_, 2.0 * ell);
240 sample.marginalY = QuantumBouncer2D::marginalProbabilityY(field);
241 dy_ = field.dy();
242 gridHeight_ = field.height();
243 samples_.push_back(sample);
244 trimToLimit();
245 return true;
246}
247
249{
250 if (samples_.size() <= maximumSamples_) return;
251 const std::size_t removeCount = samples_.size() - maximumSamples_;
252 samples_.erase(samples_.begin(), samples_.begin() + static_cast<std::ptrdiff_t>(removeCount));
253}
254
255} // 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.
std::complex< float > Value
float dy() const noexcept
Returns the Y grid spacing.
std::size_t width() const noexcept
Returns the number of samples along X.
static double yCoordinate(const ComplexField2D &field, std::size_t y) noexcept
Performs y coordinate.
Implements the Potential Scene2D component.
static bool identify(const PotentialScene2D &scene, QuantumBouncerGeometry2D &geometry, std::string *reason=nullptr) noexcept
Returns identify.
static double probabilityNearFloor(const ComplexField2D &field, const QuantumBouncerGeometry2D &geometry, double bandHeight) noexcept
Returns probability near floor.
static double airyEnergy(int stateIndex, const QuantumBouncerGeometry2D &geometry, double mass, double hbar) noexcept
Performs airy energy.
static double airyTurningHeight(int stateIndex, const QuantumBouncerGeometry2D &geometry, double mass, double hbar) noexcept
Performs airy turning height.
static double classicalHeight(double time, const QuantumBouncerGeometry2D &geometry, double mass, double initialY, double initialMomentumY) noexcept
Performs classical height.
static double airyLength(const QuantumBouncerGeometry2D &geometry, double mass, double hbar) noexcept
Performs airy length.
static std::vector< float > marginalProbabilityY(const ComplexField2D &field)
Performs marginal probability y.
void trimToLimit()
Carries out trim to limit.
void clear() noexcept
Clears stored state and returns the component to its empty/default state.
const QuantumBouncerGeometry2D & geometry() const noexcept
Carries out geometry.
bool configure(const QuantumBouncerGeometry2D &geometry, double initialY, double initialMomentumY)
Returns configure.
QuantumBouncerHistory2D(std::size_t maximumSamples=1200u)
Constructs a Quantum Bouncer History 2D instance.
bool append(double time, const ComplexField2D &field, double mass, double hbar)
Carries out append.
bool configured() const noexcept
Returns configured.
QuantumBouncerGeometry2D geometry_
double initialMomentumY() const noexcept
Initializes ial momentum y.
std::size_t maximumSamples() const noexcept
Carries out maximum samples.
double initialY() const noexcept
Initializes ial y.
void setMaximumSamples(std::size_t maximumSamples)
Sets maximum samples.
std::vector< QuantumBouncerSample2D > samples_
static SpatialObservables2D spatial(const ComplexField2D &field) noexcept
Performs spatial.
Declares the gaussianpacket2d physics/numerical component used by QPE experiments.
Declares the quantumbouncer2d physics/numerical component used by QPE experiments.
Data structure used by the Potential Object2D component.
bool valid() const noexcept
Performs valid.
Data structure used by the Quantum Bouncer Geometry2D component.
Data structure used by the Quantum Bouncer Sample2D component.
Data structure used by the Spatial Observables2D component.
Declares the wavepacketobservables2d physics/numerical component used by QPE experiments.