Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
173 commits
Select commit Hold shift + click to select a range
5a0323a
Read the transverse edge once per position, by a replay's next step
jtravs Nov 4, 2026
3cf79a3
Reduce the split collar's occupancy per column, without atomics
jtravs Nov 4, 2026
f6c975e
Sum energy, the peak and a low-storage step's norm with real reductions
jtravs Nov 4, 2026
30c2272
Size the march's snapshot before copying it
jtravs Nov 4, 2026
b774315
Add benchmarks.device_split, a field-free point's split
jtravs Nov 4, 2026
e7ec3a4
Say how the edge's reads and the occupancy work now
jtravs Nov 4, 2026
c4e423c
Cross a large state through a ring of pinned chunks
jtravs Nov 4, 2026
eb067a2
Check an observed state's finiteness with a threaded sum
jtravs Nov 4, 2026
34a5ed5
Convert a kept plane in threads, straight into the store
jtravs Nov 4, 2026
1e5d2fc
Keep the ring's device tests to the ring, and note the change
jtravs Nov 4, 2026
0b64ad0
Fit the comment to the line limit
jtravs Nov 4, 2026
b1ca76b
Merge fix/check-waste
jtravs Nov 4, 2026
19a86e5
Give the keeper's failure test the size query it now reads
jtravs Nov 4, 2026
6b0cb25
Document the rows' helpers and narrow the handlers' test fields
jtravs Nov 4, 2026
5e4493a
SEP 44 draft: read the transverse edge where the state lives
jtravs Nov 4, 2026
4996b26
Port the native State tests to GeneralState
jtravs Nov 4, 2026
44ef418
Make GeneralState BoundState's only state
jtravs Nov 4, 2026
3ccab67
Drop the phase_b_engine flag and needs_general_engine
jtravs Nov 4, 2026
9e80888
Split the transverse edge's reductions from its judgement
jtravs Nov 4, 2026
c210b08
Delete the native State and Workspace engine
jtravs Nov 4, 2026
b74e39a
Ring test: compare what a load keeps, on the active band
jtravs Nov 4, 2026
e1883d6
Read the transverse edge where the state lives
jtravs Nov 4, 2026
09e3158
Merge branch 'fix/host-crossing' into chore/native-primary
jtravs Nov 4, 2026
b54dd45
Fit the occupancy reduction to HIP's smallest block
jtravs Nov 4, 2026
d0619cd
Merge branch 'fix/check-waste' into fix/host-crossing
jtravs Nov 4, 2026
1431413
Point the probe at the reading's names, and note the plane skip's end
jtravs Nov 4, 2026
63daed4
Merge branch 'docs/sep-0044-native-edge' into feature/sep-0044-native…
jtravs Nov 4, 2026
7496542
Merge branch 'fix/host-crossing' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
9f8007e
SEP 44: weight tables, and the record of phases 1 and 2
jtravs Nov 4, 2026
a4f44a4
Say where the edge is read, and what the check now costs
jtravs Nov 4, 2026
52c6238
Hold the native read to two polarisations and to every march
jtravs Nov 4, 2026
ab880c3
Leave HIP's block room for the occupancy kernel's own memory
jtravs Nov 4, 2026
5fafbfa
Merge branch 'fix/check-waste' into fix/host-crossing
jtravs Nov 4, 2026
a07e922
Drop Problem::cubic_real and BoundState's duplicate accessors
jtravs Nov 4, 2026
2debed2
Replace KERNEL_SCHEMA_VERSION with a source digest
jtravs Nov 4, 2026
c28eeac
Declare the run layer's backend hooks and call them directly
jtravs Nov 4, 2026
a006d4a
Correct DRIVER_SURFACE and its frozen table
jtravs Nov 4, 2026
9817b52
ADR-066: native is the primary backend
jtravs Nov 4, 2026
a112bb4
Clear stale statements of a kernel schema; say what closed returns
jtravs Nov 4, 2026
ec6d6e9
Say in the contract and backends.md section 10 what ADR-066 changed
jtravs Nov 4, 2026
4c35178
Digest an array not held in its order block by block
jtravs Nov 4, 2026
e20e282
Form the transverse residual from exact squares and math.fsum
jtravs Nov 4, 2026
98e450f
Build a large launch in threads, with its admittance formed once
jtravs Nov 4, 2026
a68158f
Give nditer its one operand's flags flat, as the stubs read them
jtravs Nov 4, 2026
3cbb921
Say why nditer's flag is outside the stubs
jtravs Nov 4, 2026
c8725ff
Put the stub note on the call the checker reports
jtravs Nov 4, 2026
c26059d
SEP 44: keep Part II empty for a draft, and name its tracking issue
jtravs Nov 4, 2026
a121c73
SEP 44: count the reads, not the calls that may read
jtravs Nov 4, 2026
49ddb18
docs: record AI review of SEP 44
jtravs Nov 4, 2026
1c52059
Ledger: the default check on a device
jtravs Nov 4, 2026
9207622
Merge remote-tracking branch 'origin/refactor/record-simplification' …
jtravs Nov 4, 2026
578d098
Drop two tests whose premise is gone; fit the new text to ruff
jtravs Nov 4, 2026
e914124
Kernel schema 47, on top of the held plane's removal
jtravs Nov 4, 2026
fe2eb91
Merge fix/check-waste, on top of the held plane's removal
jtravs Nov 4, 2026
7bdf8db
Merge fix/host-crossing, on top of the held plane's removal
jtravs Nov 4, 2026
aad31ab
Ledger: the state's crossing through a ring of pinned chunks
jtravs Nov 4, 2026
40a8de0
Hold the native tests to kernel schema 47
jtravs Nov 4, 2026
5ac2482
Merge #508's literals, at this stack's schema 47
jtravs Nov 4, 2026
de35094
Merge branch 'fix/check-waste' into fix/host-crossing
jtravs Nov 4, 2026
710d943
Ledger: say machine
jtravs Nov 4, 2026
eac9c5b
Merge branch 'fix/check-waste' into fix/host-crossing
jtravs Nov 4, 2026
b848425
Add SEP 40's four-way overhead control, and independent reading tests
jtravs Nov 4, 2026
8cfaa82
Merge branch 'fix/host-crossing' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
2024a2e
Count a state's crossings, and what reading the edge holds
jtravs Nov 4, 2026
d297e50
Test the estimate's edge row by a sentinel
jtravs Nov 4, 2026
e7f2b66
SEP 44: revise in response to the AI review, with the maintainer's de…
jtravs Nov 4, 2026
a35774b
Keep huge_page with the only code that uses it
jtravs Nov 4, 2026
ddea23f
Merge branch 'fix/host-crossing' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
53ed482
Merge fix/host-crossing, on top of #508
jtravs Nov 4, 2026
f1b1e44
Merge branch 'fix/host-crossing' into chore/native-primary
jtravs Nov 4, 2026
28dd418
Drop what the merge left in the driver's frozen table
jtravs Nov 4, 2026
c1c4c65
Let the probe type-check without native installed
jtravs Nov 4, 2026
923b851
Merge branch 'fix/check-waste' into fix/host-crossing
jtravs Nov 4, 2026
a2b2354
Merge branch 'fix/host-crossing' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
facf2b2
Merge branch 'fix/host-crossing' into chore/native-primary
jtravs Nov 4, 2026
e64ff4e
Accept SEP 44
jtravs Nov 4, 2026
3f90836
Merge chore/native-primary: the edge's hook is declared, with no sche…
jtravs Nov 4, 2026
8ac7d3e
Merge the accepted SEP 44, and state the omission bound in its releas…
jtravs Nov 4, 2026
15ff393
SEP 44 in progress: report phases 1 and 2
jtravs Nov 4, 2026
5782bb9
Read a requested collar's strip and current natively
jtravs Nov 4, 2026
f59cc31
Merge remote-tracking branch 'origin/refactor/record-simplification' …
jtravs Nov 4, 2026
1939ac3
Redraw the run figure for kernel schema 47
jtravs Nov 4, 2026
5a80bbe
Merge branch 'fix/check-waste' into fix/host-crossing
jtravs Nov 4, 2026
939d95f
Merge fix/host-crossing, with the run figure redrawn
jtravs Nov 4, 2026
7971931
Hold a collar's read to the rest of SEP 44's probes
jtravs Nov 4, 2026
12982e1
Merge branch 'chore/native-primary' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
c07bbfc
Reduce a separable fluence and a plane's widths natively
jtravs Nov 4, 2026
8d3a219
Read the on-axis row natively, not the whole state
jtravs Nov 4, 2026
05acfa6
Sum a species' ionisation inventory without a Python loop
jtravs Nov 4, 2026
8d7f856
Poll a run's stop flag before each native Adams call
jtravs Nov 4, 2026
908005c
Hold a native Adams call's bound to moving no number
jtravs Nov 4, 2026
c209150
Take the held plane off the seam figure
jtravs Nov 4, 2026
c078a4e
Merge branch 'chore/native-primary' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
53b6bd9
Count page faults only where the platform can, in the device benchmark
jtravs Nov 4, 2026
f770734
Merge branch 'fix/check-waste' into fix/host-crossing
jtravs Nov 4, 2026
43ddefa
Merge branch 'fix/host-crossing' into chore/native-primary
jtravs Nov 4, 2026
ece75dd
Merge branch 'fix/host-crossing' into fix/setup-cost
jtravs Nov 4, 2026
90314b8
Say what a collar's read does now, and test it in C++ against a plain…
jtravs Nov 4, 2026
2ac217f
Merge branch 'chore/native-primary' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
9c1d908
Give the one-plane recipe the soliton its test names
jtravs Nov 4, 2026
3b9037c
Ledger: a native Adams call's return and the stop flag
jtravs Nov 4, 2026
c8a2f0b
Read on-axis intensity's spectra from the far-field slots
jtravs Nov 4, 2026
d57e3de
Ledger: say only what the return's table measured
jtravs Nov 4, 2026
17b1a87
Build the field-grid test's cases by their own constructors
jtravs Nov 4, 2026
83adad5
Describe the widths' native route and time its parts
jtravs Nov 4, 2026
564e6c1
Merge remote-tracking branch 'origin/chore/native-primary' into fix/a…
jtravs Nov 4, 2026
cd8d41f
Record what a free-space point's setup costs now
jtravs Nov 4, 2026
b77ae78
Record the widths' cost, and hold the device's axis and fluence
jtravs Nov 4, 2026
e024b45
Type the width tests' pulse and geometry
jtravs Nov 4, 2026
5c11e20
Close what an independent read of the collar's reading found
jtravs Nov 4, 2026
dd7faa5
Merge remote-tracking branch 'origin/chore/native-primary' into featu…
jtravs Nov 4, 2026
0212bdf
Split a free-space envelope's collar in place on the state
jtravs Nov 4, 2026
96548da
Cover or drop the lines coverage found
jtravs Nov 4, 2026
9f39a83
Merge branch 'fix/host-crossing' into chore/native-primary
jtravs Nov 4, 2026
fb31e98
WIP: phase 3's report, and what coverage and the docs build found
jtravs Nov 4, 2026
b41a223
Merge branch 'chore/native-primary' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
ae82a2a
Take a device's split collar a whole state at a time
jtravs Nov 4, 2026
7551397
Report SEP 44's phase 3, with its measurements
jtravs Nov 4, 2026
73b6e1f
Hold a host read's estimate on a tiny route
jtravs Nov 4, 2026
dd4e696
Merge branch 'fix/host-crossing' into fix/setup-cost
jtravs Nov 4, 2026
308a0b7
Lighten a device's low-storage passes
jtravs Nov 4, 2026
f6fa227
Merge remote-tracking branch 'origin/refactor/record-simplification' …
jtravs Nov 4, 2026
c0ea6ad
Merge branch 'fix/check-waste' into fix/host-crossing
jtravs Nov 4, 2026
353148c
Merge branch 'fix/host-crossing' into chore/native-primary
jtravs Nov 4, 2026
a4f7504
Merge branch 'chore/native-primary' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
d4002c9
Merge remote-tracking branch 'origin/main' into fix/check-waste
jtravs Nov 4, 2026
1435fc0
Merge branch 'fix/check-waste' into fix/host-crossing
jtravs Nov 4, 2026
41bb755
Merge branch 'fix/host-crossing' into chore/native-primary
jtravs Nov 4, 2026
fa9d10f
Merge branch 'chore/native-primary' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
cdc5743
Merge branch 'fix/host-crossing' into fix/setup-cost
jtravs Nov 4, 2026
8455174
Merge branch 'chore/native-primary' into fix/adams-stop
jtravs Nov 4, 2026
fabeaf5
Merge branch 'chore/native-primary' into feature/native-statistics
jtravs Nov 4, 2026
aa67d1d
Hold what a fresh read found the widths' tests leaving open
jtravs Nov 4, 2026
a2f824a
Wrap a comment in the widths' tests
jtravs Nov 4, 2026
b0bcb2f
Say where the edge is read in the output guide and the kernel contract
jtravs Nov 4, 2026
4cc6af0
Give a launch's block workers their docstrings
jtravs Nov 4, 2026
3fa3778
Stop an Adams trial forming what it does not read
jtravs Nov 4, 2026
a042178
Take a device's accept pass in one reduction, through a pinned mirror
jtravs Nov 4, 2026
614e40d
Merge branch 'fix/adams-stop' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
264b25e
Merge branch 'fix/setup-cost' into feature/sep-0044-native-edge
jtravs Nov 4, 2026
d6816e2
Merge branch 'feature/native-statistics' into feature/sep-0044-native…
jtravs Nov 4, 2026
45ebe8c
Keep one agent's work in few pull requests
jtravs Nov 4, 2026
dc30f00
Hold the accept's new norm and a small flow loss to their own values
jtravs Nov 4, 2026
fe9cc3d
Name a test's pair list in full, for nvcc
jtravs Nov 4, 2026
cb2cb06
Keep a host's fold inlined beside the lossless branch
jtravs Nov 4, 2026
d922206
Keep this work's new names private, as the maintainer decided
jtravs Nov 4, 2026
cc4570a
Ledger: a device step beyond its transforms
jtravs Nov 4, 2026
cb5f423
Translate a fresh simulation for the budget equal to the default
jtravs Nov 4, 2026
1ac209c
Drop a docstring reference Sphinx cannot resolve
jtravs Nov 4, 2026
79f1f1d
Merge feature/march-traffic: a device step's kernel traffic
jtravs Nov 4, 2026
9a5d234
Hold a stated block on a device, and name chunk_budgeted in the seam
jtravs Nov 4, 2026
ea88dce
Answer what the native lint and tidy lanes found
jtravs Nov 5, 2026
eb926df
Keep an updated problem's transform by identity, and bound a point be…
jtravs Nov 5, 2026
b9605ee
Fit that to the assembler's branch limit
jtravs Nov 5, 2026
56146ab
Say when the crossing ring is really allocated
jtravs Nov 5, 2026
b48aeec
Add CallbackOutput, a handler built from a callable
jtravs Nov 5, 2026
bc922c6
Show a reducing scan in the running guide, and test that it runs
jtravs Nov 5, 2026
5f8b047
Catch a handler holding a plane across a step
jtravs Nov 5, 2026
b80225e
Merge branch 'feature/reducing-output' into feature/sep-0044-native-edge
jtravs Nov 5, 2026
636a5b2
Let the command's status say what the file says
jtravs Nov 5, 2026
6f82f97
Read a stopped run's file only if it wrote one
jtravs Nov 5, 2026
ccd701d
Keep a device's split collar at the node chunk, and draw the handler
jtravs Nov 5, 2026
911b09e
Leave a batch member's field where it is at a plane no one reads
jtravs Nov 5, 2026
8cd170e
Check a replaced input on threads, and fuzz columns on a shape's hard…
jtravs Nov 5, 2026
84f381c
Format the command's stop, and name the protocol's reference in full
jtravs Nov 5, 2026
05e3244
Cover the exact residual's corner, and the launch mask's way past its…
jtravs Nov 5, 2026
0e0d092
SEP 44: report phase 4, and enter both cards in the ledger
jtravs Nov 5, 2026
0e1c376
Fuzz only bins the load keeps
jtravs Nov 5, 2026
180b558
Keep a device's backend across a local scan's input-only points
jtravs Nov 5, 2026
7d60e8f
Load a kept backend in the place a new one is loaded
jtravs Nov 5, 2026
ec7b5ba
Answer what CI and the NVIDIA A40 found in the kept backend
jtravs Nov 5, 2026
9fd4cea
Ledger: the NVIDIA H200 at this branch's later commit
jtravs Nov 5, 2026
7c11633
Ledger: a whole scan on a kept backend, on the NVIDIA A40
jtravs Nov 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ The long form is [SEP 0](docs/dev/seps/sep-0000-process.md); the index is
starts and marked ready when the last commit sets `Final`; there are no per-phase pull
requests. CI runs on every push to it and need not be green while it is a draft; a push
that needs no run has `[skip ci]` in its head commit's message. The branch may start on
top of an open pull request's branch when pull requests are stacked behind CI. Within
each phase: `numpy_ref` first with its tests and manual page, then native CPU in
`native/`; `mlx` only where the SEP says so (the backend rules, below). A
top of another agent's open pull request, when the SEP needs that work first. Within
each phase: `numpy_ref` and native CPU in `native/`, in either order (ADR-068), with
tests and the manual page; `mlx` only where the SEP says so (the backend rules, below). A
backend without the feature refuses it by name, never falls back silently; `Final`
needs parity on `numpy_ref` and native CPU, or an exception written into the accepted
SEP.
Expand Down Expand Up @@ -259,7 +259,8 @@ to it; a function that departs from it says so in the first line of its docstrin
sentinel where raising is correct.
- Respect the layers (`architecture.md §3`): physics code never contains performance
tricks; backend code never contains physics knowledge; the kernel contract
(`kernel-contract.md`) is the only crossing.
(`kernel-contract.md`) and the protected driver surface (frozen in
`tests/backends/test_driver.py`) are the crossings.

