Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
probabilitycurrent2d.cpp
Go to the documentation of this file.
1/**
2 * @file probabilitycurrent2d.cpp
3 * @brief Implements the probabilitycurrent2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/probabilitycurrent2d.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
14#include <algorithm>
15#include <cmath>
16#include <complex>
17#include <stdexcept>
18
19namespace qpe {
20
22{
23 const std::size_t count = width * height;
24 return width == 0 || height == 0 ||
25 jx.size() != count || jy.size() != count;
26}
27
29 float hbar,
30 float mass)
31{
32 if (field.empty()) {
34 }
35 if (!(hbar > 0.0f) || !std::isfinite(static_cast<double>(hbar))) {
36 throw std::invalid_argument("ProbabilityCurrent2D: hbar must be finite and positive");
37 }
38 if (!(mass > 0.0f) || !std::isfinite(static_cast<double>(mass))) {
39 throw std::invalid_argument("ProbabilityCurrent2D: mass must be finite and positive");
40 }
41
43 out.width = field.width();
44 out.height = field.height();
45 out.dx = field.dx();
46 out.dy = field.dy();
47 out.jx.assign(field.size(), 0.0f);
48 out.jy.assign(field.size(), 0.0f);
49
50 if (field.width() < 3 || field.height() < 3) {
51 return out;
52 }
53
54 const double inv2Dx = 1.0 / (2.0 * static_cast<double>(field.dx()));
55 const double inv2Dy = 1.0 / (2.0 * static_cast<double>(field.dy()));
56 const double prefactor = static_cast<double>(hbar) / static_cast<double>(mass);
57 const double cellArea = static_cast<double>(field.dx()) * static_cast<double>(field.dy());
58
59 for (std::size_t y = 1; y + 1 < field.height(); ++y) {
60 for (std::size_t x = 1; x + 1 < field.width(); ++x) {
61 const std::complex<double> psi(
62 static_cast<double>(field(x, y).real()),
63 static_cast<double>(field(x, y).imag()));
64
65 const std::complex<double> psiXp(
66 static_cast<double>(field(x + 1, y).real()),
67 static_cast<double>(field(x + 1, y).imag()));
68 const std::complex<double> psiXm(
69 static_cast<double>(field(x - 1, y).real()),
70 static_cast<double>(field(x - 1, y).imag()));
71 const std::complex<double> psiYp(
72 static_cast<double>(field(x, y + 1).real()),
73 static_cast<double>(field(x, y + 1).imag()));
74 const std::complex<double> psiYm(
75 static_cast<double>(field(x, y - 1).real()),
76 static_cast<double>(field(x, y - 1).imag()));
77
78 const std::complex<double> dPsiDx = (psiXp - psiXm) * inv2Dx;
79 const std::complex<double> dPsiDy = (psiYp - psiYm) * inv2Dy;
80
81 const double currentX = prefactor * std::imag(std::conj(psi) * dPsiDx);
82 const double currentY = prefactor * std::imag(std::conj(psi) * dPsiDy);
83 const std::size_t index = y * field.width() + x;
84
85 out.jx[index] = static_cast<float>(currentX);
86 out.jy[index] = static_cast<float>(currentY);
87
88 const float magnitude = static_cast<float>(std::sqrt(currentX * currentX + currentY * currentY));
89 out.maxMagnitude = std::max(out.maxMagnitude, magnitude);
90 out.integratedX += currentX * cellArea;
91 out.integratedY += currentY * cellArea;
92 }
93 }
94
95 return out;
96}
97
99 const U1GaugeField2D &gauge,
100 float hbar,
101 float mass)
102{
103 if (field.empty()) return ProbabilityCurrentField2D();
104 if (!(hbar > 0.0f) || !std::isfinite(static_cast<double>(hbar)))
105 throw std::invalid_argument("ProbabilityCurrent2D: hbar must be finite and positive");
106 if (!(mass > 0.0f) || !std::isfinite(static_cast<double>(mass)))
107 throw std::invalid_argument("ProbabilityCurrent2D: mass must be finite and positive");
108 if (!gauge.matchesGrid(field))
109 throw std::invalid_argument("ProbabilityCurrent2D: gauge field does not match wavefunction grid");
110
112 out.width = field.width(); out.height = field.height();
113 out.dx = field.dx(); out.dy = field.dy();
114 out.jx.assign(field.size(), 0.0f); out.jy.assign(field.size(), 0.0f);
115 if (field.width() < 3 || field.height() < 3) return out;
116
117 const double inv2Dx = 1.0 / (2.0 * static_cast<double>(field.dx()));
118 const double inv2Dy = 1.0 / (2.0 * static_cast<double>(field.dy()));
119 const double prefactor = static_cast<double>(hbar) / static_cast<double>(mass);
120 const double cellArea = static_cast<double>(field.dx()) * static_cast<double>(field.dy());
121
122 for (std::size_t y = 1; y + 1 < field.height(); ++y) {
123 for (std::size_t x = 1; x + 1 < field.width(); ++x) {
124 const std::complex<double> psi(field(x, y));
125 const std::complex<double> right = std::complex<double>(gauge.linkX(x, y)) *
126 std::complex<double>(field(x + 1u, y));
127 const std::complex<double> left = std::conj(std::complex<double>(gauge.linkX(x - 1u, y))) *
128 std::complex<double>(field(x - 1u, y));
129 const std::complex<double> up = std::complex<double>(gauge.linkY(x, y)) *
130 std::complex<double>(field(x, y + 1u));
131 const std::complex<double> down = std::conj(std::complex<double>(gauge.linkY(x, y - 1u))) *
132 std::complex<double>(field(x, y - 1u));
133 const std::complex<double> dPsiDx = (right - left) * inv2Dx;
134 const std::complex<double> dPsiDy = (up - down) * inv2Dy;
135 const double currentX = prefactor * std::imag(std::conj(psi) * dPsiDx);
136 const double currentY = prefactor * std::imag(std::conj(psi) * dPsiDy);
137 const std::size_t index = y * field.width() + x;
138 out.jx[index] = static_cast<float>(currentX);
139 out.jy[index] = static_cast<float>(currentY);
140 out.maxMagnitude = std::max(out.maxMagnitude,
141 static_cast<float>(std::sqrt(currentX * currentX + currentY * currentY)));
142 out.integratedX += currentX * cellArea;
143 out.integratedY += currentY * cellArea;
144 }
145 }
146 return out;
147}
148
149} // namespace qpe
Owning Cartesian grid of complex scalar samples.
std::size_t size() const noexcept
Returns the total number of complex 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.
static ProbabilityCurrentField2D compute(const ComplexField2D &field, float hbar, float mass)
Performs compute.
static ProbabilityCurrentField2D computeGaugeCovariant(const ComplexField2D &field, const U1GaugeField2D &gauge, float hbar, float mass)
Computes gauge covariant.
Discrete U(1) gauge connection for gauge-covariant Schrodinger experiments.
Link linkY(std::size_t x, std::size_t y) const noexcept
Returns the +Y parallel-transport link at a site.
bool matchesGrid(const ComplexField2D &field) const noexcept
Tests whether dimensions and spacing match a scalar field.
Link linkX(std::size_t x, std::size_t y) const noexcept
Returns the +X parallel-transport link at a site.
Declares the probabilitycurrent2d physics/numerical component used by QPE experiments.
Sampled two-dimensional probability-current field and integrated diagnostics.
bool empty() const noexcept
Performs empty.