wave opcimage → cimageimport fullseye as fs; fs.ledger.angular_spectrum_propagate(field, wavelength_um=0.55, distance_um=100.0, pixel_pitch_um=1.0) (実装を直接呼ぶなら import optics; optics.angular_spectrum_propagate(field, wavelength_um=0.55, distance_um=100.0, pixel_pitch_um=1.0)、台帳から引くなら opsoptics.get("angular_spectrum_propagate"))Exact scalar free-space propagation of a complex field (angular spectrum).
U(z) = IFFT{ FFT{U(0)} * exp(i*2*pi*z*sqrt(1/lambda^2 - fx^2 - fy^2)) }
in the exp(-i*omega*t) convention, so a positive distance_um
propagates forward. Components beyond the propagating cone
(fx^2 + fy^2 > 1/lambda^2) are attenuated by
exp(-2*pi*|z|*sqrt(fx^2 + fy^2 - 1/lambda^2)), which is the physical
evanescent decay — not zeroed, so distance_um = 0 is an exact
identity and the transfer function is continuous through it.
Unlike Fresnel propagation this makes no paraxial approximation: it is the exact solution of the Helmholtz equation for a band-limited field, valid from a fraction of a wavelength outward.
Returns a complex128 array with the same shape as field.
Ground truth it reproduces (measured): distance_um = 0 returns the field
bit-identically (it short-circuits the transform pair); propagating +z
then -z returns the original to a relative L2 error of 4.3e-16 to
5.3e-16 for a band-limited field (measured on three: 64x64 random at
+/-50 um, a 64x64 Gaussian at +/-250 um, a 128x128 random at +/-500 um);
total power is conserved to between 0 and 3.5e-16 relative on the same
three. A field with
evanescent content does not round-trip — those components are gone by
construction, in both directions, because that is what physically happens.
field is a field in the space domain, not a spectrum: do not hand it
the fftshifted output of :func:complexops.cx_fft. Real input is promoted
to complex, which loses nothing.
Aliasing: the discrete transfer function is periodic, so a field that
diffracts past the array edge wraps around. The practical guard is the
usual one — pad the field so the propagated support stays inside, and keep
pixel_pitch_um below lambda/(2*NA). No warning can detect this
reliably from the array alone, so none is invented.
Raises ValueError: field is not 2-D, smaller than 2x2, larger than
:data:MAX_FIELD_ELEMENTS, masked, or non-finite; non-positive or
non-finite wavelength_um / pixel_pitch_um; non-finite distance_um.
optics の全 op は入力を検証してから計算する(黙って通さない):
_mm / _um / _deg / _mrad。mm と µm の取り違えは crash ではなく「もっともらしく間違った答え」なので、名前で防ぐ。大きさから単位を推測する処理は一切しない。ValueError — float('50') は成功してしまうため、未パースの設定値が長さとして通り抜ける(実測: thin_lens('50', '200') がもっともらしい 66.667 mm を返していた)。bool も True == 1 の暗黙昇格として拒否。ValueError(実数枠のみ。虚部の無言切り捨て・マスク剥がしを拒否)。NaN/Inf は全入力で ValueError。depth_of_field の過焦点距離以遠の far_mm = inf(それが過焦点距離の定義)と gaussian_beam のウエストでの wavefront_radius_mm = inf(平面波面の曲率半径)。どちらも有限の相棒(far_is_infinite / curvature_per_mm)を併せて返す。それ以外の無言 NaN/Inf は内部で検出して ValueError —「float64 が溢れた」と「答えが無限大」は別の主張なので、後者の顔で前者を返さない。optics.MAX_GRID(4096)、供給された場/PSF/開口は optics.MAX_FIELD_ELEMENTS(2^24)、ABCD 素子列は optics.MAX_SYSTEM_ELEMENTS(1024)、Zernike は MAX_ZERNIKE_TERMS(512)/ MAX_ZERNIKE_ORDER(40)/ MAX_ZERNIKE_BASIS(2^25)。小さな引数から巨大な内部確保が起きる経路(実測: n_max=40 × 4096² で 108 GB)を fail-closed で塞ぐ。| 物理的に不可能な状態も拒否: 偏光度 > 1 の Stokes ベクトル、負の透過率、負の強度、n- | m | が奇数などの不正な Zernike 添字。 |
py -3.11 examples/optics_imaging.pycimage を入力に取れる)wave)airy_pattern · fraunhofer_pattern · gaussian_beam · defocus_from_shift · pupil_psf · pupil_blur
Provenance: optics.py — OPTICS operator registry. この per-op ノートは tools/opdocs.py md が自動生成(手編集しない)。
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.