## Environment and dependencies

Expand Down Expand Up @@ -317,13 +318,15 @@ no blanket `# noqa`). What is left to judgement:

The long form is [`backends.md`](docs/dev/backends.md).

- **Where features land** (ADR-061, ADR-064, `backends.md §10`). `numpy_ref` and `native`:
every new feature lands on both, and they come first for testing and development.
`mlx`: experimental; keep it runnable and correct, add nothing as part of other work. A
backend without a feature refuses it by name.
- The **NumPy reference backend is the oracle**. Every other backend is validated against
it (one linear application, one nonlinear evaluation, one step, eight steps, each
statistic, a full run) with tolerances derived from summation bounds and asserted `> 0`.
- **Where features land** (ADR-061, ADR-064, ADR-068, `backends.md §10`). `native` is the
primary backend. `numpy_ref` and `native`: every new feature lands on both, in either
order, and `native` may shape the contract. `mlx`: experimental; keep it runnable and
correct, add nothing as part of other work. A backend without a feature refuses it by
name.
- The **NumPy reference backend is the oracle** for numbers. Every other backend is
validated against it (one linear application, one nonlinear evaluation, one step, eight
steps, each statistic, a full run) with tolerances derived from summation bounds and
asserted `> 0`.
- Hot code lives only under `src/solprop/backends/`, `src/solprop/translate/` and
`native/`.
- **Native kernels** follow [`native/AGENTS.md`](native/AGENTS.md). The floating-point
Expand Down Expand Up @@ -353,11 +356,16 @@ The long form is [`backends.md`](docs/dev/backends.md).
tolerance. Adaptive work counts may differ; accounting for actual work is exact.
- **In-place and `out=`** only inside the backends, wrapped by the seam; every buffer is
listed by `memory_ledger()`, which is tested.
- Device arrays never cross the seam; backends take and return host arrays.
- **`KERNEL_SCHEMA_VERSION` versions the whole seam** (the `KernelProblem` fields, the
protected `StepDriver` surface and the stage bundles of `translate/stage.py`, frozen in
`tests/backends/test_driver.py`). Changing any of them bumps the version, rewrites the
literal in the same commit and adds a release-note fragment.
- Device arrays never cross the `Backend` protocol, whose methods take and return host
arrays. Between `StepDriver` and its subclass the state is an opaque handle (an MLX
array, a native state), and the shared code reads it only through `_observed_field`,
which copies it to the host.
- **The seam has no version number** (ADR-068). A change to the `KernelProblem` fields,
the protected `StepDriver` surface or the stage bundles of `translate/stage.py` rewrites
the frozen tables in `tests/backends/test_driver.py` in the same commit and adds a
release-note fragment. solprop and solprop-native are released together and pinned to
one version, and an editable checkout's extension is compared with its `native/` sources
by digest (`check_build`).
- Every hot-path change comes with a benchmark ledger entry
([`benchmarks.md`](docs/dev/benchmarks.md)), measured with `python -m benchmarks ab`
on about ten steps or a short `ab --length` window; report the window measured and
Expand Down Expand Up @@ -471,6 +479,10 @@ The long form is [`testing.md`](docs/dev/testing.md).
commits rather than one large one. Stage deliberately with `git add <paths>`.
- Changes reach `main` as pull requests with green CI, or by a local merge when the
maintainers ask for one.
- **Few pull requests, few pushes.** CI runs its whole matrix on every push to every pull
request, so keep one agent's work in one pull request, or a few, rather than one per
step. Stack only on another agent's open pull request, when your work needs it before it
lands. Commit as often as you like and push in batches.
- **Before opening an issue or a pull request,** read [`CONTRIBUTING.md`](CONTRIBUTING.md):
search the issues, the pull requests and the SEPs, link what is related, and follow its
sections and labels.
Expand Down
292 changes: 292 additions & 0 deletions benchmarks/device_split.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
# SPDX-FileCopyrightText: 2026 John C. Travers and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later

