solprop.backends.numba_cpu.fft#
FFTW plans, wisdom, and the five thread pools the backend owns.
Two things live here because they are the same decision. The transforms are planned by
FFTW on aligned buffers that never move, and FFTW’s own pool is one of five that share
the machine: the pool configuration alone was measured to move one step by a
factor of nine (docs/dev/backends.md section 3.4). Three pools of N threads
on N cores is 3N runnable threads and the idle ones spin, so a backend that sets its
own pool and inherits the others is not configured, it is lucky.
Planning is destructive. FFTW_MEASURE writes over its input while it searches
for a codelet, so every plan is built at construction, before a buffer holds a state.
A plan built later, on a live state, would silently replace it with noise.
No normalisation. pyfftw.FFTW.execute applies none, and this backend wants none:
the 1/N of an inverse transform is folded into the contract’s own scale factors
(kernel-contract.md section 4), exactly as the reference’s numpy.fft calls have
it folded in.
Module Attributes
Which of the three response columns a problem compiles to. |
|
The planning effort, by the name a run asks for it by. |
|
Element count above which a single problem is stepped in the large regime. |
|
Kernel-regime cutoff for fibre transforms with multiple physical nodes. |
|
Elements in one batched transform at or above which FFTW is given more than one thread. |
|
Elements in one batched transform at or above which FFTW takes every allowed thread. |
|
Threads a batched transform is given once it is over the threshold. |
|
Elements in the synthesis GEMM at or above which the BLAS keeps its own pool. |
|
Threads the synthesis GEMM is given once it is over the threshold. |
Functions
|
Set the kernel and BLAS pools, and say what was actually reached. |
|
Allocate the general route's aligned buffers and plan every transform on them. |
|
Allocate the aligned buffers and plan every transform on them. |
|
Plan an in-place complex transverse FFT pair before loading any state. |
|
Choose the four modal pools from the problem's size, with explicit overrides. |
|
Write FFTW's accumulated wisdom to a file, if one is named. |
|
Load FFTW wisdom from a file, if one is named and present. |
|
Give the BLAS pool back, if this backend took it. |
Return a short digest of the wisdom in force, for the run's metadata. |
Classes
|
The general modal route's plans and the aligned buffers they are bound to. |
|
The four modal pools, as this backend set them. |
|
Every plan one problem needs, and the aligned buffers they are bound to. |
|
Own one Cartesian scratch and its persistent in-place FFTW pair. |
- solprop.backends.numba_cpu.fft.BLAS_MAX_THREADS = 2#
Threads the synthesis GEMM is given once it is over the threshold.
Two, and the machine’s own count is far from harmless: on the CtC GEMM the server reads 1.45x at two threads, 1.22x at four, 1.23x at eight, 1.19x at eighteen, 1.05x at thirty-six and 1.01x at seventy-two. The GEMM is pathologically skinny – k is the mode count, four or six – so there is very little for a large pool to divide and a great deal of synchronisation to pay for it.
A GEMM twice the size of the CtC case’s does start to want more (1.83x at four against 1.60x at two on a 5.4-million-element shape), and that is where a size-dependent count would earn itself; neither the M2 nor the CtC case reaches it.
- solprop.backends.numba_cpu.fft.BLAS_THREAD_ELEMENTS = 262144#
Elements in the synthesis GEMM at or above which the BLAS keeps its own pool.
n_rows * n_to. Set from the x86-64 server alone, and that is not a shortcut: this is the one threshold the two machines cannot both inform, because numpy on the Apple-silicon laptop is linked against Accelerate, which exposes no runtime thread count at all. The laptop’s own measurement is what establishes that – 0.98x, 0.98x, 0.99x, 0.99x, 0.99x, 1.01x, 1.02x, 0.89x across a 160-fold range of sizes, which is not a threading curve but a flat line with noise.On the server, where OpenBLAS answers to threadpoolctl, two threads first repay at the M2 case’s own GEMM shape:
elements server, 2 threads 135 168 0.99x 270 336 (33 x 4 @ 4 x 8192) 1.22x 540 672 1.35x 2 686 976 (41 x 6 @ 6 x 65 536) 1.45x
The threshold is the round number below that crossover. The bracket of
docs/dev/backends.mdsection 3.4 – one thread wins on RDW, loses on CtC – is confirmed and located.
- solprop.backends.numba_cpu.fft.ColumnFamily#
Which of the three response columns a problem compiles to.
field_realA field grid that radiates the third harmonic: the samples are real and the Kerr term is the full cubic
c rho (sum_q E_q^2) E_p.field_analyticA field grid that does not: the column works on the analytic signal of the real field and takes the real part of the polarisation on the way out. The analytic signal is not a second transform here – it is the complex inverse of the same one-sided spectrum with the positive bins doubled, which the reference forms separately once per node chunk.
envelopeAn envelope grid: the samples are complex throughout, the harmonic arrives as a carrier when the grid radiates it. Envelope plasma carries two independent current streams through its own forward transforms before spectral assembly.
alias of
Literal[‘field_real’, ‘field_analytic’, ‘envelope’]
- solprop.backends.numba_cpu.fft.FFTW_MAX_THREADS = 4#
Threads a batched transform is given once it is over the threshold.
Not the core count, which is what the pool used to get and is worse almost everywhere: on the CtC modal pair the Apple-silicon laptop reads 1.89x at two threads, 2.64x at four, 2.31x at eight and 1.69x at eighteen, and the x86-64 server 1.81x, 2.33x, 1.66x and 1.74x. Four is at or near the best on every size the M2 and CtC cases span, on both machines.
Above about half a million elements more threads do start to pay again – the laptop reads 4.30x at eight on a 786 432-element pair against 2.62x at four – but both cases are below that and a size-dependent count is not measured finely enough to set here.
- solprop.backends.numba_cpu.fft.FFTW_THREAD_ELEMENTS = 65536#
Elements in one batched transform at or above which FFTW is given more than one thread.
n_columns * n_tofor the modal pair, and the same count over the doubled grid for the Raman pair. Measured on two machines, an Apple-silicon laptop (numpy linked against Accelerate) and an 18-core x86-64 server (OpenBLAS), as the smallest size at which two threads repay what they cost – which is the only defensible place for it, since below that they are a straight loss:elements laptop, 2 threads server, 2 threads 8 192 0.49x 0.81x 16 384 0.78x 0.63x 32 768 (the M2 modal pair) 1.06x 1.37x 65 536 <- the threshold 1.52x 1.58x 393 216 (the CtC modal pair) 1.89x 1.81x
The two FFTW pools break even at the same count, 32 768, and the threshold sits an octave above it. That is a second measurement, not caution. Timing the M2 case end to end on this backend with the pool forced to one and left to the rule, interleaved so that a drifting load cancels, the transform-threaded arm is slower: 12.89 and 11.48 s at one thread against 14.66 and 13.36 s at four, on a case whose modal pair is exactly 32 768 elements. A tight loop over one plan amortises the pool’s wake-up; a step pays it on every call, so break-even in the sweep is a loss in a run. The threshold belongs where the sweep shows a gain worth having on both machines – 1.52x and 1.58x at 65 536 – and the CtC case, at 393 216, is far above it either way.
A placeholder of 1e6 is fifteen times too high: with it the CtC case ran its 393 216-element transform serially and took 5710 s at 132 % of one core.
- solprop.backends.numba_cpu.fft.FFTW_WIDE_ELEMENTS = 1048576#
Elements in one batched transform at or above which FFTW takes every allowed thread.
FFTW_MAX_THREADSwas measured on transforms of up to 393 216 elements. A free-space evaluation’s transforms are far larger: the transverse pair moves a whole field volume,n_x n_y n_omega n_polelements (88 million on a 384-square grid of 600 bins), and a node chunk of the spatial default budget gives a temporal batch of about three million. Both scale with the pool (SEP 28, the 384-square Kerr march):pool x86-64, 64 cores (Linux) Apple silicon, 18 cores transverse 4, temporal 4 -- 4.75 s/step transverse all, temporal 4 2.63 s/step 2.26 s/step transverse all, temporal all 0.77 s/step 2.02 s/step transverse all, temporal 1 3.31 s/step --
A transform this long also makes a pool’s wake-up negligible.
- class solprop.backends.numba_cpu.fft.GeneralPlans(spectrum, signal, polarisation, polarisation_spectrum, to_time, to_frequency, tail_to_time=None, tail_to_frequency=None, drive=None, drive_spectrum=None, product=None, history=None, drive_to_frequency=None, history_to_time=None, tail_drive_to_frequency=None, tail_history_to_time=None, planning_seconds=0.0, buffer_bytes=<factory>)[source]#
Bases:
objectThe general modal route’s plans and the aligned buffers they are bound to.
The fourth plan family: the general route synthesises in the frequency domain and transforms three Cartesian components per node, a complex inverse of the doubled one-sided spectrum that is the analytic signal, where the three column families above transform modal columns once per stage. Every buffer is a node chunk wide, rows
3 q + c; the delayed response adds a pair on the doubled axis, rows6 q + eover the six stored pair histories. A partial last chunk has its own plans bound to prefix views of the same buffers, so it transforms its own rows and no more, and owns nothing.- Attributes:
- spectrumnumpy.ndarray
The synthesised fine spectrum, complex,
(3 chunk, n_to).- signalnumpy.ndarray
Its inverse, the analytic signal at the chunk’s nodes, complex.
- polarisationnumpy.ndarray
The real polarisation on the fine time grid, float64,
(3 chunk, n_to).- polarisation_spectrumnumpy.ndarray
Its one-sided spectrum, complex,
(3 chunk, n_to // 2 + 1).- to_time, to_frequencypyfftw.FFTW
The chunk’s pair.
- tail_to_time, tail_to_frequencypyfftw.FFTW or None
A partial last chunk’s pair, or
Nonewhen the chunk divides the nodes.- drive, drive_spectrum, product, historynumpy.ndarray or None
The delayed response’s buffers on the doubled axis,
(6 chunk, 2 n_to)real,(6 chunk, n_to + 1)complex twice and(6 chunk, 2 n_to)real;Nonewhen no region declares a delayed response. The upper half of drive is zero and stays zero: the real-to-complex transform preserves its input and nothing else writes there.- drive_to_frequency, history_to_timepyfftw.FFTW or None
The doubled pair,
Nonewith the buffers.- tail_drive_to_frequency, tail_history_to_timepyfftw.FFTW or None
Their partial-chunk pair.
- planning_secondsfloat
What planning cost.
- buffer_bytesdict
Bytes each named buffer holds, and the allocator’s growth over planning.
- solprop.backends.numba_cpu.fft.MODAL_REGIME_ELEMENTS = 131072#
Kernel-regime cutoff for fibre transforms with multiple physical nodes.
The reduced M2 and CtC trials benefit from node parallelism below a million-element cutoff. Re-measured after SEP 25’s multipass plasma column: the resolved argon blue-shift window (one mode, 33 nodes, 4096 samples, 135 168 elements) took 3.35 ms a Runge-Kutta trial in the parallel regime against 7.68 ms serially at six threads on Apple silicon, so the power-of-two bracket now lies below it. Single-node and spatial problems keep
REGIME_ELEMENTS. FFTW and BLAS retain independent size rules.
- solprop.backends.numba_cpu.fft.PLAN_FLAGS: Mapping[str, str] = {'estimate': 'FFTW_ESTIMATE', 'measure': 'FFTW_MEASURE', 'patient': 'FFTW_PATIENT'}#
The planning effort, by the name a run asks for it by.
measureis the default: a plan executed millions of times gains from codelet selection. It must be built on a quiet machine, though –MEASUREhas been measured planning 2.4 times slower thanESTIMATEon a busy host, because the search itself is timed against the load (docs/dev/backends.mdsection 3.3).
- solprop.backends.numba_cpu.fft.REGIME_ELEMENTS = 1000000#
Element count above which a single problem is stepped in the large regime.
n_to * n_modes * n_pol * n_nodesfromdocs/dev/backends.mdsection 3.4: below this the kernels run serially and FFTW takes one thread, because the pools cost more to wake than the work saves; above it the response pass runsprangeover node chunks and the batched plans take FFTW’s pool.
- class solprop.backends.numba_cpu.fft.ThreadPools(regime, numba, fftw, blas, blas_mechanism, numba_openmp='unset', thread_budget=0, thread_budget_source='unset')[source]#
Bases:
objectThe four modal pools, as this backend set them.
The transverse FFTW pool of a Cartesian run is chosen beside them by
build_transverse_plans().- Attributes:
- regimestr
"small"or"large", fromREGIME_ELEMENTS.- numbaint
Threads the fused kernels may use.
- fftwint
Threads each batched plan was built with.
- blasint
Threads the BLAS was limited to, or its own count where it could not be set.
- blas_mechanismstr
How the BLAS pool was reached: the library threadpoolctl found and limited, or
"uncontrolled"when there is none it can set. Apple’s Accelerate is the latter: it dispatches through Grand Central Dispatch and exposes noset_num_threads, so it is left to its own size gating and said to be.- numba_openmpstr
How Numba’s threads were started (
solprop.backends.numba_cpu.openmp.numba_launch()):"unset"untilapply_pools()starts them.- thread_budgetint
The process’s thread budget, which every pool stays within (ADR-052); zero where it was not read.
- thread_budget_sourcestr
The limit that set it (
solprop._process.ThreadBudget).
- class solprop.backends.numba_cpu.fft.TransformPlans(family, modal_spectrum, modal_time, projected, projected_spectrum, to_time, to_frequency, coarse_spectrum=None, coarse_time=None, absorber_to_time=None, absorber_to_frequency=None, in_stage_time=None, in_stage_spectrum=None, in_stage_to_frequency=None, coarse_projected=None, coarse_projected_spectrum=None, coarse_to_frequency=None, drive=None, drive_spectrum=None, convolved=None, raman_to_frequency=None, raman_to_time=None, raman_tail_to_frequency=None, raman_tail_to_time=None, planning_seconds=0.0, buffer_bytes=<factory>)[source]#
Bases:
objectEvery plan one problem needs, and the aligned buffers they are bound to.
A plan owns its buffers:
pyfftw.FFTW.executereads and writes the arrays the plan was built on and nothing else, so the stage driver fills these in place rather than passing arrays in.- Attributes:
- familystr
Which column the plans were built for.
- modal_spectrumnumpy.ndarray
The padded fine spectrum the inverse transform reads, complex.
- modal_timenumpy.ndarray
The modal field on the fine time grid, real for
field_realand complex otherwise.- projectednumpy.ndarray
The modal polarisation on the fine time grid, real except on an envelope grid.
- projected_spectrumnumpy.ndarray
Its one-sided (or full, on an envelope grid) spectrum, complex.
- to_timepyfftw.FFTW
Padded spectrum to modal time.
- to_frequencypyfftw.FFTW
Modal polarisation back to the fine spectrum.
- coarse_spectrum, coarse_timenumpy.ndarray
The absorber’s buffers on the coarse grid.
- absorber_to_time, absorber_to_frequencypyfftw.FFTW
The absorber’s pair, or
Nonewhen the problem has no absorber.- in_stage_time, in_stage_spectrumnumpy.ndarray or None
The in-stage absorber’s term on the fine time grid (real except on an envelope grid) and its spectrum, a second output beside the polarisation’s because both spectra are read by the same stage’s finish.
- in_stage_to_frequencypyfftw.FFTW or None
The in-stage term’s forward transform, or
Nonewhen no in-stage absorber is on.- coarse_projected, coarse_projected_spectrumnumpy.ndarray or None
The embedded coarse rule’s projection and its spectrum, for the quadrature alarm. The fine rule’s spectrum is already in hand from pass 6 of the last stage, so the alarm costs one forward transform where the reference pays two.
- coarse_to_frequencypyfftw.FFTW or None
The alarm’s transform, or
Nonewhen the rule has no embedded pair.- drive, drive_spectrum, convolvednumpy.ndarray or None
The Raman buffers on the doubled grid, one set for a whole node chunk.
- raman_to_frequency, raman_to_timepyfftw.FFTW or None
The Raman pair on the doubled grid.
- raman_tail_to_frequency, raman_tail_to_timepyfftw.FFTW or None
A partial node chunk’s pair, bound to prefix views of the same Raman buffers. The pair adds no owned array and executes sequentially with the full pair.
- planning_secondsfloat
What planning cost, for the ledger.
- ledger()[source]#
Return the bytes each plan buffer holds, by name.
- Returns:
- dict
Buffer name to bytes, for
solprop.backends.protocol.Backend.memory_ledger().
- class solprop.backends.numba_cpu.fft.TransversePlans(spectrum, forward, inverse, plan_heap_growth, planning_seconds)[source]#
Bases:
objectOwn one Cartesian scratch and its persistent in-place FFTW pair.
- Attributes:
- spectrumnumpy.ndarray
Complex scratch shaped (n_y, n_x, n_pol, n_omega), with x fastest after flattening the first two axes.
- forward, inversepyfftw.FFTW
Unnormalised transverse plans over the first two axes.
- plan_heap_growthint
Process allocator net growth during planning in bytes, including opaque FFTW work areas. A measured count, not a portable bound or allocator high-water mark.
- planning_secondsfloat
Time spent constructing the pair, in seconds.
- solprop.backends.numba_cpu.fft.apply_pools(pools, *, controller=None)[source]#
Set the kernel and BLAS pools, and say what was actually reached.
FFTW’s pool is not set here: it is fixed when a plan is built, so it travels with the plan rather than with the process.
- Parameters:
- poolsThreadPools
What
choose_pools()decided.- controllerthreadpoolctl.ThreadpoolController, optional
The library walk to use. A backend passes none and gets the real one; a test passes its own, because which branch is taken here is a property of the machine – Accelerate has no controllable BLAS and OpenBLAS has – and both must be exercised wherever the suite runs.
- Returns:
- poolsThreadPools
The same, with
blas_mechanismfilled in from what was found.- limiterobject or None
The live threadpoolctl limit, to be released when the backend closes, or
Nonewhere there was no controllable library.
- solprop.backends.numba_cpu.fft.build_general_plans(chunk_nodes, tail_nodes, n_to, *, delayed, flag='measure', threads=1)[source]#
Allocate the general route’s aligned buffers and plan every transform on them.
Planning is destructive, so every buffer is zeroed after the last plan is built and before a state ever reaches one.
- Parameters:
- chunk_nodesint
Nodes a chunk holds, positive.
- tail_nodesint
Nodes of a partial last chunk, or zero when the chunk divides the node count. Smaller than chunk_nodes.
- n_toint
Fine time samples, positive and even.
- delayedbool
Whether to add the delayed response’s pair on the doubled axis.
- flagstr, optional
A key of
PLAN_FLAGS."measure"by default.- threadsint, optional
FFTW’s pool for every plan of the family, positive.
- Returns:
- GeneralPlans
The plans, their buffers, and what planning cost.
- Raises:
- BackendError
If
flagis not one ofPLAN_FLAGSor the tail is not smaller than the chunk.
- solprop.backends.numba_cpu.fft.build_plans(family, n_columns, n_to, n_t, *, flag='measure', threads=1, raman_threads=None, chunk_columns=0, tail_columns=0, raman_tail_threads=None, absorber=False, in_stage_absorber=False, embedded=False)[source]#
Allocate the aligned buffers and plan every transform on them.
- Parameters:
- familystr
Which response column the problem compiles to; see
ColumnFamily.- n_columnsint
Modal columns transformed together,
n_modes * n_pol.- n_toint
Fine time samples.
- n_tint
Coarse time samples, which the temporal absorber works on.
- flagstr, optional
A key of
PLAN_FLAGS."measure"by default.- threadsint, optional
FFTW’s pool for the modal, embedded and absorber plans, and the Raman pair when
raman_threadsis not supplied.- raman_threadsint, optional
Positive FFTW thread count for the doubled-grid Raman pair.
Noneusesthreads; a backend sizes this pair from its own batched operand.- chunk_columnsint, optional
Columns in one node chunk. Non-zero adds the Raman pair on the doubled grid, one pair for the whole chunk rather than one per column (
docs/dev/backends.mdsection 3.3).- tail_columnsint, optional
Columns in a partial Raman chunk, or zero when there is no tail. A positive count must be smaller than
chunk_columns. The persistent tail pair uses prefix views of the existing Raman arrays, without extra owned storage.- raman_tail_threadsint, optional
Positive FFTW thread count for the tail pair.
Noneuses the full Raman pair’s count; a backend sizes the tail from its own doubled-grid batch.- absorberbool, optional
Whether to plan the coarse-grid pair the temporal absorber needs.
- in_stage_absorberbool, optional
Whether to plan the fine-grid forward transform of ADR-025’s in-stage absorber term, on buffers of its own.
- embeddedbool, optional
Whether to plan the extra forward transform the quadrature alarm needs, which only a rule with an embedded coarse pair has.
- Returns:
- TransformPlans
The plans, their buffers, and what planning cost.
- Raises:
- BackendError
If
flagis not one ofPLAN_FLAGS, an explicit Raman thread count is not positive, or the tail width is outside[0, chunk_columns)except that both widths may be zero when there is no Raman pair.
- solprop.backends.numba_cpu.fft.build_transverse_plans(shape, *, flag, threads, spatial_contiguous=False)[source]#
Plan an in-place complex transverse FFT pair before loading any state.
- Parameters:
- shapetuple of int
Scratch dimensions (n_y, n_x, n_pol, n_omega), all positive.
- flagstr
Existing FFTW planning choice: estimate, measure or patient.
- threadsint
FFTW worker count, positive.
- spatial_contiguousbool, optional
Store spatial/polarisation columns contiguously behind the public view. Envelope transforms benefit from this layout; field columns retain their contiguous temporal-frequency axis. Both use exactly one owning scratch.
- Returns:
- TransversePlans
Persistent pair sharing exactly one full coarse spectral scratch.
- Raises:
- BackendError
If the planning flag is unsupported.
- solprop.backends.numba_cpu.fft.choose_pools(elements, gemm_elements, transform_elements, *, threads=None, fftw_threads=None, blas_threads=None, regime=None, regime_elements=None, spatial=False)[source]#
Choose the four modal pools from the problem’s size, with explicit overrides.
- Parameters:
- elementsint
n_to * n_modes * n_pol * n_nodes, which selects the regime.- gemm_elementsint
n_rows * n_toof the synthesis GEMM, which decides the BLAS pool.- transform_elementsint
n_columns * n_toof the batched modal transform, which decides FFTW’s.- threadsint, optional
Override for the kernels’ pool.
Nonetakes the regime’s choice. Either way the pool is clamped to the process’s thread budget (solprop._process.ThreadBudget.explicit) and to Numba’s ownNUMBA_NUM_THREADS.- fftw_threadsint, optional
Override for FFTW’s pool.
- blas_threadsint, optional
Override for the BLAS pool.
- regime{‘small’, ‘large’}, optional
Override for the regime itself.
Nonereads it offelementsagainst the selected cutoff, which is what a run should normally do; forcing it is for a measurement that wants a problem below the threshold stepped as a large one – the node loop becomes aprangeand every pool takes its threads, so a case that would otherwise be serial can be measured against thread count.- regime_elementsint, optional
Positive element cutoff for the automatic kernel regime.
NoneusesREGIME_ELEMENTS; modal problems with multiple physical nodes useMODAL_REGIME_ELEMENTS. An explicitregimestill takes precedence.- spatialbool, optional
Whether the transform is a free-space or planar evaluation’s (its transverse pair or its chunk’s temporal batch), which takes every thread from
FFTW_WIDE_ELEMENTS. Modal and Raman transforms keepFFTW_MAX_THREADS, the rule measured for them; the wider pool was measured on spatial transforms only (SEP 28).
- Returns:
- ThreadPools
What was chosen, before it is applied.
- Raises:
- BackendError
If a thread count or cutoff is not positive, or the regime is unknown.
Examples
>>> from solprop.backends.numba_cpu.fft import choose_pools >>> choose_pools(1000, 1000, 1000, threads=4).regime 'small'
- solprop.backends.numba_cpu.fft.export_wisdom(path=None)[source]#
Write FFTW’s accumulated wisdom to a file, if one is named.
- Parameters:
- pathstr or os.PathLike, optional
The wisdom file.
NonetakesSOLPROP_FFTW_WISDOM.
- Returns:
- bool
Whether wisdom was written.
- solprop.backends.numba_cpu.fft.import_wisdom(path=None)[source]#
Load FFTW wisdom from a file, if one is named and present.
Explicit, never implicit: a plan built from someone else’s wisdom is a plan whose provenance is not in the run’s metadata.
Nonereads theSOLPROP_FFTW_WISDOMenvironment variable, and an absent or unreadable file is not an error – planning simply starts from nothing.- Parameters:
- pathstr or os.PathLike, optional
The wisdom file.
NonetakesSOLPROP_FFTW_WISDOM.
- Returns:
- bool
Whether wisdom was loaded.
- solprop.backends.numba_cpu.fft.release_pools(limiter)[source]#
Give the BLAS pool back, if this backend took it.
- Parameters:
- limiterobject or None
Whatever
apply_pools()returned.
- solprop.backends.numba_cpu.fft.wisdom_digest()[source]#
Return a short digest of the wisdom in force, for the run’s metadata.
Two runs whose plans were chosen from different wisdom are not the same execution, and the digest is what makes that visible in a ledger entry.
- Returns:
- str
The first sixteen hex characters of a BLAKE2b over the exported wisdom.