scan of worm works now
[woropt.git] / frontend / merit.lisp
blob2736c95fbdf1651a451b5e12367fc9d064c09aa4
1 (in-package :frontend)
3 ;; it turns out that using a few points on the border of the circles
4 ;; is too sparse. the following scheme should sample the space a bit
5 ;; better and therefore give a better approximation of the exposure
6 ;; integral
7 ;;
8 ;; ---------
9 ;; ---/ \--- xy-cross section through a
10 ;; / \ nucleus in the sample plane
11 ;; -/ \-
12 ;; / \ choose s points along a line
13 ;; | | .
14 ;; / \ .
15 ;; 0 1 2 ...
16 ;; \ /
17 ;; | |
18 ;; \ /
19 ;; -\ /-
20 ;; \ /
21 ;; ---\ /-- -----------
22 ;; --------- ---/ \---
23 ;; -/ \-
24 ;; -/ back focal \-
25 ;; / plane \
26 ;; / \
27 ;; | |
28 ;; choose / \
29 ;; b points .... a b c d e f
30 ;; along a lin \ /
31 ;; with the same | |
32 ;; inclination theta \ /
33 ;; \ /
34 ;; -\ /-
35 ;; -\ /-
36 ;; ---\ /---
37 ;; -----------
39 ;; now shoot rays from each o the points in the bfp to each of the
40 ;; points in the sample plane. we have to make sure that a nucleus
41 ;; inside the illumination cone convolved with the cross section of
42 ;; the illuminated nucleus is hit by at least one ray. the necessary
43 ;; number of points s and b depends on the size of the nuclei as well
44 ;; as the furthest distance to the border of the stack. for now i
45 ;; don't want to think too hard about that. i guess it will be
46 ;; sufficient to sample with .01 in the back focal plane and 3 points
47 ;; in the nucleus.
48 ;; two dimensional coordinates will be represented as complex numbers.
50 (defun sample-line (nr-ffp nr-bfp)
51 "Create two lists like -1 0 1 and -1 -.5 0 .5 1. Here nr-ffp would
52 be 3 and nr-bfp would be 5. The result is the outer product -1 -1, -1
53 -.5, -1, 0, ..., a list with all interconnections. Note that the pair
54 0,0 isn't emitted as it would result in duplications when turned by
55 theta."
56 (declare ((integer 2) nr-ffp nr-bfp))
57 (let ((ffps (loop for i below nr-ffp collect
58 (complex (- (/ (* 2d0 i) (1- nr-ffp)) 1))))
59 (bfps (loop for i below nr-bfp collect
60 (complex (- (/ (* 2d0 i) (1- nr-bfp)) 1))))
61 (result nil))
62 (loop for f in ffps do
63 (loop for b in bfps do
64 ;; prevent duplication of central ray
65 (unless (= (complex 0d0) f b)
66 (push (list f b) result))))
67 (nreverse result)))
69 #+nil
70 (sample-line 2 2)
72 (defun sample-circles (nr-ffp nr-bfp nr-theta)
73 "Create coordinates in front and backfocal plane that sample circles
74 in a regular pattern."
75 (declare ((integer 2) nr-ffp nr-bfp)
76 ((integer 1) nr-theta)
77 (values cons &optional))
78 (let ((line (sample-line nr-ffp nr-bfp))
79 (result nil))
80 (loop for theta below nr-theta do
81 (let ((rotator (exp (complex 0d0 (/ (* pi theta) nr-theta)))))
82 (loop for (f b) in line do
83 (push (list (* rotator f)
84 (* rotator b))
85 result))))
86 (when (and (oddp nr-ffp) (oddp nr-bfp)) ;; central ray was omitted above
87 (push (list (complex 0d0) (complex 0d0)) result))
88 (nreverse result)))
90 #+nil
91 (sample-circles 2 2 1)
93 ;; ------+------
94 ;; ---/ | \---
95 ;; --/ |---+--- \--
96 ;; -/ -| | \- \-
97 ;; / / | z | \ \
98 ;; -/ / | | r \ \-
99 ;; / |y +---+------+ \
100 ;; / \ | | / \
101 ;; | \ | | / |
102 ;; / -| | /- \
103 ;; | |---+--- |
104 ;; -+--------------------+---+----------------+--
105 ;; | | x rr |
106 ;; \ | /
108 (defun move-complex-circle (z rr x/rr y/rr r/rr)
109 "Given a complex number Z inside the unit circle, move the unit
110 circle to position X,Y inside the BFP with radius RR. Scale the unit
111 circle to the window-radius R."
112 (declare ((complex double-float) z)
113 ((double-float -1d0 1d0) x/rr y/rr)
114 ((double-float 0d0 1d0) r/rr)
115 (double-float rr)
116 (values (complex double-float) &optional))
117 (+ (complex (* x/rr rr) (* y/rr rr))
118 (* r/rr rr z)))
120 #+nil
121 (move-complex-circle (complex 1d0 0d0) 2d0 .9d0 0d0 .1d0)
123 #+nil
124 (move-complex-circle (complex 1d0 0d0) 1d0 .9d0 0d0 .1d0)
126 #+nil
127 (move-complex-circle (complex 1d0) 3.601d0 0d0 0d0 .1d0)
129 (defmethod make-rays ((objective lens::objective) (model sphere-model)
130 nucleus positions win-x/r win-y/r win-r/r)
131 "Given an objective and a nucleus in a model generate rays from a
132 circle on the back focal plane into the front focal plane. The pattern
133 of the rays is given as a list of 2-lists of complex numbers. The
134 first complex number gives the relative position inside the central
135 cross section of the nucleus and the second number gives the relative
136 position in the bfp. The coordinates and size of the window in the
137 back focal plane are given relative to the radius of the bfp. The
138 return value is a list of 2-lists of rays, where the first ray starts
139 from the principal sphere and the second ray from the bfp."
140 (declare (fixnum nucleus)
141 (cons positions)
142 (double-float win-x/r win-y/r win-r/r)
143 (values (or null cons) &optional))
144 (assert (subtypep (type-of (first (first positions)))
145 '(complex double-float)))
146 (assert (subtypep (type-of (second (first positions)))
147 '(complex double-float)))
148 (with-slots (centers-mm radii-mm) model
149 (let ((center (elt centers-mm nucleus))
150 (radius (elt radii-mm nucleus))
151 (result nil))
152 (with-slots ((bfp-radius lens::bfp-radius)
153 (ri lens::immersion-index)
154 (f lens::focal-length)) objective
155 (loop for (f b) in positions do
156 (let ((br (move-complex-circle b 1d0
157 win-x/r win-y/r win-r/r))
158 (fr (move-complex-circle f 1d0 (vec-x center) (vec-y center)
159 radius)))
160 (handler-case
161 (multiple-value-bind (exit enter)
162 (lens:get-ray-behind-objective
163 objective
164 (realpart fr) (imagpart fr)
165 (realpart br) (imagpart br))
166 (push (list exit enter) result))
167 (ray-lost () nil))))
168 (nreverse result)))))
170 #+nil
171 (defparameter *look*
172 (loop for (exit enter) in (make-rays (lens:make-objective :center (v 0 0 1))
173 *model* 0
174 (sample-circles 2 2 1)
175 .0d0 0d0 .1d0)
176 collect
177 (vector::start enter)))
179 (defun merit-function (vec2 params &key (border-value 100d0))
180 "Vec2 contains the center of the window in th bfp. Params must be a
181 list containing objective model nucleus-index window-radius
182 positions (positions is a list of 2-lists of complex
183 numbers). BORDER-VALUE has to be bigger than then the maximum of
184 integrals in the back focal plane. It will be returned when the beam
185 wanders outside of the bfp."
186 (declare ((simple-array double-float (2)) vec2)
187 (cons params)
188 (values double-float &optional))
189 (destructuring-bind (objective model nucleus-index
190 window-radius positions)
191 params
192 (let* ((border-width window-radius) ;; in this range to the
193 ;; border of the bfp
194 ;; enforce bad merit
195 ;; function
196 (sum 0d0)
197 (radius (norm2 vec2)))
198 (if (< radius (- .99d0 border-width))
199 ;; inside
200 (let* ((rays (make-rays objective model nucleus-index
201 positions (vec2-x vec2)
202 (vec2-y vec2) window-radius)))
203 (unless rays
204 (return-from merit-function border-value))
205 (let ((s (/ 1d0 (length rays))))
206 (loop for (exit enter) in rays do
207 (incf sum
208 (* s (raytrace:ray-spheres-intersection
209 exit model nucleus-index))))))
210 ;; in the border-width or outside of bfp
211 (incf sum border-value))
212 sum)))
214 #+nil ;; call merit function for one window center position
215 (let* ((obj (lens:make-objective :center (v) :normal (v 0 0 1)))
216 (window-radius .1d0)
217 (positions (sample-circles 3 10 12))
218 (z-plane-mm (vec-z (elt (raytrace::centers-mm *model*) 0))))
219 (with-slots ((c lens::center)
220 (ri lens::immersion-index)
221 (f lens::focal-length)) obj
222 (setf c (make-vec 0d0 0d0 (+ (- (* ri f)) z-plane-mm)))
223 (let* ((params (list obj *model* 0 window-radius positions)))
224 (merit-function (make-vec2 :x .4d0 :y .4d0)
225 params))))
227 #+nil ;; store the scan for different bfp window sizes
228 (time
229 (let* ((n 100)
230 (nn 6 #+nil (length (centers *model*)))
231 (mosaicx (ceiling (sqrt nn)))
232 (mosaic (make-array (list (* n mosaicx) (* n mosaicx))
233 :element-type 'double-float))
234 (obj (lens:make-objective :center (v) :normal (v 0 0 1)))
235 (nucleus 0)
236 (positions (sample-circles 3 7 5)))
237 (dotimes (nuc nn)
238 (with-slots ((c lens::center)
239 (ri lens::immersion-index)
240 (f lens::focal-length)) obj
241 (let* ((window-radius (* nuc (/ .30d0 nn)))
242 (z-plane-mm (vec-z (elt (raytrace::centers-mm *model*) nucleus)))
243 (vals nil))
244 (setf c (make-vec 0d0 0d0 (+ (- (* ri f)) z-plane-mm)))
245 (let* ((params (list obj *model* nucleus window-radius positions))
246 (px (* n (mod nuc mosaicx)))
247 (py (* n (floor nuc mosaicx))))
248 (do-region ((j i) (n n))
249 (let* ((x (- (* 2d0 (/ i n)) 1d0))
250 (y (- (* 2d0 (/ j n)) 1d0))
251 (v (merit-function (make-vec2 :x x :y y)
252 params
253 :border-value 0d0)))
254 (setf (aref mosaic (+ py j) (+ px i)) v)
255 (unless (= v 0d0) (push v vals)))))
256 (format t "min ~2,6f max ~2,6f win-r ~2,3f~%"
257 (reduce #'min vals)
258 (reduce #'max vals)
259 window-radius))))
260 (write-pgm "/home/martin/tmp/scan-mosaic.pgm" (normalize-2-df/ub8 mosaic))))
262 (defun find-optimal-bfp-window-center (nucleus params)
263 (declare (fixnum nucleus)
264 (cons params)
265 (values vec2 &optional))
266 (setf (elt params 2) nucleus)
267 (loop
268 (multiple-value-bind (min point)
269 (simplex-anneal:anneal (simplex-anneal:make-simplex
270 (make-vec2 :x -.4d0 :y -.4d0) .3d0)
271 #'merit-function
272 ;; set temperature bigger than the
273 ;; maxima in the bfp but smaller
274 ;; than border-value
275 :start-temperature .04d0
276 :cooling-steps 30
277 :eps/m .001d0 ;; high eps/m cools faster
278 :itmax 100 ;; steps per temperature
279 :ftol 10d-3
280 :params params)
281 (when (< min 100d0)
282 (return-from find-optimal-bfp-window-center point)))))
284 #+nil
285 (time
286 (let* ((n 30)
287 (nn 5 #+nil (length (centers *model*)))
288 (mosaicx (ceiling (sqrt nn)))
289 (mosaic (make-array (list (* n mosaicx) (* n mosaicx))
290 :element-type 'double-float))
291 (obj (lens:make-objective :center (v) :normal (v 0 0 1)))
292 (positions (sample-circles 3 7 5))
293 (scan 0)
294 (nucleus 0))
295 (with-open-file (*standard-output* "/home/martin/tmp/scan-min.dat"
296 :direction :output
297 :if-exists :supersede)
298 (with-slots ((c lens::center)
299 (ri lens::immersion-index)
300 (f lens::focal-length)) obj
301 (let* ((window-radius .08d0 #+nil (* nuc (/ .20d0 nn)))
302 (z-plane-mm (vec-z (elt (raytrace::centers-mm *model*) nucleus))))
303 (setf c (make-vec 0d0 0d0 (+ (- (* ri f)) z-plane-mm)))
304 (let* ((params (list obj *model* nucleus window-radius positions))
305 (px (* n (mod scan mosaicx)))
306 (py (* n (floor scan mosaicx))))
307 (find-optimal-bfp-window-center nucleus params)
308 #+nil (format t "~a~%"
309 )))))
310 #+nil (write-pgm "/home/martin/tmp/scan-mosaic.pgm"
311 (normalize-2-df/ub8 mosaic))))