"""Split a field-free point of a Kerr scan: the march, the check, the crossings.

One input of a delay scan through :class:`solprop.driver.SimulationRunner`, with no
fields kept: a fused-silica Kerr beam at 260 nm on fixed low-storage RK4IP steps, with
and a stated input identity. It runs with ``boundary_check=None`` and
with the default check, budgets relaxed so that nothing stops, and profiles one point
of each. Then it times what a plane can ask of a device: a state's copy to the host, an
observation and the common statistics.

Usage::

uv run python -m benchmarks.device_split NX [--steps 8] [--planes 4] [--out FILE]

It prints one JSON object per section (``record``, ``setup``, ``point`` twice,
``calls``), and writes them to ``FILE`` too when given. ``point`` names each function's
calls, own and cumulative seconds, and ``calls`` each crossing's minor page faults.
The profiler inflates Python-heavy work, so compare ``walls_s``, which it doesn't run
under. ``calls`` also splits a width: its native moments, and what the same azimuthal
average takes on the host. The native backend is required, and a device build is what
it's for; on a CPU build the crossings are plain copies.
"""

from __future__ import annotations

import argparse
import cProfile
import json
import platform
import pstats
import statistics
import subprocess
import sys
import time
import warnings
from pathlib import Path
from typing import Any

import numpy as np

