23const double kPi = 3.14159265358979323846;
29bool finite(
double v)
noexcept
31 return std::isfinite(v);
39void setReason(std::string *reason,
const char *text)
41 if (reason) *reason = text;
50double coordinate(std::size_t i, std::size_t n,
double spacing)
noexcept
52 if (n == 0u)
return 0.0;
53 return (
static_cast<double>(i) - 0.5 *
static_cast<double>(n - 1u)) * spacing;
62 const double twoPi = 2.0 * kPi;
63 while (phase <= -kPi) phase += twoPi;
64 while (phase > kPi) phase -= twoPi;
75 setReason(reason,
"QuantumVortex2D: configuration values must be finite");
79 setReason(reason,
"QuantumVortex2D: envelope sigma must be positive");
83 setReason(reason,
"QuantumVortex2D: charge must be +1 or -1");
94 setReason(reason,
"QuantumVortex2D: field must not be empty");
100 const double inv4Sigma2 = 1.0 / (4.0 * sigma * sigma);
101 const double dxGrid =
static_cast<double>(field.
dx());
102 const double dyGrid =
static_cast<double>(field.
dy());
104 for (std::size_t y = 0; y < field.
height(); ++y) {
105 const double py = coordinate(y, field.
height(), dyGrid);
107 const double vy = py - config.
coreY;
108 for (std::size_t x = 0; x < field.
width(); ++x) {
109 const double px = coordinate(x, field.
width(), dxGrid);
111 const double vx = px - config.
coreX;
112 const double envelope = std::exp(-(ex * ex + ey * ey) * inv4Sigma2);
114 static_cast<float>(vx * envelope),
115 static_cast<float>(
static_cast<double>(config.
charge) * vy * envelope));
120 setReason(reason,
"QuantumVortex2D: normalization failed");
133 if (!
object.enabled)
continue;
135 if (harmonic !=
nullptr) {
136 setReason(reason,
"QuantumVortex2D: expected exactly one enabled harmonic trap");
141 setReason(reason,
"QuantumVortex2D: vortex guided analysis expects only one harmonic object");
146 setReason(reason,
"QuantumVortex2D: harmonic trap not found");
149 const double kx =
static_cast<double>(harmonic->
curvatureX);
150 const double ky =
static_cast<double>(harmonic->
curvatureY);
151 if (!(kx > 0.0) || !(ky > 0.0) || !finite(kx) || !finite(ky)) {
152 setReason(reason,
"QuantumVortex2D: trap curvature must be positive");
155 const double scale = std::max(1.0, std::max(std::abs(kx), std::abs(ky)));
156 if (std::abs(kx - ky) > 1.0e-6 * scale) {
157 setReason(reason,
"QuantumVortex2D: guided vortex requires an isotropic harmonic trap");
171 if (!trap.
valid || !(trap.
curvature > 0.0) || !(mass > 0.0) || !finite(mass))
return 0.0;
179 return omega > 0.0 ? 2.0 * kPi / omega : 0.0;
190 const double maxRadius = std::max(2.5, 4.0 * config.
envelopeSigma);
191 const double maxRadius2 = maxRadius * maxRadius;
192 double bestDensity = std::numeric_limits<double>::infinity();
193 const int expectedSign = config.
charge >= 0 ? 1 : -1;
195 for (std::size_t y = 0; y + 1u < field.
height(); ++y) {
196 for (std::size_t x = 0; x + 1u < field.
width(); ++x) {
197 const double cx = 0.5 * (coordinate(x, field.
width(), field.
dx()) +
198 coordinate(x + 1u, field.
width(), field.
dx()));
199 const double cy = 0.5 * (coordinate(y, field.
height(), field.
dy()) +
200 coordinate(y + 1u, field.
height(), field.
dy()));
201 const double tx = cx - trap.
centerX;
202 const double ty = cy - trap.
centerY;
203 if (tx * tx + ty * ty > maxRadius2)
continue;
209 const double windingPhase =
210 wrapPhase(std::arg(p10) - std::arg(p00)) +
211 wrapPhase(std::arg(p11) - std::arg(p10)) +
212 wrapPhase(std::arg(p01) - std::arg(p11)) +
213 wrapPhase(std::arg(p00) - std::arg(p01));
214 const int winding =
static_cast<int>(std::lround(windingPhase / (2.0 * kPi)));
215 if (winding != expectedSign)
continue;
217 const double density = 0.25 * (
218 static_cast<double>(std::norm(p00)) +
static_cast<double>(std::norm(p10)) +
219 static_cast<double>(std::norm(p11)) +
static_cast<double>(std::norm(p01)));
220 if (density < bestDensity) {
221 bestDensity = density;
238 if (field.
width() < 3u || field.
height() < 3u || !(hbar > 0.0) || !finite(hbar))
return 0.0;
239 const double area =
static_cast<double>(field.
dx()) *
static_cast<double>(field.
dy());
240 const double inv2Dx = 1.0 / (2.0 *
static_cast<double>(field.
dx()));
241 const double inv2Dy = 1.0 / (2.0 *
static_cast<double>(field.
dy()));
245 for (std::size_t y = 1u; y + 1u < field.
height(); ++y) {
246 const double py = coordinate(y, field.
height(), field.
dy()) - originY;
247 for (std::size_t x = 1u; x + 1u < field.
width(); ++x) {
248 const double px = coordinate(x, field.
width(), field.
dx()) - originX;
249 const std::complex<double> psi(field(x, y).real(), field(x, y).imag());
250 const std::complex<double> dPsiDx =
251 (std::complex<double>(field(x + 1u, y).real(), field(x + 1u, y).imag()) -
252 std::complex<double>(field(x - 1u, y).real(), field(x - 1u, y).imag())) * inv2Dx;
253 const std::complex<double> dPsiDy =
254 (std::complex<double>(field(x, y + 1u).real(), field(x, y + 1u).imag()) -
255 std::complex<double>(field(x, y - 1u).real(), field(x, y - 1u).imag())) * inv2Dy;
256 const std::complex<double> lzPsi = std::complex<double>(0.0, -hbar) *
257 (px * dPsiDy - py * dPsiDx);
258 value += std::real(std::conj(psi) * lzPsi) * area;
259 norm += std::norm(psi) * area;
262 return norm > 1.0e-30 ? value / norm : 0.0;
275 if (!(omega > 0.0) || !finite(time))
return;
278 const double theta =
static_cast<double>(config.
charge) * omega * time;
279 const double c = std::cos(theta);
280 const double s = std::sin(theta);
281 x = trap.
centerX + dx * c - dy * s;
282 y = trap.
centerY + dx * s + dy * c;
286 : maxSamples_(std::max<std::size_t>(32u, maxSamples))
321 sample.norm = field.
norm2();
Owning Cartesian grid of complex scalar samples.
double norm2() const noexcept
Returns the discrete integral of |psi|^2 including dx*dy.
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.
bool normalize() noexcept
Scales the field to unit discrete probability norm.
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.
Implements the Potential Scene2D component.
const std::vector< PotentialObject2D > & objects() const noexcept
Performs objects.
static double angularMomentumZ(const ComplexField2D &field, double hbar, double originX=0.0, double originY=0.0)
Performs angular momentum z.
static void idealCorePosition(double time, const QuantumVortexInitialState2DConfig &config, const QuantumVortexTrap2D &trap, double mass, double &x, double &y)
Returns ideal core position.
static bool validateConfig(const QuantumVortexInitialState2DConfig &config, std::string *reason=nullptr)
Validates config.
static bool identifyTrap(const PotentialScene2D &scene, QuantumVortexTrap2D &trap, std::string *reason=nullptr)
Returns identify trap.
static QuantumVortexCore2D detectCore(const ComplexField2D &field, const QuantumVortexInitialState2DConfig &config, const QuantumVortexTrap2D &trap)
Performs detect core.
static double trapAngularFrequency(const QuantumVortexTrap2D &trap, double mass)
Performs trap angular frequency.
static double trapPeriod(const QuantumVortexTrap2D &trap, double mass)
Performs trap period.
static bool initialize(ComplexField2D &field, const QuantumVortexInitialState2DConfig &config, std::string *reason=nullptr)
Initializes ialize.
QuantumVortexTrap2D trap_
void configure(const QuantumVortexInitialState2DConfig &config, const QuantumVortexTrap2D &trap)
Returns configure.
void clear()
Performs clear.
std::vector< QuantumVortexSample2D > samples_
const QuantumVortexTrap2D & trap() const noexcept
Performs trap.
void trim()
Performs trim.
QuantumVortexHistory2D(std::size_t maxSamples=1400u)
Performs quantum vortex history2 d.
bool configured() const noexcept
Returns configured.
const std::vector< QuantumVortexSample2D > & samples() const noexcept
Samples s.
bool empty() const noexcept
Performs empty.
QuantumVortexInitialState2DConfig config_
const QuantumVortexInitialState2DConfig & config() const noexcept
Returns config.
void append(double time, const ComplexField2D &field, double hbar)
Performs append.
double wrapPhase(double phase) noexcept
Performs wrap phase.
Declares the quantumvortex2d physics/numerical component used by QPE experiments.
Data structure used by the Potential Object2D component.
Data structure used by the Quantum Vortex Core2D component.
Configuration values for Quantum Vortex Initial State2D Config.
Data structure used by the Quantum Vortex Sample2D component.