defocus_from_shift — OPTICS wave op

• Data kinds: कोई नहीं → measurement (केवल arguments से तय होने वाला op — यह कोई image या data input नहीं लेता)

• Call: import fullseye as fs; fs.ledger.defocus_from_shift(shift_um=10.0, wavelength_um=0.55, f_number=5.6) (implementation को सीधे call करना हो तो import optics; optics.defocus_from_shift(shift_um=10.0, wavelength_um=0.55, f_number=5.6), registry से लेना हो तो opsoptics.get("defocus_from_shift"))

उपयोग

axial focus shift का defocus wavefront error (pupil के किनारे पर waves)।

> नीचे का विस्तृत विवरण मूल text में है — सारांश और headings का अनुवाद हो चुका है।

Moving the detector (or, equivalently, the focus) by shift_um along the axis of an f/N beam adds the quadratic wavefront error W(rho) = W20 * rho^2 with

W20 = shift / (8 * lambda * N^2) [waves]

— the paraxial Seidel defocus term, rho the normalised pupil radius (1 at the edge). This is the number pupil_psf takes as defocus_waves, so the two compose: a longitudinal chromatic aberration (focal shift versus wavelength, e.g. from raytrace.chromatic_shift or a published df/f(lambda)) becomes a per-band defocus_waves here and a per-band PSF there.

Returns a float (a measurement). The sign is the sign of *shift_um*:

positive = the detector sits beyond the focus (the beam has converged and is diverging again). For a symmetric pupil the PSF does not depend on the sign; for an asymmetric one (a slit, a W, an off-axis hole) the sign flips the PSF through the centre — that is exactly the handle a one-photoreceptor eye can read the direction of defocus from.

Ground truth (closed form, tests/test_optics.py): shift = 8 lambda N^2 is exactly one wave; the function is linear in *shift_um* and inverse in *wavelength_um* and in N^2 (checked at two of each). At N = 1.5, lambda = 0.55 um (a cephalopod-scale f/1.5 eye) a 250 um focus shift is 25.3 waves.

Raises ValueError: non-finite *shift_um*; non-positive or non-finite *wavelength_um* / *f_number*.

Paraxial: W20 = shift/(8 N^2) is the small-angle expansion of the exact shift * (1 - cos theta) sag; at f/1.5 (sin theta = 1/3) the exact edge value is 5.7 % below the paraxial one — the number is a *defocus convention*, not a high-NA wavefront.

पूरी family का input contract (fail-closed)

optics के सभी ops गणना से पहले input की जाँच करते हैं (कुछ भी चुपचाप पास नहीं होता):

• Unit argument के नाम में ही लिखी होती है — _mm / _um / _deg / _mrad। mm और µm की गड़बड़ी crash नहीं करती, बल्कि "सही दिखने वाला पर गलत उत्तर" देती है, इसलिए नाम से इसे रोका जाता है। मान के आकार से unit का अनुमान बिल्कुल नहीं लगाया जाता।

• String से ValueError — float('50') सफल हो जाता है, इसलिए बिना parse किया config value length के रूप में निकल जाता (मापा गया: thin_lens('50', '200') सही दिखने वाला 66.667 mm लौटा रहा था)। bool को भी True == 1 वाले implicit promotion के रूप में अस्वीकार किया जाता है।

• complex / masked array से ValueError (केवल real-valued slots; imaginary part को चुपचाप छोड़ना या mask हटाना अस्वीकार)। हर input पर NaN/Inf से ValueError।

• शून्य से भाग और उससे जुड़े मामलों को नाम लेकर अस्वीकार किया जाता है: focal length 0, radius of curvature 0, refractive index <= 0, पूरी तरह opaque aperture (सब 0, इसलिए normalisation 0/0), ऐसा PSF जिसका योग <= 0 हो, S0 = 0 वाला Stokes vector, और front focal point पर रखा object (image अनंत पर)।

• केवल 2 ops non-finite value लौटाते हैं, और वह भी contract के रूप में लिखा हुआ: depth_of_field hyperfocal distance से आगे far_mm = inf (hyperfocal distance की परिभाषा यही है) और gaussian_beam waist पर wavefront_radius_mm = inf (plane wavefront की radius of curvature)। दोनों साथ में एक finite साथी (far_is_infinite / curvature_per_mm) भी लौटाते हैं। इसके अलावा कोई भी चुपचाप आया NaN/Inf अंदर ही पकड़ा जाता है और ValueError देता है — "float64 overflow हुआ" और "उत्तर अनंत है" अलग-अलग दावे हैं, इसलिए पहले को दूसरे का चेहरा पहनाकर नहीं लौटाया जाता।

• Size की सीमा: generate होने वाले grid के लिए optics.MAX_GRID (4096), दिए गए field/PSF/aperture के लिए optics.MAX_FIELD_ELEMENTS (2^24), ABCD element chain के लिए optics.MAX_SYSTEM_ELEMENTS (1024), Zernike के लिए MAX_ZERNIKE_TERMS (512) / MAX_ZERNIKE_ORDER (40) / MAX_ZERNIKE_BASIS (2^25)। छोटे argument से बहुत बड़ा internal allocation होने वाले रास्तों (मापा गया: n_max=40 × 4096² पर 108 GB) को fail-closed ढंग से बंद किया गया है।

• भौतिक रूप से असंभव states भी अस्वीकार: degree of polarisation > 1 वाला Stokes vector, ऋणात्मक transmittance, ऋणात्मक intensity, और n-|m| odd जैसे अमान्य Zernike indices।

उपयोग की विस्तृत guide

• optics_imaging family guide

संदर्भ (sample data, साहित्य)

• Sample data catalog (DL URL / license) — 2-D के लिए skimage.data (BSD/public) + synthetic, 3-D के लिए real data sources (Stanford/PDS आदि) के DL URL।

• Operators का स्रोत और संदर्भ — इस op family के मूल research/methods के स्रोत।

• Algorithm का मानक स्रोत (लेखक, वर्ष) और उपयोग ऊपर की family usage guide में दिए गए हैं।

चलाए जा सकने वाले उदाहरण (verified samples जो सच में यह op call करते हैं)

• optics_imaging — py -3.11 examples/optics_imaging.py

वे अगले ops जिनसे type जुड़ता है (measurement को input के रूप में लेते हैं)

—

इसी category के (wave)

airy_pattern · angular_spectrum_propagate · fraunhofer_pattern · fourier_plane_filter · four_f_filter · gaussian_beam · pupil_psf · pupil_blur


*Provenance: optics.py — OPTICS operator registry. यह per-op note tools/opdocs.py md से अपने-आप generate होता है (हाथ से edit न करें)।*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.