from solprop.constants import EPS0, MU0, C
from solprop.driver import Simulation, SimulationSettings
from solprop.grid import EnvelopeGrid, FreeGrid
from solprop.monitor import BoundaryCheck
from solprop.nonlinear.responses import KerrResponse
from solprop.physdata.glasses import SellmeierGlass
from solprop.pulses import GaussianPulse, SpatialPulse
from solprop.spatial import SpatialMedium, cartesian_fluence_widths
from solprop.stats import StatsSpec, default_spec
from solprop.stepper import FixedSchedule, LowStorageRungeKutta, PIController

__all__ = ["main"]

STEP = 0.5e-6
"""The fixed step, m."""

NAMED = (
"_read_transverse",
"_edge_reading",
"node_power",
"column_power",
"_spacing",
"_observed_field",
"_boundary_energies",
"_take_step_stats",
"_monitor_plane",
"configuration_hash",
"_hashed_record",
"_check_replaced_input",
"attach_monitor",
"replay_step",
"c_einsum",
"field_to_host",
)
"""The functions a point's profile reports by name."""

GLASS = SellmeierGlass(
name="malitson",
source="Malitson 1965",
wavelength_range=(0.21e-6, 3.71e-6),
citation="Malitson, JOSA 55, 1205 (1965), doi:10.1364/JOSA.55.001205",
poles=((0.6961663, 0.0684043), (0.4079426, 0.1162414), (0.8974794, 9.896161)),
)


