begin comment John Walker's Floating Point Benchmark, derived from... Marinchip Interactive Lens Design System By John Walker http://www.fourmilab.ch/ This program may be used, distributed, and modified freely as long as the origin information is preserved. This is a complete optical design raytracing algorithm, stripped of its user interface and recast into ALGOL 60. It not only determines execution speed on an extremely floating point (including trig function) intensive real-world application, it checks accuracy on an algorithm that is exquisitely sensitive to errors. The performance of this program is typically far more sensitive to changes in the efficiency of the trigonometric library routines than the average floating point program. Ported from the Algol 68 language implementation in May 2014 by John Walker. ; comment Variables and constants global to all procedures. ; integer a line, b line, c line, d line, e line, f line, g prime line, h line; real array spectral line[1:8]; integer current surfaces; real array test case[1:4, 1:4]; integer curvature radius, index of refraction, dispersion, edge thickness; real clear aperture, aberr lspher, aberr osc, aberr lchrom, max lspher, max osc, max lchrom, radius of curvature, object distance, ray height, axis slope angle, from index, to index; integer number of iterations, iteration; integer paraxial, marginal ray, paraxial ray; comment The od sa array holds the result from the main trace of paraxial and marginal rays in D light. The first subscript indicates whether the ray is marginal or paraxial and the second selects the object distance or axis slope angle. ; integer od field, sa field; real array od sa[1:2, 1:2]; real od cline, od fline; comment The cot function defined in terms of sin and cos ; real procedure cot(x); value x; real x; begin cot := 1 / (sin(x) / cos(x)) end cot; comment The arc sin function defined in terms of arc tan ; real procedure arc sin(x); value x; real x; begin arc sin := 2 * arc tan(x / (1 + sqrt(1 - x^2))) end arc sin; comment The built-in procedure outreal() uses the equivalent of a C format "%.12g", which does not provide sufficient precision for the largest numbers we edit. This brutal hack gets around this limitation by defining an outbigreal() procedure which uses the marst inline() mechanism to print its argument with a format of "%.11f" as used by other implementations of the benchmark. This is only needed, and only used, when printing the object distance for the marginal and paraxial rays in D light. If you have trouble with this, just replace the body of this procedure with: outreal(channel, x) but then you'll have to manually verify these values are rounded correctly. Usually, I would avoid using a compiler-specific feature like this in a benchmark, but since ALGOL 60 has no I/O facilities defined in the standard, they differ among almost all compilers, so any working version of the program is necessarily non-portable without modifying the I/O. ; procedure outbigreal(channel, x); value channel, x; integer channel; real x; begin real fraction; fraction := x; inline("printf(\"%.11f\", dsa_1->fraction_10);") end outbigreal; comment Calculate passage through surface If the variable paraxial is paraxial ray, the trace through the surface will be done using the paraxial approximations. Otherwise, the normal trigonometric trace will be done. This procedure takes the following global inputs: radius of curvature Radius of curvature of surface being crossed. If 0, surface is plane. object distance Distance of object focus from lens vertex. If 0, incoming rays are parallel and the following must be specified: ray height Height of ray from axis. Only relevant if object distance = 0 axis slope angle Angle incoming ray makes with axis at intercept from index Refractive index of medium being left to index Refractive index of medium being entered. The outputs are the following global variables: object distance Distance from vertex to object focus after refraction. axis slope angle Angle incoming ray makes with axis at intercept after refraction. ; procedure transit surface; begin real iang, rang, iang sin, rang sin, old axis slope angle, sagitta; if paraxial = paraxial ray then begin if radius of curvature != 0 then begin if object distance = 0 then begin axis slope angle := 0; iang sin := ray height / radius of curvature end else iang sin := ((object distance - radius of curvature) / radius of curvature) * axis slope angle; rang sin := (from index / to index) * iang sin; old axis slope angle := axis slope angle; axis slope angle := axis slope angle + iang sin - rang sin; if object distance != 0 then ray height := object distance * old axis slope angle; object distance := ray height / axis slope angle end else begin object distance := object distance * (to index / from index); axis slope angle := axis slope angle * (from index / to index) end end else begin if radius of curvature != 0 then begin if object distance = 0 then begin axis slope angle := 0; iang sin := ray height / radius of curvature end else iang sin := ((object distance - radius of curvature) / radius of curvature) * sin(axis slope angle); iang := arc sin(iang sin); rang sin := (from index / to index) * iang sin; old axis slope angle := axis slope angle; axis slope angle := axis slope angle + iang - arcsin(rang sin); sagitta := sin((old axis slope angle + iang) / 2); sagitta := 2 * radius of curvature * (sagitta ^ 2); object distance := ((radius of curvature * sin(old axis slope angle + iang)) * cot(axis slope angle)) + sagitta end else begin rang := -arcsin((from index / to index) * sin(axis slope angle)); object distance := object distance * ((to index * cos(-rang)) / (from index * cos(axis slope angle))); axis slope angle := -rang end end end transit surface; comment Perform ray trace for a given design for a specific spectral line and ray height. The caller specifies the desired spectral line and ray height. The global object distance is updated based upon tracing this ray. ; procedure trace line(line, ray h); value line, ray h; integer line; real ray h; begin integer i; object distance := 0; ray height := ray h; from index := 1; for i := 1 step 1 until current surfaces do begin radius of curvature := test case[i, curvature radius]; to index := test case[i, index of refraction]; if to index > 1 then to index := to index + ((spectral line[d line] - spectral line[line]) / (spectral line[c line] - spectral line[f line])) * ((test case[i, index of refraction] - 1.0) / test case[i, dispersion]); transit surface; from index := to index; if i < current surfaces then object distance := object distance - test case[i, edge thickness] end end trace line; comment END GLOBAL DECLARATONS ; comment Spectral lines ; a line := 1; b line := 2; c line := 3; d line := 4; e line := 5; f line := 6; g prime line := 7; h line := 8; spectral line[a line] := 7621.0; spectral line[b line] := 6869.955; spectral line[c line] := 6562.8160; spectral line[d line] := 5895.944; spectral line[e line] := 5269.557; spectral line[f line] := 4861.344; spectral line[g prime line] := 4340.477; spectral line[h line] := 3968.494; comment Initialise the test case array The test case used in this program is the design for a 4 inch f/12 achromatic telescope objective used as the example in Wyld's classic work on ray tracing by hand, given in Amateur Telescope Making, Volume 3 (Volume 2 in the 1996 reprint edition). ; current surfaces := 4; curvature radius := 1; index of refraction := 2; dispersion := 3; edge thickness := 4; test case[1, curvature radius] := 27.05; test case[1, index of refraction] := 1.5137; test case[1, dispersion] := 63.6; test case[1, edge thickness] := 0.52; test case[2, curvature radius] := -16.68; test case[2, index of refraction] := 1.0; test case[2, dispersion] := 0.0; test case[2, edge thickness] := 0.138; test case[3, curvature radius] := -16.68; test case[3, index of refraction] := 1.6164; test case[3, dispersion] := 36.7; test case[3, edge thickness] := 0.38; test case[4, curvature radius] := -78.1; test case[4, index of refraction] := 1.0; test case[4, dispersion] := 0.0; test case[4, edge thickness] := 0.0; marginal ray := 1; paraxial ray := 2; od field := 1; sa field := 2; comment number of iterations:= 40263820; number of iterations:= 1; clear aperture := 4; for iteration := 1 step 1 until number of iterations do begin for paraxial:= marginal ray step 1 until paraxial ray do begin trace line(d line, clear aperture / 2); od sa[paraxial, od field] := object distance; od sa[paraxial, sa field] := axis slope angle end; comment Trace marginal ray in C ; paraxial := marginal ray; trace line(c line, clear aperture / 2); od cline := object distance; comment Trace marginal ray in F ; trace line(f line, clear aperture / 2); od fline := object distance; comment Compute aberrations of the design The longitudinal spherical aberration is just the difference between where the D line comes to focus for paraxial and marginal rays. ; aberr lspher := od sa[paraxial ray, od field] - od sa[marginal ray, od field]; comment The offense against the sine condition is a measure of the degree of coma in the design. We compute it as the lateral distance in the focal plane between where a paraxial ray and marginal ray in the D line come to focus. ; aberr osc := 1 - ((od sa[paraxial ray, od field] * od sa[paraxial ray, sa field]) / (sin(od sa[marginal ray, sa field]) * od sa[marginal ray, od field])); comment The axial chromatic aberration is the distance between where marginal rays in the C and F lines come to focus. ; aberr lchrom := od fline - od cline; comment Compute maximum acceptable values for each aberration Maximum longitudinal spherical aberration, which is also the maximum for axial chromatic aberration. This is computed for the D line. ; max lspher := 0.0000926 / sin(od sa[marginal ray, sa field]) ^ 2; max lchrom := max lspher; max osc := 0.0025 end; comment Print the analysis of the ray trace ; outstring(1, " Marginal ray "); outbigreal(1, od sa[marginal ray, od field]); outstring(1, " "); outreal(1, od sa[marginal ray, sa field]); outstring(1, "\n"); outstring(1, " Paraxial ray "); outbigreal(1, od sa[paraxial ray, od field]); outstring(1, " "); outreal(1, od sa[paraxial ray, sa field]); outstring(1, "\n"); outstring(1, "Longitudinal spherical aberration: "); outreal(1, aberr lspher); outstring(1, "\n"); outstring(1, " (Maximum permissible): "); outreal(1, max lspher); outstring(1, "\n"); outstring(1, "Offense against sine condition (coma): "); outreal(1, aberr osc); outstring(1, "\n"); outstring(1, " (Maximum permissible): "); outreal(1, max osc); outstring(1, "\n"); outstring(1, "Axial chromatic aberration: "); outreal(1, aberr lchrom); outstring(1, "\n"); outstring(1, " (Maximum permissible): "); outreal(1, max lchrom); outstring(1, "\n") end