51 std::string *reason)
noexcept
54 if (reason) reason->clear();
56 std::vector<const PotentialObject2D *> wells;
60 if (!
object.enabled)
continue;
63 wells.push_back(&
object);
68 if (linear !=
nullptr) {
69 fail(reason,
"Bloch lattice expects exactly one enabled Linear force term.");
76 fail(reason,
"Bloch lattice accepts only negative Rectangle wells plus one Linear X term.");
80 if (wells.size() < 5u) {
81 fail(reason,
"Bloch lattice needs at least five enabled periodic wells.");
84 if (linear ==
nullptr || std::fabs(
static_cast<double>(linear->
slopeX)) <= 1.0e-12 ||
85 std::fabs(
static_cast<double>(linear->
slopeY)) > 1.0e-9) {
86 fail(reason,
"Bloch lattice needs one non-zero Linear slopeX and zero slopeY.");
90 std::sort(wells.begin(), wells.end(),
92 return a->centerX < b->centerX;
96 const double spacing =
static_cast<double>(wells[1]->centerX - wells[0]->centerX);
97 if (!(spacing > 0.0) || !std::isfinite(spacing)) {
98 fail(reason,
"Bloch lattice spacing is invalid.");
102 for (std::size_t i = 0u; i < wells.size(); ++i) {
108 fail(reason,
"Bloch lattice wells must have equal depth, width, height and centerY.");
112 const double localSpacing =
static_cast<double>(wells[i]->centerX - wells[i - 1u]->centerX);
113 if (!near(localSpacing, spacing, spacing)) {
114 fail(reason,
"Bloch lattice wells are not equally spaced.");
120 geometry.valid =
true;
121 geometry.spacing = spacing;
122 geometry.wellWidth = first.
width;
123 geometry.wellDepth = -
static_cast<double>(first.
value);
124 geometry.slopeX = linear->
slopeX;
125 geometry.forceX = -
static_cast<double>(linear->
slopeX);
126 geometry.firstCenterX = wells.front()->centerX;
127 geometry.lastCenterX = wells.back()->centerX;
128 geometry.wellCount =
static_cast<int>(wells.size());
155 double initialMomentum,
157 double hbar)
noexcept
159 const double edge = zoneEdgeMomentum(geometry, hbar);
160 if (!(edge > 0.0) || !std::isfinite(initialMomentum) || !std::isfinite(time)) {
165 const double raw = initialMomentum + geometry.forceX * time;
166 const double width = 2.0 * edge;
167 double wrapped = std::fmod(raw + edge, width);
168 if (wrapped < 0.0) wrapped += width;
169 return wrapped - edge;