def _shell(command: str) -> str:
return subprocess.run(
command, shell=True, capture_output=True, text=True, check=False
).stdout.strip()


def _simulation(
nx: int, steps: int, planes: int, check: BoundaryCheck | None
) -> Simulation:
length = STEP * steps
half = 183e-6 * nx / 384 * 1.1
grid = EnvelopeGrid.from_limits(
length, 260e-9, (143e-9, 600e-9), 200e-15, thg=False, fft_size="smooth"
)
positions = [length * (index + 1) / planes for index in range(planes)]
settings = SimulationSettings(
spatial_absorber=False,
controller=PIController(rtol=1e-6, atol=1e-14),
save_planes=positions,
dz_max=length,
dz_init=STEP,
schedule=FixedSchedule.uniform(0.0, length, STEP, save_planes=positions),
integrator=LowStorageRungeKutta(),
backend="native",
boundary_check=check,
fields="none",
stats=StatsSpec(names=("energy",)),
)
return Simulation.from_free(
grid,
FreeGrid(half, nx, half, nx, collar_fraction=0.0),
SpatialMedium(GLASS),
[KerrResponse(chi3=2.2e-22 * MU0 * EPS0 * C**2, thg=False)],
SpatialPulse(GaussianPulse(wavelength=260e-9, fwhm=30e-15, energy=1e-7), 40e-6),
settings=settings,
)


