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

ColumnFamily

Which of the three response columns a problem compiles to.

PLAN_FLAGS

The planning effort, by the name a run asks for it by.

REGIME_ELEMENTS

Element count above which a single problem is stepped in the large regime.

MODAL_REGIME_ELEMENTS

Kernel-regime cutoff for fibre transforms with multiple physical nodes.

FFTW_THREAD_ELEMENTS

Elements in one batched transform at or above which FFTW is given more than one thread.

FFTW_WIDE_ELEMENTS

Elements in one batched transform at or above which FFTW takes every allowed thread.

FFTW_MAX_THREADS

Threads a batched transform is given once it is over the threshold.

BLAS_THREAD_ELEMENTS

Elements in the synthesis GEMM at or above which the BLAS keeps its own pool.

BLAS_MAX_THREADS

Threads the synthesis GEMM is given once it is over the threshold.

Functions

apply_pools(pools, *[, controller])

Set the kernel and BLAS pools, and say what was actually reached.

build_general_plans(chunk_nodes, tail_nodes, ...)

Allocate the general route's aligned buffers and plan every transform on them.

build_plans(family, n_columns, n_to, n_t, *)

Allocate the aligned buffers and plan every transform on them.

build_transverse_plans(shape, *, flag, threads)

Plan an in-place complex transverse FFT pair before loading any state.

choose_pools(elements, gemm_elements, ...[, ...])

Choose the four modal pools from the problem's size, with explicit overrides.

export_wisdom([path])

Write FFTW's accumulated wisdom to a file, if one is named.

import_wisdom([path])

Load FFTW wisdom from a file, if one is named and present.

release_pools(limiter)

Give the BLAS pool back, if this backend took it.

wisdom_digest()

Return a short digest of the wisdom in force, for the run's metadata.

Classes

GeneralPlans(spectrum, signal, polarisation, ...)

The general modal route's plans and the aligned buffers they are bound to.

ThreadPools(regime, numba, fftw, blas, ...)

The four modal pools, as this backend set them.

TransformPlans(family, modal_spectrum, ...)

Every plan one problem needs, and the aligned buffers they are bound to.

TransversePlans(spectrum, forward, inverse, ...)

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.md section 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_real

A 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_analytic

A 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.

envelope

An 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_to for 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_THREADS was 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_pol elements (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: object

The 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, rows 6 q + e over 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 None when 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; None when 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, None with 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.

buffer_bytes: dict[str, int]#
drive: Any = None#
drive_spectrum: Any = None#
drive_to_frequency: Any = None#
history: Any = None#
history_to_time: Any = None#
planning_seconds: float = 0.0#
polarisation: Any#
polarisation_spectrum: Any#
product: Any = None#
signal: Any#
spectrum: Any#
tail_drive_to_frequency: Any = None#
tail_history_to_time: Any = None#
tail_to_frequency: Any = None#
tail_to_time: Any = None#
to_frequency: Any#
to_time: Any#
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.

measure is the default: a plan executed millions of times gains from codelet selection. It must be built on a quiet machine, though – MEASURE has been measured planning 2.4 times slower than ESTIMATE on a busy host, because the search itself is timed against the load (docs/dev/backends.md section 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_nodes from docs/dev/backends.md section 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 runs prange over 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: object

The 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", from REGIME_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 no set_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" until apply_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).

as_metadata()[source]#

Return the pools as the run’s metadata records them.

Returns:
dict

One entry per pool plus the regime, all JSON-representable.

blas: int#
blas_mechanism: str#
fftw: int#
numba: int#
numba_openmp: str = 'unset'#
regime: str#
thread_budget: int = 0#
thread_budget_source: str = 'unset'#
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: object

Every plan one problem needs, and the aligned buffers they are bound to.

A plan owns its buffers: pyfftw.FFTW.execute reads 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_real and 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 None when 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 None when 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 None when 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.

absorber_to_frequency: Any = None#
absorber_to_time: Any = None#
buffer_bytes: dict[str, int]#
coarse_projected: Any = None#
coarse_projected_spectrum: Any = None#
coarse_spectrum: Any = None#
coarse_time: Any = None#
coarse_to_frequency: Any = None#
convolved: Any = None#
drive: Any = None#
drive_spectrum: Any = None#
family: str#
in_stage_spectrum: Any = None#
in_stage_time: Any = None#
in_stage_to_frequency: Any = None#
ledger()[source]#

Return the bytes each plan buffer holds, by name.

Returns:
dict

Buffer name to bytes, for solprop.backends.protocol.Backend.memory_ledger().

modal_spectrum: Any#
modal_time: Any#
planning_seconds: float = 0.0#
projected: Any#
projected_spectrum: Any#
raman_tail_to_frequency: Any = None#
raman_tail_to_time: Any = None#
raman_to_frequency: Any = None#
raman_to_time: Any = None#
to_frequency: Any#
to_time: Any#
class solprop.backends.numba_cpu.fft.TransversePlans(spectrum, forward, inverse, plan_heap_growth, planning_seconds)[source]#

Bases: object

Own 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.

forward: Any#
inverse: Any#
plan_heap_growth: int#
planning_seconds: float#
spectrum: Any#
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_mechanism filled in from what was found.

limiterobject or None

The live threadpoolctl limit, to be released when the backend closes, or None where 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 flag is not one of PLAN_FLAGS or 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_threads is not supplied.

raman_threadsint, optional

Positive FFTW thread count for the doubled-grid Raman pair. None uses threads; 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.md section 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. None uses 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 flag is not one of PLAN_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_to of the synthesis GEMM, which decides the BLAS pool.

transform_elementsint

n_columns * n_to of the batched modal transform, which decides FFTW’s.

threadsint, optional

Override for the kernels’ pool. None takes the regime’s choice. Either way the pool is clamped to the process’s thread budget (solprop._process.ThreadBudget.explicit) and to Numba’s own NUMBA_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. None reads it off elements against 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 a prange and 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. None uses REGIME_ELEMENTS; modal problems with multiple physical nodes use MODAL_REGIME_ELEMENTS. An explicit regime still 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 keep FFTW_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. None takes SOLPROP_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. None reads the SOLPROP_FFTW_WISDOM environment 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. None takes SOLPROP_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.