75
75
# classic work on ray tracing by hand, given in Amateur Telescope
76
76
# Making, Volume 3, p597.
77
77
78
- my @ testcase = (
78
+ my $ testcase = pdl (
79
79
# radius of curvature, refractive index, Abbe number, axial distance
80
80
[ 27.05, 1.5137, 63.6, 0.52 ], # crown
81
81
[ -16.68, 1, 0, 0.138 ],
82
82
[ -16.68, 1.6164, 36.7, 0.38 ], # flint
83
83
[ -78.1, 1, 0, 0 ]
84
84
);
85
85
86
- # Perform ray trace in specific spectral line
87
- sub trace_line {
88
- my ($paraxial , $line , $ray_h ) = @_ ;
89
- $object_distance .= 0;
90
- $ray_height .= $ray_h ;
91
- $from_index .= 1;
92
- for (@s ) {
93
- ($radius_of_curvature , $to_index , my $abbe_number , my $axial_distance ) = map PDL::Core::topdl($_ ), @$_ ;
94
- if ($to_index > 1) {
95
- $to_index += (
96
- ($spectral_line -> slice(" (4)" ) - $spectral_line -> slice(" ($line )" )) /
97
- ($spectral_line -> slice(" (3)" ) - $spectral_line -> slice(" (6)" ))
98
- ) *
99
- (($to_index - 1) / $abbe_number );
100
- }
101
- $paraxial ? transit_surface_paraxial() : transit_surface();
102
- $from_index .= $to_index ;
103
- $object_distance -= $axial_distance ;
86
+ use Inline Pdlpp => <<'EOF' ;
87
+ pp_def('trace_line',
88
+ Pars => '
89
+ surface(elt=4,s); spectral_lines(l); int paraxial(); indx line(); ray_height();
90
+ [o] object_distance(s); [o] slope_angle(s);',
91
+ GenericTypes => ['D'],
92
+ Code => <<'EOCODE',
93
+ /*
94
+ Inputs:
95
+ radius_of_curvature Radius of curvature of surface
96
+ being crossed. If 0, surface is plane.
97
+ object_distance Distance of object focus from
98
+ lens vertex. If 0, incoming rays are parallel and
99
+ the following must be specified:
100
+ ray_height Height of ray from axis. Only
101
+ relevant if $object_distance == 0
102
+ axis_slope_angle Angle incoming ray makes with axis at intercept
103
+ from_index Refractive index of medium being left
104
+ to_index Refractive index of medium being entered.
105
+ Outputs:
106
+ object_distance Distance from vertex to object focus after refraction.
107
+ axis_slope_angle Angle incoming ray makes with axis
108
+ at intercept after refraction.
109
+ */
110
+ $GENERIC() o_d = 0, s_a, r_h = $ray_height(), from_index = 1;
111
+ loop(s) %{
112
+ /* Perform ray trace in specific spectral line */
113
+ $GENERIC() radius_of_curvature = $surface(elt=>0, s=>s),
114
+ to_index = $surface(elt=>1, s=>s),
115
+ abbe_number = $surface(elt=>2, s=>s),
116
+ axial_distance = $surface(elt=>3, s=>s);
117
+ PDL_Indx which_line = $line();
118
+ if (to_index > 1) {
119
+ to_index += (
120
+ ($spectral_lines(l=>4) - $spectral_lines(l=>which_line)) /
121
+ ($spectral_lines(l=>3) - $spectral_lines(l=>6))
122
+ ) *
123
+ ((to_index - 1) / abbe_number);
104
124
}
105
- }
106
-
107
- # Calculate passage through surface using the paraxial approximations.
108
- #
109
- # This subroutine takes the following global inputs:
110
- #
111
- # $radius_of_curvature Radius of curvature of surface
112
- # being crossed. If 0, surface is
113
- # plane.
114
- #
115
- # $object_distance Distance of object focus from
116
- # lens vertex. If 0, incoming
117
- # rays are parallel and
118
- # the following must be specified:
119
- #
120
- # $ray_height Height of ray from axis. Only
121
- # relevant if $object_distance == 0
122
- #
123
- # $axis_slope_angle Angle incoming ray makes with axis
124
- # at intercept
125
- #
126
- # $from_index Refractive index of medium being left
127
- #
128
- # $to_index Refractive index of medium being
129
- # entered.
130
- #
131
- # The outputs are the following global variables:
132
- #
133
- # $object_distance Distance from vertex to object focus
134
- # after refraction.
135
- #
136
- # $axis_slope_angle Angle incoming ray makes with axis
137
- # at intercept after refraction.
138
- sub transit_surface_paraxial {
139
- my ($iang_sin , # Incidence angle sin
140
- $rang_sin , # Refraction angle sin
141
- );
142
- if ($radius_of_curvature != 0) {
143
- if ($object_distance == 0) {
144
- $axis_slope_angle .= 0;
145
- $iang_sin = $ray_height / $radius_of_curvature ;
125
+ $GENERIC() iang_sin /* Incidence angle sin */,
126
+ rang_sin /* Refraction angle sin */;
127
+ if ($paraxial()) {
128
+ if (radius_of_curvature != 0) {
129
+ if (o_d == 0) {
130
+ s_a = 0;
131
+ iang_sin = r_h / radius_of_curvature;
132
+ } else {
133
+ iang_sin = ((o_d -
134
+ radius_of_curvature) / radius_of_curvature) *
135
+ s_a;
136
+ }
137
+ rang_sin = (from_index / to_index) *
138
+ iang_sin;
139
+ $GENERIC() old_axis_slope_angle = s_a;
140
+ s_a += iang_sin - rang_sin;
141
+ if (o_d != 0)
142
+ r_h = o_d * old_axis_slope_angle;
143
+ o_d = r_h / s_a;
146
144
} else {
147
- $iang_sin = (($object_distance -
148
- $radius_of_curvature ) / $radius_of_curvature ) *
149
- $axis_slope_angle ;
150
- }
151
- $rang_sin = ($from_index / $to_index ) *
152
- $iang_sin ;
153
- my $old_axis_slope_angle = $axis_slope_angle -> copy;
154
- $axis_slope_angle .= $axis_slope_angle +
155
- $iang_sin - $rang_sin ;
156
- if ($object_distance != 0) {
157
- $ray_height .= $object_distance * $old_axis_slope_angle ;
145
+ o_d *= to_index / from_index;
146
+ s_a *= from_index / to_index;
158
147
}
159
- $object_distance .= $ray_height / $axis_slope_angle ;
160
- return ;
161
- }
162
- $object_distance *= ($to_index / $from_index );
163
- $axis_slope_angle *= ($from_index / $to_index );
164
- }
165
-
166
- # Calculate passage through surface using normal trigonometric trace
167
- # inputs and output same as paraxial version above
168
- sub transit_surface {
169
- my ($iang_sin , # Incidence angle sin
170
- $rang_sin , # Refraction angle sin
171
- );
172
- if ($radius_of_curvature != 0) {
173
- if ($object_distance == 0) {
174
- $axis_slope_angle .= 0;
175
- $iang_sin = $ray_height / $radius_of_curvature ;
176
- } else {
177
- $iang_sin = (($object_distance -
178
- $radius_of_curvature ) / $radius_of_curvature ) *
179
- sin ($axis_slope_angle );
180
- }
181
- my $iang = asin($iang_sin ); # Incidence angle
182
- $rang_sin = ($from_index / $to_index ) *
183
- $iang_sin ;
184
- my $old_axis_slope_angle = $axis_slope_angle -> copy;
185
- $axis_slope_angle += $iang - asin($rang_sin );
186
- my $sagitta = sin (($old_axis_slope_angle + $iang ) / 2);
187
- $sagitta .= 2 * $radius_of_curvature * $sagitta * $sagitta ;
188
- $object_distance .= (($radius_of_curvature * sin (
189
- $old_axis_slope_angle + $iang )) /
190
- tan($axis_slope_angle )) + $sagitta ;
191
- return ;
148
+ } else {
149
+ if (radius_of_curvature != 0) {
150
+ if (o_d == 0) {
151
+ s_a = 0;
152
+ iang_sin = r_h / radius_of_curvature;
153
+ } else {
154
+ iang_sin = ((o_d -
155
+ radius_of_curvature) / radius_of_curvature) *
156
+ sin(s_a);
192
157
}
193
- my $rang = -asin(($from_index / $to_index ) *
194
- sin ($axis_slope_angle )); # Refraction angle
195
- $object_distance *= (($to_index *
196
- cos (-$rang )) / ($from_index *
197
- cos ($axis_slope_angle )));
198
- $axis_slope_angle .= -$rang ;
199
- }
158
+ $GENERIC() iang = asin(iang_sin); /* Incidence angle */
159
+ rang_sin = (from_index / to_index) *
160
+ iang_sin;
161
+ $GENERIC() old_axis_slope_angle = s_a;
162
+ s_a += iang - asin(rang_sin);
163
+ $GENERIC() sagitta = sin((old_axis_slope_angle + iang) / 2);
164
+ sagitta = 2 * radius_of_curvature * sagitta * sagitta;
165
+ o_d = ((radius_of_curvature * sin(
166
+ old_axis_slope_angle + iang)) /
167
+ tan(s_a)) + sagitta;
168
+ } else {
169
+ $GENERIC() rang = -asin((from_index / to_index) *
170
+ sin(s_a)); /* Refraction angle */
171
+ o_d *= ((to_index *
172
+ cos(-rang)) / (from_index *
173
+ cos(s_a)));
174
+ s_a = -rang;
175
+ }
176
+ }
177
+ from_index = to_index;
178
+ o_d -= axial_distance;
179
+ $object_distance() = o_d;
180
+ $slope_angle() = s_a;
181
+ %}
182
+ EOCODE
183
+ );
184
+ EOF
200
185
201
186
# Process the number of iterations argument, if one is supplied.
202
187
@@ -221,7 +206,6 @@ sub transit_surface {
221
206
# Load test case into working array
222
207
223
208
my $clear_aperture = 4;
224
- @s = map [@$_ ], @testcase ; # one-level copy
225
209
226
210
my $nik = $niter / 1000;
227
211
print << "EOD" ;
@@ -241,34 +225,26 @@ sub transit_surface {
241
225
my ($od_fline , $od_cline );
242
226
243
227
for (my $itercount = 0; $itercount < $niter ; $itercount ++) {
244
-
245
- for my $paraxial (0, 1) {
246
- # Do main trace in D light
247
- trace_line($paraxial , 4, $clear_aperture / 2);
248
- $od_sa [$paraxial ] = [$object_distance -> copy, $axis_slope_angle -> copy];
249
- }
250
-
251
- # Trace marginal ray in C
252
-
253
- trace_line(0, 3, $clear_aperture / 2);
254
- $od_cline = $object_distance -> copy;
255
-
256
- # Trace marginal ray in F
257
-
258
- trace_line(0, 6, $clear_aperture / 2);
259
- $od_fline = $object_distance -> copy;
260
-
261
- $aberr_lspher = $od_sa [1][0] - $od_sa [0][0];
262
- $aberr_osc = 1 - ($od_sa [1][0] * $od_sa [1][1]) /
263
- (sin ($od_sa [0][1]) * $od_sa [0][0]);
264
- $aberr_lchrom = $od_fline - $od_cline ;
265
- $max_lspher = sin ($od_sa [0][1]);
266
-
267
- # D light
268
-
269
- $max_lspher = 0.0000926 / ($max_lspher * $max_lspher );
270
- $max_osc = 0.0025;
271
- $max_lchrom = $max_lspher ;
228
+ my @inputs = (
229
+ $testcase ,
230
+ $spectral_line ,
231
+ pdl(0, 1, 0, 0), # paraxial
232
+ pdl(4, 4, 3, 6), # spectral line - main trace in D light, marginal in C,F
233
+ pdl($clear_aperture / 2), # ray height, threads so no need to repeat
234
+ );
235
+ my ($od , $sa ) = PDL::trace_line(@inputs );
236
+ my $pdl_od_sa = pdl($od , $sa )-> slice(" (3)" )-> transpose; # slice as only last col is of interest
237
+ @od_sa = @{ $pdl_od_sa -> slice(" ,0:1" )-> unpdl };
238
+ ($od_cline , $od_fline ) = @{ $pdl_od_sa -> slice(" (0),2:3" )-> unpdl };
239
+ $aberr_lspher = $od_sa [1][0] - $od_sa [0][0];
240
+ $aberr_osc = 1 - ($od_sa [1][0] * $od_sa [1][1]) /
241
+ (sin ($od_sa [0][1]) * $od_sa [0][0]);
242
+ $aberr_lchrom = $od_fline - $od_cline ;
243
+ $max_lspher = sin ($od_sa [0][1]);
244
+ # D light
245
+ $max_lspher = 0.0000926 / ($max_lspher * $max_lspher );
246
+ $max_osc = 0.0025;
247
+ $max_lchrom = $max_lspher ;
272
248
}
273
249
274
250
my $interval = tv_interval(\@t );
0 commit comments