def _profile_summary(profile: cProfile.Profile, top: int = 30) -> dict[str, Any]:
rows, named = [], {}
# `Stats.stats` is pstats' documented table, untyped in its stubs.
table: dict[Any, Any] = pstats.Stats(profile).stats # pyright: ignore[reportAttributeAccessIssue]
for (path, line, function), (_, calls, own, cumulative, _) in table.items():
where = f"{function}@{Path(path).name}:{line}"
rows.append((own, cumulative, calls, where))
if any(name in function for name in NAMED):
named[where] = [calls, round(own, 3), round(cumulative, 3)]
rows.sort(reverse=True)
return {
"named": named,
"top_own": [[round(a, 3), round(b, 3), c, d] for a, b, c, d in rows[:top]],
}


def _minor_faults() -> int:
"""Return this process's minor page faults so far: none are counted on Windows.

Returns
-------
int
The count.
"""
if sys.platform == "win32":
return 0
import resource # noqa: PLC0415 - not on Windows

return resource.getrusage(resource.RUSAGE_SELF).ru_minflt


def _timed(call: Any, repeats: int) -> dict[str, float]:
times, faults = [], 0
for _ in range(repeats):
before = _minor_faults()
started = time.perf_counter()
value = call()
times.append(time.perf_counter() - started)
faults = _minor_faults() - before
del value
return {
"median_ms": round(1e3 * statistics.median(times), 2),
"first_ms": round(1e3 * times[0], 2),
"minor_faults": faults,
}


