Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
energyobservables2d.cpp
Go to the documentation of this file.
1/**
2 * @file energyobservables2d.cpp
3 * @brief Implements the energyobservables2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/energyobservables2d.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 "energyobservables2d.h"
13
14#include <cmath>
15#include <stdexcept>
16
17namespace qpe {
18namespace {
19
20std::size_t wrapIndex(long long index, std::size_t count) noexcept
21{
22 const long long n = static_cast<long long>(count);
23 long long wrapped = index % n;
24 if (wrapped < 0) {
25 wrapped += n;
26 }
27 return static_cast<std::size_t>(wrapped);
28}
29
30std::complex<double> sample(const ComplexField2D &field,
31 long long x,
32 long long y) noexcept
33{
34 const ComplexField2D::Value &v = field(
35 wrapIndex(x, field.width()),
36 wrapIndex(y, field.height()));
37 return std::complex<double>(static_cast<double>(v.real()),
38 static_cast<double>(v.imag()));
39}
40
41std::complex<double> derivativeX4(const ComplexField2D &field,
42 std::size_t x,
43 std::size_t y) noexcept
44{
45 const long long xi = static_cast<long long>(x);
46 const long long yi = static_cast<long long>(y);
47 const double h = static_cast<double>(field.dx());
48 return (-sample(field, xi + 2, yi)
49 + 8.0 * sample(field, xi + 1, yi)
50 - 8.0 * sample(field, xi - 1, yi)
51 + sample(field, xi - 2, yi)) / (12.0 * h);
52}
53
54std::complex<double> derivativeY4(const ComplexField2D &field,
55 std::size_t x,
56 std::size_t y) noexcept
57{
58 const long long xi = static_cast<long long>(x);
59 const long long yi = static_cast<long long>(y);
60 const double h = static_cast<double>(field.dy());
61 return (-sample(field, xi, yi + 2)
62 + 8.0 * sample(field, xi, yi + 1)
63 - 8.0 * sample(field, xi, yi - 1)
64 + sample(field, xi, yi - 2)) / (12.0 * h);
65}
66
67} // namespace
68
70 const ComplexField2D &field,
71 const PotentialField2D &potential,
72 double hbar,
73 double mass)
74{
75 if (field.empty()) {
76 throw std::invalid_argument("EnergyObservables2D: field must not be empty");
77 }
78 if (!potential.matchesGrid(field)) {
79 throw std::invalid_argument("EnergyObservables2D: potential grid must match psi");
80 }
81 if (!(hbar > 0.0) || !std::isfinite(hbar)) {
82 throw std::invalid_argument("EnergyObservables2D: hbar must be finite and positive");
83 }
84 if (!(mass > 0.0) || !std::isfinite(mass)) {
85 throw std::invalid_argument("EnergyObservables2D: mass must be finite and positive");
86 }
87 if (field.width() < 5u || field.height() < 5u) {
88 throw std::invalid_argument("EnergyObservables2D: grid must be at least 5x5");
89 }
90
92 const double cellArea = static_cast<double>(field.dx()) *
93 static_cast<double>(field.dy());
94 const double kineticScale = (hbar * hbar) / (2.0 * mass);
95
96 double kineticRaw = 0.0;
97 double potentialRaw = 0.0;
98
99 for (std::size_t y = 0u; y < field.height(); ++y) {
100 for (std::size_t x = 0u; x < field.width(); ++x) {
101 const ComplexField2D::Value &vf = field(x, y);
102 const std::complex<double> psi(static_cast<double>(vf.real()),
103 static_cast<double>(vf.imag()));
104 const double probabilityDensity = std::norm(psi);
105 out.norm2 += probabilityDensity * cellArea;
106
107 const std::complex<double> dxPsi = derivativeX4(field, x, y);
108 const std::complex<double> dyPsi = derivativeY4(field, x, y);
109 kineticRaw += kineticScale *
110 (std::norm(dxPsi) + std::norm(dyPsi)) * cellArea;
111 potentialRaw += static_cast<double>(potential(x, y)) *
112 probabilityDensity * cellArea;
113 }
114 }
115
116 if (!(out.norm2 > 0.0) || !std::isfinite(out.norm2)) {
117 return out;
118 }
119
120 out.kinetic = kineticRaw / out.norm2;
121 out.potential = potentialRaw / out.norm2;
122 out.total = out.kinetic + out.potential;
123 return out;
124}
125
126} // 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 EnergyObservables2DResult compute(const ComplexField2D &field, const PotentialField2D &potential, double hbar, double mass)
Performs compute.
Implements the Potential Field2D component.
bool matchesGrid(const ComplexField2D &field) const noexcept
Performs matches grid.
Declares the energyobservables2d physics/numerical component used by QPE experiments.
std::size_t wrapIndex(long long index, std::size_t count) noexcept
std::complex< double > sample(const ComplexField2D &field, long long x, long long y) noexcept
std::complex< double > derivativeY4(const ComplexField2D &field, std::size_t x, std::size_t y) noexcept
std::complex< double > derivativeX4(const ComplexField2D &field, std::size_t x, std::size_t y) noexcept
Data record containing Energy Observables2D Result.