Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
ramseyinterferometry2d.cpp
Go to the documentation of this file.
1/**
2 * @file ramseyinterferometry2d.cpp
3 * @brief Implements the ramseyinterferometry2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/ramseyinterferometry2d.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
16
17#include <algorithm>
18#include <cmath>
19#include <complex>
20#include <limits>
21#include <vector>
22
23namespace qpe {
24namespace {
25
26const double Pi = 3.1415926535897932384626433832795;
27const double EnsembleSpanSigma = 3.0;
28
29struct State2
30{
31 std::complex<double> a;
32 std::complex<double> b;
33};
34
36{
37 std::complex<double> aa;
38 std::complex<double> ab;
39 std::complex<double> ba;
40 std::complex<double> bb;
41};
42
44{
45 double energyA = 0.0;
46 double energyB = 0.0;
47 double coupling = 0.0;
48 double weight = 1.0;
52};
53
54/**
55 * @brief Performs finite.
56 * @param v The v value used by the operation.
57 */
58bool finite(double v) noexcept
59{
60 return std::isfinite(v);
61}
62
63/**
64 * @brief Performs valid pair.
65 * @param spectrum The spectrum value used by the operation.
66 * @param a The a value used by the operation.
67 * @param b The b value used by the operation.
68 */
69bool validPair(const BoundStateSpectrum1D &spectrum, int a, int b) noexcept
70{
71 return spectrum.valid && a >= 0 && b >= 0 && a != b &&
72 a < static_cast<int>(spectrum.states.size()) &&
73 b < static_cast<int>(spectrum.states.size());
74}
75
76/**
77 * @brief Performs propagate constant.
78 * @param state The state value used by the operation.
79 * @param energyA The energy a value used by the operation.
80 * @param energyB The energy b value used by the operation.
81 * @param coupling The coupling value used by the operation.
82 * @param driveValue The drive value value used by the operation.
83 * @param dt Time increment in solver units.
84 * @param hbar The hbar value used by the operation.
85 */
87 double energyA,
88 double energyB,
89 double coupling,
90 double driveValue,
91 double dt,
92 double hbar) noexcept
93{
94 const double mean = 0.5 * (energyA + energyB);
95 const double hz = 0.5 * (energyA - energyB);
96 const double hx = coupling * driveValue;
97 const double radius = std::sqrt(hx * hx + hz * hz);
98 const std::complex<double> global =
99 std::exp(std::complex<double>(0.0, -mean * dt / hbar));
100 if (!(radius > 1.0e-15)) {
101 State2 out;
102 out.a = global * state.a;
103 out.b = global * state.b;
104 return out;
105 }
106 const double angle = radius * dt / hbar;
107 const double c = std::cos(angle);
108 const double s = std::sin(angle) / radius;
109 const std::complex<double> minusI(0.0, -1.0);
110 State2 out;
111 out.a = global * (c * state.a + minusI * s * (hz * state.a + hx * state.b));
112 out.b = global * (c * state.b + minusI * s * (hx * state.a - hz * state.b));
113 return out;
114}
115
116/**
117 * @brief Applies operator.
118 * @param op The op value used by the operation.
119 * @param state The state value used by the operation.
120 */
121State2 applyOperator(const Operator2 &op, const State2 &state) noexcept
122{
123 State2 out;
124 out.a = op.aa * state.a + op.ab * state.b;
125 out.b = op.ba * state.a + op.bb * state.b;
126 return out;
127}
128
129/**
130 * @brief Performs pulse operator.
131 * @param energyA The energy a value used by the operation.
132 * @param energyB The energy b value used by the operation.
133 * @param coupling The coupling value used by the operation.
134 * @param sequence The sequence value used by the operation.
135 * @param duration The duration value used by the operation.
136 * @param phaseDegrees The phase degrees value used by the operation.
137 * @param hbar The hbar value used by the operation.
138 * @param maximumStep The maximum step value used by the operation.
139 */
141 double energyB,
142 double coupling,
143 const RamseyPulseSequence2DConfig &sequence,
144 double duration,
145 double phaseDegrees,
146 double hbar,
147 double maximumStep) noexcept
148{
149 Operator2 op;
150 State2 columnA;
151 columnA.a = std::complex<double>(1.0, 0.0);
152 columnA.b = std::complex<double>(0.0, 0.0);
153 State2 columnB;
154 columnB.a = std::complex<double>(0.0, 0.0);
155 columnB.b = std::complex<double>(1.0, 0.0);
156
157 if (!(duration > 0.0)) {
158 op.aa = columnA.a;
159 op.ba = columnA.b;
160 op.ab = columnB.a;
161 op.bb = columnB.b;
162 return op;
163 }
164
165 const int steps = std::max(80, static_cast<int>(std::ceil(duration / maximumStep)));
166 const double dt = duration / static_cast<double>(steps);
167 const double phase0 = phaseDegrees * Pi / 180.0;
168 for (int i = 0; i < steps; ++i) {
169 const double localTime = (static_cast<double>(i) + 0.5) * dt;
170 const double phase = sequence.angularFrequency * localTime + phase0;
171 const double drive = sequence.meanValue + sequence.amplitude * std::sin(phase);
172 columnA = propagateConstant(columnA, energyA, energyB, coupling, drive, dt, hbar);
173 columnB = propagateConstant(columnB, energyA, energyB, coupling, drive, dt, hbar);
174 }
175
176 op.aa = columnA.a;
177 op.ba = columnA.b;
178 op.ab = columnB.a;
179 op.bb = columnB.b;
180 return op;
181}
182
183/**
184 * @brief Prepares member.
185 * @param spectrum The spectrum value used by the operation.
186 * @param sequence The sequence value used by the operation.
187 * @param stateA The state a value used by the operation.
188 * @param stateB The state b value used by the operation.
189 * @param hbar The hbar value used by the operation.
190 * @param transitionOffset The transition offset value used by the operation.
191 * @param weight The weight value used by the operation.
192 * @param maximumPulseStep The maximum pulse step value used by the operation.
193 */
195 const RamseyPulseSequence2DConfig &sequence,
196 int stateA,
197 int stateB,
198 double hbar,
199 double transitionOffset,
200 double weight,
201 double maximumPulseStep) noexcept
202{
203 PreparedMember member;
204 const double baseA = spectrum.states[static_cast<std::size_t>(stateA)].energy;
205 const double baseB = spectrum.states[static_cast<std::size_t>(stateB)].energy;
206 // Keep the mean energy fixed and shift only the transition frequency.
207 const double halfShift = 0.5 * hbar * transitionOffset;
208 member.energyA = baseA - halfShift;
209 member.energyB = baseB + halfShift;
210 member.coupling = DrivenTwoLevel2D::matrixElementX(spectrum, stateA, stateB);
211 member.weight = weight;
212 member.firstPulse = pulseOperator(member.energyA, member.energyB, member.coupling,
213 sequence, sequence.pulseDuration,
214 sequence.firstPulsePhaseDegrees,
215 hbar, maximumPulseStep);
216 member.secondPulse = pulseOperator(member.energyA, member.energyB, member.coupling,
217 sequence, sequence.pulseDuration,
218 sequence.secondPulsePhaseDegrees,
219 hbar, maximumPulseStep);
220 if (sequence.echoEnabled) {
221 member.echoPulse = pulseOperator(member.energyA, member.energyB, member.coupling,
222 sequence, sequence.echoPulseDuration,
223 sequence.echoPulsePhaseDegrees,
224 hbar, maximumPulseStep);
225 }
226 return member;
227}
228
229/**
230 * @brief Performs predict prepared.
231 * @param member The member value used by the operation.
232 * @param sequence The sequence value used by the operation.
233 * @param freeEvolutionDuration The free evolution duration value used by the operation.
234 * @param hbar The hbar value used by the operation.
235 * @param populationA The population a value used by the operation.
236 * @param populationB The population b value used by the operation.
237 */
239 const RamseyPulseSequence2DConfig &sequence,
240 double freeEvolutionDuration,
241 double hbar,
242 double &populationA,
243 double &populationB) noexcept
244{
245 State2 state;
246 state.a = std::complex<double>(1.0, 0.0);
247 state.b = std::complex<double>(0.0, 0.0);
248 state = applyOperator(member.firstPulse, state);
249
250 if (sequence.echoEnabled) {
251 const double halfFree = 0.5 * freeEvolutionDuration;
252 if (halfFree > 0.0) {
253 state = propagateConstant(state, member.energyA, member.energyB,
254 member.coupling, sequence.meanValue,
255 halfFree, hbar);
256 }
257 state = applyOperator(member.echoPulse, state);
258 if (halfFree > 0.0) {
259 state = propagateConstant(state, member.energyA, member.energyB,
260 member.coupling, sequence.meanValue,
261 halfFree, hbar);
262 }
263 } else if (freeEvolutionDuration > 0.0) {
264 state = propagateConstant(state, member.energyA, member.energyB,
265 member.coupling, sequence.meanValue,
266 freeEvolutionDuration, hbar);
267 }
268
269 state = applyOperator(member.secondPulse, state);
270 populationA = std::norm(state.a);
271 populationB = std::norm(state.b);
272}
273
274/**
275 * @brief Performs sequence valid for reference.
276 * @param sequence The sequence value used by the operation.
277 */
279{
280 return sequence.enabled &&
281 sequence.parameter == TimePotentialParameter2D::LinearSlopeX &&
282 finite(sequence.meanValue) && finite(sequence.amplitude) &&
283 finite(sequence.angularFrequency) && sequence.angularFrequency > 0.0 &&
284 finite(sequence.pulseDuration) && sequence.pulseDuration > 0.0 &&
285 finite(sequence.freeEvolutionDuration) && sequence.freeEvolutionDuration >= 0.0 &&
286 finite(sequence.echoPulseDuration) &&
287 (!sequence.echoEnabled || sequence.echoPulseDuration > 0.0) &&
288 finite(sequence.firstPulsePhaseDegrees) &&
289 finite(sequence.secondPulsePhaseDegrees) &&
290 finite(sequence.echoPulsePhaseDegrees);
291}
292
293std::vector<PreparedMember> prepareEnsemble(const BoundStateSpectrum1D &spectrum,
294 const RamseyPulseSequence2DConfig &sequence,
295 const RamseyEnsemble2DConfig &ensemble,
296 int stateA,
297 int stateB,
298 double hbar)
299{
300 std::vector<PreparedMember> members;
301 if (!validPair(spectrum, stateA, stateB) || !sequenceValidForReference(sequence) ||
302 !RamseyInterferometry2D::validateEnsembleConfig(ensemble) || !(hbar > 0.0)) {
303 return members;
304 }
305
306 const int count = ensemble.detuningStdDev > 0.0 ? ensemble.memberCount : 1;
307 members.reserve(static_cast<std::size_t>(count));
308 double totalWeight = 0.0;
309 for (int i = 0; i < count; ++i) {
310 double z = 0.0;
311 if (count > 1) {
313 static_cast<double>(i) / static_cast<double>(count - 1);
314 }
315 const double weight = std::exp(-0.5 * z * z);
316 const double offset = z * ensemble.detuningStdDev;
317 // Step 28 is diagnostic only. A 0.01 pulse step is sufficiently fine
318 // for the ensemble overlay and makes interactive scans inexpensive.
319 members.push_back(prepareMember(spectrum, sequence, stateA, stateB, hbar,
320 offset, weight, 0.01));
321 totalWeight += weight;
322 }
323 if (!(totalWeight > 0.0)) {
324 members.clear();
325 return members;
326 }
327 for (PreparedMember &member : members) {
328 member.weight /= totalWeight;
329 }
330 return members;
331}
332
333/**
334 * @brief Performs average prepared.
335 * @param members The members value used by the operation.
336 * @param sequence The sequence value used by the operation.
337 * @param freeEvolutionDuration The free evolution duration value used by the operation.
338 * @param hbar The hbar value used by the operation.
339 * @param meanA The mean a value used by the operation.
340 * @param meanB The mean b value used by the operation.
341 * @param minimumB The minimum b value used by the operation.
342 * @param maximumB The maximum b value used by the operation.
343 */
344void averagePrepared(const std::vector<PreparedMember> &members,
345 const RamseyPulseSequence2DConfig &sequence,
346 double freeEvolutionDuration,
347 double hbar,
348 double &meanA,
349 double &meanB,
350 double *minimumB,
351 double *maximumB) noexcept
352{
353 meanA = 0.0;
354 meanB = 0.0;
355 double minB = std::numeric_limits<double>::infinity();
356 double maxB = -std::numeric_limits<double>::infinity();
357 for (const PreparedMember &member : members) {
358 double a = 0.0;
359 double b = 0.0;
360 predictPrepared(member, sequence, freeEvolutionDuration, hbar, a, b);
361 meanA += member.weight * a;
362 meanB += member.weight * b;
363 minB = std::min(minB, b);
364 maxB = std::max(maxB, b);
365 }
366 if (minimumB) *minimumB = std::isfinite(minB) ? minB : 0.0;
367 if (maximumB) *maximumB = std::isfinite(maxB) ? maxB : 0.0;
368}
369
370} // namespace
371
373 const BoundStateSpectrum1D &spectrum,
374 const RamseyPulseSequence2DConfig &sequence,
375 int stateA,
376 int stateB,
377 double hbar) noexcept
378{
379 RamseyTheory2D result;
380 if (!validPair(spectrum, stateA, stateB) || !(hbar > 0.0) ||
381 !sequenceValidForReference(sequence)) {
382 return result;
383 }
384
385 result.stateA = stateA;
386 result.stateB = stateB;
387 result.energyA = spectrum.states[static_cast<std::size_t>(stateA)].energy;
388 result.energyB = spectrum.states[static_cast<std::size_t>(stateB)].energy;
389 result.transitionAngularFrequency = std::abs(result.energyB - result.energyA) / hbar;
390 result.driveAngularFrequency = sequence.angularFrequency;
391 result.detuning = sequence.angularFrequency - result.transitionAngularFrequency;
392 result.couplingMatrixElementX = DrivenTwoLevel2D::matrixElementX(spectrum, stateA, stateB);
394 std::abs(sequence.amplitude * result.couplingMatrixElementX) / hbar;
395 if (result.resonantRabiAngularFrequency > 0.0) {
396 result.idealPiOver2Duration = Pi / (2.0 * result.resonantRabiAngularFrequency);
397 }
398 result.firstPulseEnd = sequence.startTime + sequence.pulseDuration;
399 result.echoEnabled = sequence.echoEnabled;
400 if (sequence.echoEnabled) {
401 result.echoPulseStart = result.firstPulseEnd + 0.5 * sequence.freeEvolutionDuration;
402 result.echoPulseEnd = result.echoPulseStart + sequence.echoPulseDuration;
403 result.secondPulseStart = result.echoPulseEnd + 0.5 * sequence.freeEvolutionDuration;
404 } else {
405 result.echoPulseStart = 0.0;
406 result.echoPulseEnd = 0.0;
407 result.secondPulseStart = result.firstPulseEnd + sequence.freeEvolutionDuration;
408 }
409 result.secondPulseEnd = result.secondPulseStart + sequence.pulseDuration;
410 result.freePhaseRadians = result.transitionAngularFrequency * sequence.freeEvolutionDuration;
411
412 const PreparedMember nominal = prepareMember(spectrum, sequence, stateA, stateB,
413 hbar, 0.0, 1.0, 0.0025);
414 predictPrepared(nominal, sequence, sequence.freeEvolutionDuration, hbar,
417 result.valid = true;
418 return result;
419}
420
422 const ComplexField2D &field,
423 const BoundStateSpectrum1D &spectrum,
424 int stateA,
425 int stateB)
426{
427 return DrivenTwoLevel2D::project(field, spectrum, stateA, stateB);
428}
429
430std::vector<RamseyFringeSample2D> RamseyInterferometry2D::scanFreeEvolution(
431 const BoundStateSpectrum1D &spectrum,
432 const RamseyPulseSequence2DConfig &sequence,
433 double minimumFreeTime,
434 double maximumFreeTime,
435 int sampleCount,
436 int stateA,
437 int stateB,
438 double hbar)
439{
440 std::vector<RamseyFringeSample2D> result;
441 if (!validPair(spectrum, stateA, stateB) || !sequenceValidForReference(sequence) ||
442 !finite(minimumFreeTime) || !finite(maximumFreeTime) ||
443 minimumFreeTime < 0.0 || !(maximumFreeTime > minimumFreeTime) ||
444 sampleCount < 2 || !(hbar > 0.0)) {
445 return result;
446 }
447
448 const double omega12 = std::abs(
449 spectrum.states[static_cast<std::size_t>(stateB)].energy -
450 spectrum.states[static_cast<std::size_t>(stateA)].energy) / hbar;
451 const PreparedMember nominal = prepareMember(spectrum, sequence, stateA, stateB,
452 hbar, 0.0, 1.0, 0.0025);
453 result.reserve(static_cast<std::size_t>(sampleCount));
454 for (int i = 0; i < sampleCount; ++i) {
455 const double u = static_cast<double>(i) / static_cast<double>(sampleCount - 1);
457 sample.freeEvolutionDuration =
458 minimumFreeTime + (maximumFreeTime - minimumFreeTime) * u;
459 sample.phaseRadians = omega12 * sample.freeEvolutionDuration;
460 predictPrepared(nominal, sequence, sample.freeEvolutionDuration, hbar,
461 sample.populationA, sample.populationB);
462 result.push_back(sample);
463 }
464 return result;
465}
466
468 const RamseyEnsemble2DConfig &config) noexcept
469{
470 return finite(config.detuningStdDev) && config.detuningStdDev >= 0.0 &&
471 config.detuningStdDev <= 10.0 && config.memberCount >= 3 &&
472 config.memberCount <= 201;
473}
474
476 const BoundStateSpectrum1D &spectrum,
477 const RamseyPulseSequence2DConfig &sequence,
478 const RamseyEnsemble2DConfig &ensemble,
479 int stateA,
480 int stateB,
481 double hbar)
482{
484 if (!ensemble.enabled || !validateEnsembleConfig(ensemble) ||
485 !validPair(spectrum, stateA, stateB) || !sequenceValidForReference(sequence) ||
486 !(hbar > 0.0)) {
487 return result;
488 }
489
490 const std::vector<PreparedMember> members =
491 prepareEnsemble(spectrum, sequence, ensemble, stateA, stateB, hbar);
492 if (members.empty()) return result;
493
494 averagePrepared(members, sequence, sequence.freeEvolutionDuration, hbar,
495 result.meanPopulationA, result.meanPopulationB,
496 &result.minimumPopulationB, &result.maximumPopulationB);
497 result.valid = true;
498 result.memberCount = ensemble.detuningStdDev > 0.0
499 ? static_cast<int>(members.size()) : 1;
500 result.detuningStdDev = ensemble.detuningStdDev;
501 return result;
502}
503
504std::vector<RamseyEnsembleFringeSample2D>
506 const BoundStateSpectrum1D &spectrum,
507 const RamseyPulseSequence2DConfig &sequence,
508 const RamseyEnsemble2DConfig &ensemble,
509 double minimumFreeTime,
510 double maximumFreeTime,
511 int sampleCount,
512 int stateA,
513 int stateB,
514 double hbar)
515{
516 std::vector<RamseyEnsembleFringeSample2D> result;
517 if (!ensemble.enabled || !validateEnsembleConfig(ensemble) ||
518 !validPair(spectrum, stateA, stateB) || !sequenceValidForReference(sequence) ||
519 !finite(minimumFreeTime) || !finite(maximumFreeTime) ||
520 minimumFreeTime < 0.0 || !(maximumFreeTime > minimumFreeTime) ||
521 sampleCount < 2 || !(hbar > 0.0)) {
522 return result;
523 }
524
525 const std::vector<PreparedMember> members =
526 prepareEnsemble(spectrum, sequence, ensemble, stateA, stateB, hbar);
527 if (members.empty()) return result;
528
529 result.reserve(static_cast<std::size_t>(sampleCount));
530 for (int i = 0; i < sampleCount; ++i) {
531 const double u = static_cast<double>(i) / static_cast<double>(sampleCount - 1);
533 sample.freeEvolutionDuration =
534 minimumFreeTime + (maximumFreeTime - minimumFreeTime) * u;
535 averagePrepared(members, sequence, sample.freeEvolutionDuration, hbar,
536 sample.meanPopulationA, sample.meanPopulationB,
537 nullptr, nullptr);
538 result.push_back(sample);
539 }
540 return result;
541}
542
543} // namespace qpe
Declares the boundstates1d physics/numerical component used by QPE experiments.
Owning Cartesian grid of complex scalar samples.
static double matrixElementX(const BoundStateSpectrum1D &spectrum, int stateA=0, int stateB=1) noexcept
Performs matrix element x.
static DrivenTwoLevelProjection2D project(const ComplexField2D &field, const BoundStateSpectrum1D &spectrum, int stateA=0, int stateB=1)
Performs project.
static DrivenTwoLevelProjection2D project(const ComplexField2D &field, const BoundStateSpectrum1D &spectrum, int stateA=0, int stateB=1)
Performs project.
static bool validateEnsembleConfig(const RamseyEnsemble2DConfig &config) noexcept
Validates ensemble config.
static RamseyTheory2D analyze(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, int stateA=0, int stateB=1, double hbar=1.0) noexcept
Performs analyze.
static RamseyEnsembleResult2D analyzeStaticDetuningEnsemble(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, const RamseyEnsemble2DConfig &ensemble, int stateA=0, int stateB=1, double hbar=1.0)
Performs analyze static detuning ensemble.
static std::vector< RamseyFringeSample2D > scanFreeEvolution(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, double minimumFreeTime, double maximumFreeTime, int sampleCount, int stateA=0, int stateB=1, double hbar=1.0)
Performs scan free evolution.
static std::vector< RamseyEnsembleFringeSample2D > scanFreeEvolutionEnsemble(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, const RamseyEnsemble2DConfig &ensemble, double minimumFreeTime, double maximumFreeTime, int sampleCount, int stateA=0, int stateB=1, double hbar=1.0)
Performs scan free evolution ensemble.
State2 applyOperator(const Operator2 &op, const State2 &state) noexcept
Applies operator.
bool sequenceValidForReference(const RamseyPulseSequence2DConfig &sequence) noexcept
Performs sequence valid for reference.
void averagePrepared(const std::vector< PreparedMember > &members, const RamseyPulseSequence2DConfig &sequence, double freeEvolutionDuration, double hbar, double &meanA, double &meanB, double *minimumB, double *maximumB) noexcept
Performs average prepared.
void predictPrepared(const PreparedMember &member, const RamseyPulseSequence2DConfig &sequence, double freeEvolutionDuration, double hbar, double &populationA, double &populationB) noexcept
Performs predict prepared.
Operator2 pulseOperator(double energyA, double energyB, double coupling, const RamseyPulseSequence2DConfig &sequence, double duration, double phaseDegrees, double hbar, double maximumStep) noexcept
Performs pulse operator.
std::vector< PreparedMember > prepareEnsemble(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, const RamseyEnsemble2DConfig &ensemble, int stateA, int stateB, double hbar)
PreparedMember prepareMember(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, int stateA, int stateB, double hbar, double transitionOffset, double weight, double maximumPulseStep) noexcept
Prepares member.
State2 propagateConstant(const State2 &state, double energyA, double energyB, double coupling, double driveValue, double dt, double hbar) noexcept
Performs propagate constant.
Declares the ramseyinterferometry2d physics/numerical component used by QPE experiments.
Data structure used by the Bound State Spectrum1D component.
std::vector< BoundState1D > states
Data structure used by the Driven Two Level Projection2D component.
Configuration values for Ramsey Ensemble2D Config.
Data structure used by the Ramsey Ensemble Fringe Sample2D component.
Data structure used by the Ramsey Ensemble Result2D component.
Data structure used by the Ramsey Fringe Sample2D component.
Configuration values for Ramsey Pulse Sequence2D Config.
Data structure used by the Ramsey Theory2D component.
Declares the timedependentpotential2d physics/numerical component used by QPE experiments.