def _calls(backend: Any) -> dict[str, Any]:
specs = {
name: StatsSpec(names=(name,))
for name in (
"energy",
"beam_spectrum",
"absorbed_power",
"on_axis_intensity",
"peak_intensity",
"beam_radius",
"fwhm_r",
)
}
specs["default"] = default_spec("free")
geometry = backend._problem.transform.geometry
x, y = geometry.axes
state = backend._native_state
return {
"crossings": {
"field_to_host": _timed(state.field_to_host, 3),
"observe_field": _timed(lambda: backend.observe(field=True), 3),
"observe_free": _timed(lambda: backend.observe(field=False), 3),
},
"statistics": {
name: _timed(lambda spec=spec: backend.stats(spec), 5)
for name, spec in specs.items()
},
# A width's parts: the fluence and its moments, one native call; and what the
# host's azimuthal average of a Gaussian's fluence takes on this machine.
"widths": {
"moments": _timed(
lambda: state.spatial_statistics(32, 0.5 * EPS0 * C, backend._z), 5
),
"host_average": _timed(
lambda: cartesian_fluence_widths(
x,
y,
np.exp(-2 * (x[None, :] ** 2 + y[:, None] ** 2) / 40e-6**2).ravel(),
),
3,
),
},
}


def main(argv: list[str] | None = None) -> None:
"""Run the split and print its sections.

Parameters
----------
argv : list of str or None, optional
The arguments, by default the process's.
"""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("nx", type=int, help="transverse samples per axis")
parser.add_argument("--steps", type=int, default=8)
parser.add_argument("--planes", type=int, default=4)
parser.add_argument("--out", type=Path, default=None)
arguments = parser.parse_args(argv)
warnings.simplefilter("ignore")
sink = arguments.out.open("w", encoding="utf-8") if arguments.out else None

