20std::size_t
wrapIndex(
long long index, std::size_t count)
noexcept
22 const long long n =
static_cast<long long>(count);
23 long long wrapped = index % n;
27 return static_cast<std::size_t
>(wrapped);
37 return std::complex<double>(
static_cast<double>(v.real()),
38 static_cast<double>(v.imag()));
43 std::size_t y)
noexcept
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);
56 std::size_t y)
noexcept
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);
76 throw std::invalid_argument(
"EnergyObservables2D: field must not be empty");
79 throw std::invalid_argument(
"EnergyObservables2D: potential grid must match psi");
81 if (!(hbar > 0.0) || !std::isfinite(hbar)) {
82 throw std::invalid_argument(
"EnergyObservables2D: hbar must be finite and positive");
84 if (!(mass > 0.0) || !std::isfinite(mass)) {
85 throw std::invalid_argument(
"EnergyObservables2D: mass must be finite and positive");
88 throw std::invalid_argument(
"EnergyObservables2D: grid must be at least 5x5");
92 const double cellArea =
static_cast<double>(field.
dx()) *
93 static_cast<double>(field.
dy());
94 const double kineticScale = (hbar * hbar) / (2.0 * mass);
96 double kineticRaw = 0.0;
97 double potentialRaw = 0.0;
99 for (std::size_t y = 0u; y < field.
height(); ++y) {
100 for (std::size_t x = 0u; x < field.
width(); ++x) {
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;
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;
116 if (!(out.
norm2 > 0.0) || !std::isfinite(out.
norm2)) {
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.