22constexpr double kTwoPi = 6.283185307179586476925286766559005768;
28bool finite(
double x)
noexcept {
return std::isfinite(x); }
36double coordinate(std::size_t i, std::size_t n,
double d)
noexcept
38 return (
static_cast<double>(i) - 0.5 *
static_cast<double>(n)) * d;
47double fftK(std::size_t i, std::size_t n,
double d)
noexcept
49 const long long m = (i <= n / 2u) ?
static_cast<long long>(i)
50 :
static_cast<long long>(i) -
static_cast<long long>(n);
51 return kTwoPi *
static_cast<double>(m) / (
static_cast<double>(n) * d);
63 if (s.empty())
return;
64 const double cell =
static_cast<double>(s.dx()) *
static_cast<double>(s.dy());
66 for (std::size_t y = 0; y < s.height(); ++y) {
67 const double yy = coordinate(y, s.height(), s.dy());
68 for (std::size_t x = 0; x < s.width(); ++x) {
69 const double xx = coordinate(x, s.width(), s.dx());
70 const double rho = std::norm(s.upper()(x,y)) + std::norm(s.lower()(x,y));
71 const double w = rho * cell;
77 if (n > 0.0) { mx /= n; my /= n; }
82 std::string *reason)
noexcept
84 const bool ok = finite(c.lightSpeed) && finite(c.restMass) && finite(c.centerX) &&
85 finite(c.centerY) && finite(c.sigma) && finite(c.momentumX) &&
86 finite(c.momentumY) && finite(c.analysisEndTime) &&
87 c.lightSpeed >= 0.05 && c.lightSpeed <= 20.0 &&
88 c.restMass >= 0.02 && c.restMass <= 20.0 &&
89 c.sigma >= 0.25 && c.sigma <= 20.0 &&
90 std::abs(c.momentumX) <= 20.0 && std::abs(c.momentumY) <= 20.0 &&
91 std::abs(c.centerX) <= 1000.0 && std::abs(c.centerY) <= 1000.0 &&
92 (c.preparationMode == 0 || c.preparationMode == 1) &&
93 c.analysisEndTime >= 0.2 && c.analysisEndTime <= 200.0;
94 if (!ok && reason) *reason =
"QuantumZitterbewegung2D: invalid configuration";
105 if (reason && reason->empty()) *reason =
"QuantumZitterbewegung2D: invalid spinor or hbar";
109 const double inv4Sigma2 = 1.0 / (4.0 * c.
sigma * c.
sigma);
112 for (std::size_t y = 0; y < s.
height(); ++y) {
113 const double yy = coordinate(y, s.
height(), s.
dy());
114 for (std::size_t x = 0; x < s.
width(); ++x) {
115 const double xx = coordinate(x, s.
width(), s.
dx());
116 const double dx = xx - c.
centerX;
117 const double dy = yy - c.
centerY;
118 const double amp = std::exp(-(dx*dx + dy*dy) * inv4Sigma2);
119 const double ph = kx0 * dx + ky0 * dy;
120 const std::complex<double> a = amp * std::complex<double>(std::cos(ph), std::sin(ph));
132 const double rest = c.
restMass * cc * cc;
133 for (std::size_t y = 0; y < s.
height(); ++y) {
134 const double py = hbar * fftK(y, s.
height(), s.
dy());
135 for (std::size_t x = 0; x < s.
width(); ++x) {
136 const double px = hbar * fftK(x, s.
width(), s.
dx());
137 const double ax = cc * px;
138 const double ay = cc * py;
139 const double e = std::sqrt(ax*ax + ay*ay + rest*rest);
140 const std::complex<double> a(s.
upper()(x,y).real(), s.
upper()(x,y).imag());
141 const std::complex<double> up = 0.5 * (1.0 + rest / e) * a;
142 const std::complex<double> lo = 0.5 * std::complex<double>(ax, ay) / e * a;
152 if (reason) *reason =
"QuantumZitterbewegung2D: normalization failed";
162 if (s.empty() || !(hbar > 0.0) || !finite(hbar))
return out;
168 const double cc = c.lightSpeed;
169 const double rest = c.restMass * cc * cc;
170 double pos = 0.0, neg = 0.0;
171 for (std::size_t y = 0; y < s.height(); ++y) {
172 const double py = hbar * fftK(y, s.height(), s.dy());
173 for (std::size_t x = 0; x < s.width(); ++x) {
174 const double px = hbar * fftK(x, s.width(), s.dx());
175 const double ax = cc * px;
176 const double ay = cc * py;
177 const double e = std::sqrt(ax*ax + ay*ay + rest*rest);
178 const std::complex<double> a(u(x,y).real(), u(x,y).imag());
179 const std::complex<double> b(v(x,y).real(), v(x,y).imag());
180 const double rho = std::norm(a) + std::norm(b);
181 const std::complex<double> h01(ax, -ay);
182 const std::complex<double> h10(ax, ay);
183 const std::complex<double> ha = rest * a + h01 * b;
184 const std::complex<double> hb = h10 * a - rest * b;
185 const double hExp = std::real(std::conj(a)*ha + std::conj(b)*hb);
186 pos += 0.5 * (rho + hExp/e);
187 neg += 0.5 * (rho - hExp/e);
190 const double total = pos + neg;
198 const double rest = c.restMass * c.lightSpeed * c.lightSpeed;
199 const double cp2 = c.lightSpeed*c.lightSpeed *
200 (c.momentumX*c.momentumX + c.momentumY*c.momentumY);
201 return std::sqrt(cp2 + rest*rest);
205 double hbar)
noexcept
207 return hbar > 0.0 ? 2.0 * centralEnergy(c) / hbar : 0.0;
211 double hbar)
noexcept
213 const double w = predictedZitterOmega(c, hbar);
214 return w > 0.0 ? kTwoPi / w : 0.0;
219 const double e = centralEnergy(c);
220 if (!(e > 0.0))
return 0.0;
221 if (c.preparationMode == 1)
222 return c.lightSpeed*c.lightSpeed*c.momentumX/e;
223 return c.restMass*std::pow(c.lightSpeed,4.0)*c.momentumX/(e*e);
228 const double e = centralEnergy(c);
229 if (!(e > 0.0))
return 0.0;
230 if (c.preparationMode == 1)
231 return c.lightSpeed*c.lightSpeed*c.momentumY/e;
232 return c.restMass*std::pow(c.lightSpeed,4.0)*c.momentumY/(e*e);
237 double time,
double x0,
double y0)
noexcept
241 if (s.empty())
return out;
243 const double cell =
static_cast<double>(s.dx()) *
static_cast<double>(s.dy());
244 double vx = 0.0, vy = 0.0, n = 0.0;
245 for (std::size_t i = 0; i < s.upper().size(); ++i) {
246 const std::complex<double> a(s.upper().data()[i].real(), s.upper().data()[i].imag());
247 const std::complex<double> b(s.lower().data()[i].real(), s.lower().data()[i].imag());
248 const double w = (std::norm(a)+std::norm(b))*cell;
250 vx += c.lightSpeed * 2.0 * std::real(std::conj(a)*b) * cell;
251 vy += c.lightSpeed * 2.0 * std::imag(std::conj(a)*b) * cell;
253 if (n > 0.0) { vx /= n; vy /= n; }
260 out.
norm = s.norm2();
283 !(
hbar > 0.0) || !finite(
hbar))
return false;
305 const std::size_t n =
samples_.size();
313 double st = 0.0, stt = 0.0, sx = 0.0, sy = 0.0, stx = 0.0, sty = 0.0;
315 st += a.time; stt += a.time * a.time;
316 sx += a.meanX; sy += a.meanY;
317 stx += a.time * a.meanX; sty += a.time * a.meanY;
319 const double dn =
static_cast<double>(n);
320 const double denominator = dn * stt - st * st;
321 if (!(std::abs(denominator) > 1.0e-18))
return;
328 a.residualX = a.meanX - (interceptX +
fittedDriftX_ * a.time);
329 a.residualY = a.meanY - (interceptY +
fittedDriftY_ * a.time);
Owning Cartesian grid of complex scalar samples.
std::complex< float > Value
Two-component complex spinor field used by the QPE Dirac branch.
ComplexField2D & lower() noexcept
Carries out lower.
std::size_t height() const noexcept
Returns height.
ComplexField2D & upper() noexcept
Carries out upper.
bool normalize() noexcept
Returns normalize.
float dx() const noexcept
Returns the X-axis grid spacing.
float dy() const noexcept
Returns the Y-axis grid spacing.
std::size_t width() const noexcept
Returns width.
bool empty() const noexcept
Returns whether the object contains no data.
static bool supportsSize(std::size_t width, std::size_t height) noexcept
Returns whether both grid dimensions are supported by the radix-2 backend.
static void forward(ComplexField2D &field)
Convenience wrapper for an in-place forward transform.
static void inverse(ComplexField2D &field)
Convenience wrapper for an in-place normalized inverse transform.
static double predictedZitterPeriod(const QuantumZitterbewegung2DConfig &config, double hbar) noexcept
Performs predicted zitter period.
static double predictedDriftX(const QuantumZitterbewegung2DConfig &config) noexcept
Performs predicted drift x.
static double predictedDriftY(const QuantumZitterbewegung2DConfig &config) noexcept
Performs predicted drift y.
static bool validateConfig(const QuantumZitterbewegung2DConfig &config, std::string *reason=nullptr) noexcept
Validates config.
static QuantumZitterbewegungSample2D measure(const DiracSpinor2D &spinor, const QuantumZitterbewegung2DConfig &config, double hbar, double time, double initialMeanX, double initialMeanY) noexcept
Performs measure.
static double centralEnergy(const QuantumZitterbewegung2DConfig &config) noexcept
Performs central energy.
static DiracEnergyFractions2D energyFractions(const DiracSpinor2D &spinor, const QuantumZitterbewegung2DConfig &config, double hbar) noexcept
Returns energy fractions.
static bool prepareState(DiracSpinor2D &spinor, const QuantumZitterbewegung2DConfig &config, double hbar, std::string *reason=nullptr)
Prepares state.
static double predictedZitterOmega(const QuantumZitterbewegung2DConfig &config, double hbar) noexcept
Performs predicted zitter omega.
QuantumZitterbewegung2DConfig config_
bool configure(const DiracSpinor2D &spinor, const QuantumZitterbewegung2DConfig &config, double hbar)
Returns configure.
void clear()
Clears stored state and returns the component to its empty/default state.
DiracEnergyFractions2D energyFractions_
std::size_t maximumSamples_
void append(double time, const DiracSpinor2D &spinor)
Carries out append.
void trim()
Carries out trim.
std::vector< QuantumZitterbewegungSample2D > samples_
double maximumAbsResidualX_
double hbar() const noexcept
Carries out hbar.
Declares the two-dimensional FFT utility used by spectral quantum propagators and diagnostics.
void meanPosition(const DiracSpinor2D &s, double &mx, double &my) noexcept
Performs mean position.
Declares the quantumzitterbewegung2d physics/numerical component used by QPE experiments.
Data structure used by the Dirac Energy Fractions2D component.
Configuration values for Quantum Zitterbewegung2D Config.
Data structure used by the Quantum Zitterbewegung Sample2D component.