Parallel observable grids¶
An observable grid evaluates a scalar quantity at independent logical points.
The model owns the physics and prepares its background; the shared
bethe::evaluate_observable_grid adapter owns bounded admission and ordered collection.
Uni20 supplies the scheduler. No output format or model formula belongs in the
adapter.
Hubbard and Lieb–Liniger dispersion use this path. See the command-line examples. Thermodynamic XXX spectral grids also use this path, with ordered spectrum rows and final failure/threshold counts; see the frontend. Thermal Lieb–Liniger also uses this adapter for independent temperature solves in either ensemble, preserving each point's iteration and convergence diagnostics. Hubbard continuum edges also use this adapter for independent total momenta, retaining each point's inversion/search diagnostics. Sine-Gordon Bethe–Yang levels also use this adapter: each level owns an independent root search and repeated phase integrals. Other observables can adopt the same path after checking their independence and scratch ownership.
Using the library¶
#include <bethe/lieb_liniger_thermo.hpp>
#include <bethe/observable_grid.hpp>
#include <uni20/async/tbb_scheduler.hpp>
namespace model = bethe::lieb_liniger::thermo;
model::Solver<double> const background(4.0, 1.0);
// Validate the physical request and decide how to report background failure
// before opening output. This example samples the Type-I branch on [0, 6].
uni20::async::TbbScheduler scheduler(4);
std::vector<model::Point<double>> points;
bethe::evaluate_observable_grid(
129,
[&](std::size_t i) {
return background.at_momentum(model::Branch::type_i, 6.0 * double(i) / 128.0);
},
[&](std::size_t i, auto point) {
// Calling thread, increasing i: append a row or accumulate a moment here.
points.push_back(std::move(point));
},
{.scheduler = &scheduler, .max_in_flight = 128});
The borrowed scheduler must outlive the synchronous call. An omitted scheduler
uses Uni20's active scheduler (serial by default); the adapter never changes it.
ExecutionOptions::max_in_flight bounds active and completed-but-uncollected
points and defaults to 128. batch_size remains for other batched clients; both
bounds must be positive. Logical
indices are exactly [0, count), including empty and single-point grids.
Frontends keep branches in their declared output order; a row's identity is
its branch and logical point index. Hubbard dispersion flattens its selected
branches into one logical grid, allowing evaluation across branch boundaries
without changing row order. Lieb–Liniger currently evaluates each branch's grid
in turn. In both cases the admission window bounds active and uncollected results;
collected output can still grow with the grid.
Ownership, errors and timing¶
- Evaluation may run concurrently. Share only immutable prepared data, use worker-local scratch, and return an owning value. Move-only results work. Do not change global precision settings or run dependent continuation steps as independent points.
- Collection runs on the calling thread in increasing index order. Tables, diagnostics and floating-point reductions belong here, never in workers. Ordinary numerical failure/status values are passed through, not dropped.
- As each result is collected, a new point may enter the bounded window. A slow early point or collector applies backpressure; there is no whole-batch join. Collection may retain more data; disable table retention for streaming output.
- Execution errors and cancellation stop admission and drain every accepted task before propagating. Already collected prefixes remain, but a partial grid is never reported as successful completion. Numerical failure/status values are ordinary results.
- The optional fifth argument,
observe_wait(index, 1), scopes waiting for one point, excluding collection. It does not measure total computation: workers may compute while collection runs. Time the entire grid call for total costs. The frontends'CPU timeincludes background preparation and complete grid calls, including row output;CPU timing scoperecords this in metadata.
evaluate_observable_grid_async offers the same window policy with an owning
Async result. Its collector is serialized on the scheduler, so use the
synchronous adapter for caller-thread-only CLI output. See
Async execution for ownership and cancellation.
When the background itself is not ready yet,
prepared_observable_grid.hpp
adds preparation as an Async result and a reader-dependent overload of the same
grid. Several curves can share one prepared solver without copying its meshes;
see shared backgrounds and the complete
Lieb–Liniger example there.
Threading leaves each scalar solver's arithmetic and stopping rules unchanged.
Native-precision tests compare every field against independent scalar calls,
using both a reverse-order scheduler and oneTBB, for binary64, native
long double and binary128 when configured. CLI tests also compare row order,
partial failures and non-retained file output across concurrency limits.
Performance measurements¶
Benchmarks by workload preserve the dated timings, commands, precision/provider configurations and whole-result comparisons.
Thermodynamic XXX streaming measurement.
Thermal Lieb–Liniger temperature scans.
Sine-Gordon Bethe–Yang level scans.
Finite-volume integral-equation sweeps.
Overlapping one vacuum/excited pair.
ASEP whole-process measurements.
TASEP and finite XYZ parameter sweeps.
Measurement on 28 October 2026.
Nested-model parameter sweeps.