def emit(section: str, **values: Any) -> None:
line = json.dumps({"section": section, "nx": arguments.nx, **values})
sys.stdout.write(line + "\n")
sys.stdout.flush()
if sink is not None:
sink.write(line + "\n")
sink.flush()

import solprop_native # noqa: PLC0415 # pyright: ignore[reportMissingImports]

emit(
"record",
cpu=_shell("lscpu | grep 'Model name' | cut -d: -f2 | xargs"),
cores=_shell("nproc"),
uptime=_shell("uptime"),
machine=platform.machine(),
transparent_hugepages=_shell("cat /sys/kernel/mm/transparent_hugepage/enabled"),
commit=_shell("git rev-parse --short HEAD"),
device=solprop_native.build_info().device_backend,
)
started = time.perf_counter()
base = _simulation(arguments.nx, arguments.steps, arguments.planes, None)
built = time.perf_counter()
problem = base.translate()
translated = time.perf_counter()
launch = np.array(problem.initial.field0)[0]
emit(
"setup",
from_free_s=round(built - started, 2),
translate_s=round(translated - built, 2),
field_GiB=round(launch.nbytes / 2**30, 3),
shape=list(launch.shape),
)
relaxed = BoundaryCheck(budget=1.0, transverse_budget=1.0)
for name, check in (("off", None), ("on", relaxed)):
simulation = _simulation(arguments.nx, arguments.steps, arguments.planes, check)
with simulation.runner() as runner:
walls = []
for point in range(2):
field = launch * np.exp(1j * point)
started = time.perf_counter()
runner.run(field, progress="none", input_identity=f"point-{point}")
walls.append(round(time.perf_counter() - started, 3))
field = launch * np.exp(0.5j)
profile = cProfile.Profile()
profile.enable()
runner.run(field, progress="none", input_identity="profiled")
profile.disable()
emit("point", check=name, walls_s=walls, **_profile_summary(profile))
if check is None:
emit("calls", **_calls(runner.backend))
if sink is not None:
sink.close()


if __name__ == "__main__":
main()
Loading