rename HklPseudoAxisEngine -> HklEngine
[hkl.git] / Documentation / hkl.texi
blob1b0af7a92598a066e0a653b5a3fcdcf96a21a02e
1 \input texinfo   @c -*-texinfo-*-
2 @comment %**start of header
3 @setfilename hkl.info
4 @include version.texi
5 @settitle Hkl Diffraction Library @value{VERSION}
6 @syncodeindex pg cp
7 @comment %**end of header
8 @copying
9 This manual is for hkl Library (version @value{VERSION}, @value{UPDATED}).
11 Copyright @copyright{} 2003-2010 Synchrotron SOLEIL
12                        L'Orme des Merisiers Saint-Aubin
13                        BP 48 91192 GIF-sur-YVETTE CEDEX
15 @quotation
16 The hkl library is free software: you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
21 The hkl library is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with the hkl library.  If not, see <http://www.gnu.org/licenses/>.
28 @end quotation
29 @end copying
31 @dircategory Software libraries
32 @direntry
33 * hkl: (hkl).           Library for hkl diffraction computation.
34 @end direntry
36 @titlepage
37 @title hkl Library
38 @subtitle for version @value{VERSION}, @value{UPDATED}
39 @author F-E. Picca (@email{picca@@synchrotorn-soleil.fr})
40 @page
41 @vskip 0pt plus 1filll
42 @insertcopying
43 @end titlepage
45 @contents
47 @ifnottex
48 @node Top, Introduction, (dir), (dir)
49 @top hkl Diffraction Library
51 This manual is for Hkl Diffraction Library (version @value{VERSION}, @value{UPDATED}).
52 @end ifnottex
54 @menu
55 * Introduction::                
56 * Diffractometer::              
57 * Developpement::               
58 * Index::                       
60 @detailmenu
61  --- The Detailed Node Listing ---
63 Diffractometer
65 * Eulerian 4 circles::          
66 * Eulerian 6 circles::          
67 * Kappa 4 circles vertical::    
68 * Kappa 6 circles::             
69 * Z-axis::                      
70 * SOLEIL SIXS MED2+2::          
72 @end detailmenu
73 @end menu
75 @node Introduction, Diffractometer, Top, Top
76 @chapter Introduction
78 The purpose of the library is to factories diffraction angles computation for
79 different kind of diffractometers geometries. It is used at the SOLEIL, Desy
80 and Alba synchrotron with the Tango control system to pilot diffractometers.
82 @section Features
84 @itemize
85 @item mode computation (aka PseudoAxis)
86         @itemize
87         @item for different diffractometer geometries.
88         @end itemize
89 @item UB matrix computation.
90       @itemize
91         @item busing & Levy with 2 reflections
92         @item simplex computation with more than 2 reflections using the GSL library.
93         @item Eulerians angles to pre-orientate your sample.
94       @end itemize
95 @item Crystal lattice affinement
96       @itemize
97         @item with more than 2 reflections you can select which parameter must be fitted.
98       @end itemize
99 @item Pseudoaxes
100       @itemize
101         @item psi, eulerians, q, ...
102       @end itemize
103 @end itemize
105 @section Conventions.
107 In all this document the next convention will be used to describe the diffractometers
108 geometries.
109 @itemize
110 @item right handed convention for all the angles.
111 @item direct space orthogonal base.
112 @item description of the diffractometer geometries is done with all axes values set to zero.
113 @end itemize
115 @node Diffractometer, Developpement, Introduction, Top
116 @chapter Diffractometer
118 @menu
119 * Eulerian 4 circles::          
120 * Eulerian 6 circles::          
121 * Kappa 4 circles vertical::    
122 * Kappa 6 circles::             
123 * Z-axis::                      
124 * SOLEIL SIXS MED2+2::          
125 @end menu
127 @node Eulerian 4 circles, Eulerian 6 circles, Diffractometer, Diffractometer
128 @section Eulerian 4 circles
130 @subsection Geometries
132 @subsubsection Vertical
133 @itemize
134     @item xrays source fix allong the @math{\vec{x}} direction (1, 0, 0)
135     @item 3 axes for the sample
136           @itemize
137                 @item @samp{omega} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
138                 @item @samp{chi} : rotating around the @math{\vec{x}} direction (1, 0, 0)
139                 @item @samp{phi} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
140           @end itemize
141     @item 1 axis for the detector
142           @itemize
143                 @item @samp{tth} : rotation around the @math{-\vec{y}} direction (0, -1, 0)
144           @end itemize
145 @end itemize
147 @subsubsection Horizontal
149 @itemize
150     @item xrays source fix allong the @math{\vec{x}} direction (1, 0, 0)
151     @item 3 axes for the sample
152           @itemize
153                 @item @samp{omega} : rotating around the @math{\vec{z}} direction (0, 0, 1)
154                 @item @samp{chi} : rotating around the @math{\vec{x}} direction (1, 0, 0)
155                 @item @samp{phi} : rotating around the @math{\vec{z}} direction (0, 0, 1)
156           @end itemize
157     @item 1 axis for the detector
158           @itemize
159                 @item @samp{tth} : rotation around the @math{\vec{z}} direction (0, 0, 1)
160           @end itemize
161 @end itemize
163 @subsubsection Soleil Mars Beamline
165 @itemize
166     @item xrays source fix allong the @math{\vec{x}} direction (1, 0, 0)
167     @item 3 axes for the sample
168           @itemize
169                 @item @samp{omega} : rotating around the @math{\vec{z}} direction (0, -1, 0)
170                 @item @samp{chi} : rotating around the @math{\vec{x}} direction (-1, 0, 0)
171                 @item @samp{phi} : rotating around the @math{\vec{z}} direction (0, 0, 1)
172           @end itemize
173     @item 1 axis for the detector
174           @itemize
175                 @item @samp{tth} : rotation around the @math{\vec{z}} direction (0, -1, 0)
176           @end itemize
177 @end itemize
179 @subsection Pseudo axis @samp{hkl}
181 PseudoAxes provided : @samp{h}, @samp{k} and @samp{l}
183 @subsubsection mode @samp{bissector}
185 @itemize
186 @item Axes : @samp{omega}, @samp{chi}, @samp{phi}, @samp{tth}
187 @item Parameters : No parameter
188 @end itemize
190 This mode add the bissector constrain @code{tth = 2 * omega}. In this mode the @samp{chi}
191 circle containt the vector of diffusion @math{\vec{Q}}. So it is easy to know the orientation
192 of the hkl plan.
194 @subsubsection mode @samp{constant_omega}
196 @itemize
197 @item Axes : @samp{chi}, @samp{phi}, @samp{tth}
198 @item Parameters : No parameter
199 @end itemize
201 This mode do not move the current @samp{omega} axis.
203 @subsubsection mode @samp{constant_chi}
205 @itemize
206 @item Axes :  @samp{omega}, @samp{phi}, @samp{tth}
207 @item Parameters : No parameter
208 @end itemize
210 This mode do not move the current @samp{chi} axis.
212 @subsubsection mode @samp{constant_phi}
214 @itemize
215 @item Axes related : @samp{omega}, @samp{chi}, @samp{tth}
216 @item Parameters : No parameter
217 @end itemize
218 This mode do not move the current @samp{phi} axis.
220 @subsubsection mode @samp{double_diffraction}
222 @itemize
223 @item Axes : @samp{omega}, @samp{chi}, @samp{phi}, @samp{tth}
224 @item Parameters : @samp{h2}, @samp{k2}, @samp{l2}
225 @end itemize
227 This mode put a second hkl vector (@samp{h2}, @samp{k2}, @samp{l2}) in Bragg condition.
228 This is usefull sometimes when you want to explore two bragg peaks without moving your sample.
230 @subsubsection mode @samp{psi_constant}
232 @itemize
233 @item Axes :  @samp{omega}, @samp{chi}, @samp{phi}, @samp{tth}
234 @item Parameters : @samp{h2}, @samp{k2}, @samp{l2}, @samp{psi}
235 @end itemize
237 This mode allow to fix the value of the pseudo axis @samp{psi} at a constant value when you move
238 around an @samp{h}, @samp{k} ,@samp{l} position. The (@samp{h2}, @samp{k2}, @samp{l2}) vector is
239 used as a reference for the computation of the @samp{psi} pseudo axis value.
241 You can retrive and ``freeze'' the current value of the @samp{psi} pseudo axis value into the
242 @samp{psi} parameter when you initialize the mode. But you can also write directly the value
243 of the desired @samp{psi} parameter.
245 @subsection PseudoAxis @samp{psi}
247 PseudoAxis provided : @samp{psi}
249 @subsubsection mode @samp{psi}
251 @itemize
252 @item Axes : @samp{omega}, @samp{chi}, @samp{phi}, @samp{tth}
253 @item Parameters : @samp{h1}, @samp{k1},@samp{l1}
254 @end itemize
256 @node Eulerian 6 circles, Kappa 4 circles vertical, Eulerian 4 circles, Diffractometer
257 @section Eulerian 6 circles
259 @subsection Geometry
261 @itemize
262     @item xrays source fix allong the @math{\vec{x}} direction (1, 0, 0)
263     @item 4 axes for the sample
264           @itemize
265                 @item @samp{mu} : rotating around the @math{\vec{z}} direction (0, 0, 1)
266                 @item @samp{omega} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
267                 @item @samp{chi} : rotating around the @math{\vec{x}} direction (1, 0, 0)
268                 @item @samp{phi} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
269           @end itemize
270     @item 2 axes for the detector
271           @itemize
272                 @item @samp{gamma} : rotation around the @math{\vec{z}} direction (0, 0, 1)
273                 @item @samp{delta} : rotation around the @math{-\vec{y}} direction (0, -1, 0)
274           @end itemize
275 @end itemize
277 @subsection PseudoAxes
279 @node Kappa 4 circles vertical, Kappa 6 circles, Eulerian 6 circles, Diffractometer
280 @section Kappa 4 circles vertical
282 @subsection Geometry
284 For this geometry there is a special parameters called @math{\alpha} which is the
285 angle between the kappa rotation axis and the  @math{\vec{y}} direction.
287 @itemize
288     @item xrays source fix allong the @math{\vec{x}} direction (1, 0, 0)
289     @item 3 axes for the sample
290           @itemize
291                 @item @samp{komega} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
292                 @item @samp{kappa} : rotating around the @math{\vec{x}} direction (0, @math{-\cos\alpha}, @math{-\sin\alpha})
293                 @item @samp{kphi} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
294           @end itemize
295     @item 1 axis for the detector
296           @itemize
297                 @item @samp{tth} : rotation around the @math{-\vec{y}} direction (0, -1, 0)
298           @end itemize
299 @end itemize
301 @subsection PseudoAxes
303 @node Kappa 6 circles, Z-axis, Kappa 4 circles vertical, Diffractometer
304 @section Kappa 6 circles
306 @subsection Geometry
308 For this geometry there is a special parameters called @math{\alpha} which is the
309 angle between the kappa rotation axis and the  @math{\vec{y}} direction.
311 @itemize
312     @item xrays source fix allong the @math{\vec{x}} direction (1, 0, 0)
313     @item 4 axes for the sample
314           @itemize
315                 @item @samp{mu} : rotating around the @math{\vec{z}} direction (0, 0, 1)
316                 @item @samp{komega} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
317                 @item @samp{kappa} : rotating around the @math{\vec{x}} direction (0, @math{-\cos\alpha}, @math{-\sin\alpha})
318                 @item @samp{kphi} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
319           @end itemize
320     @item 2 axes for the detector
321           @itemize
322                 @item @samp{gamma} : rotation around the @math{\vec{z}} direction (0, 0, 1)
323                 @item @samp{delta} : rotation around the @math{-\vec{y}} direction (0, -1, 0)
324           @end itemize
325 @end itemize
327 @subsection PseudoAxes
329 @node Z-axis, SOLEIL SIXS MED2+2, Kappa 6 circles, Diffractometer
330 @section Z-Axis
332 @subsection Geometry
334 For this geometry the @samp{mu} axis is common to the sample and the detector.
336 @itemize
337     @item xrays source fix allong the @math{\vec{x}} direction (1, 0, 0)
338     @item 2 axes for the sample
339           @itemize
340                 @item @samp{mu} : rotation around the @math{\vec{z}} direction (0, 0, 1)
341                 @item @samp{omega} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
342           @end itemize
343     @item 3 axis for the detector
344           @itemize
345                 @item @samp{mu} : rotation around the @math{\vec{z}} direction (0, 0, 1)
346                 @item @samp{delta} : rotation around the @math{-\vec{y}} direction (0, -1, 0)
347                 @item @samp{gamma} : rotation around the @math{\vec{z}} direction (0, 0, 1)
348           @end itemize
349 @end itemize
351 @subsection PseudoAxes
353 PseudoAxes provided : @samp{h}, @samp{k} and @samp{l}
355 @subsubsection mode @samp{zaxis}
357 @itemize
358 @item Axes : @samp{omega}, @samp{delta}, @samp{gamma}
359 @item Parameters : No parameter
360 @end itemize
362 @subsubsection mode @samp{reflectivity}
364 @itemize
365 @item Axes : @samp{mu}, @samp{omega}, @samp{delta}, @samp{gamma}
366 @item Parameters : No parameter
367 @end itemize
369 This mode add the reflectivity constraint @code{mu = gamma}. The
370 incomming beam angle and the outgoing beam angle are equals.
372 @node SOLEIL SIXS MED2+2,  , Z-axis, Diffractometer
373 @section SOLEIL SIXS MED2+2
375 @subsection Geometry
377 @itemize
378     @item xrays source fix allong the @math{\vec{x}} direction (1, 0, 0)
379     @item 2 axes for the sample
380           @itemize
381                 @item @samp{mu} : rotation around the @math{\vec{z}} direction (0, 0, 1)
382                 @item @samp{omega} : rotating around the @math{-\vec{y}} direction (0, -1, 0)
383           @end itemize
384     @item 3 axis for the detector
385           @itemize
386                  @item @samp{gamma} : rotation around the @math{\vec{z}} direction (0, 0, 1)
387                 @item @samp{delta} : rotation around the @math{-\vec{y}} direction (0, -1, 0)
388           @end itemize
389 @end itemize
391 @subsection Pseudo axis @samp{hkl}
393 PseudoAxes provided : @samp{h}, @samp{k} and @samp{l}
395 @subsubsection mode @samp{mu_fixed}
397 @itemize
398 @item Axes : @samp{omega}, @samp{gamma}, @samp{delta}
399 @item Parameters : No parameter
400 @end itemize
402 @node Developpement, Index, Diffractometer, Top
403 @chapter Developpement
405 @section Getting hkl
407 To get hkl, you can download the last stable version from sourceforge or if you
408 want the latest development version use @uref{http://git.or.cz/, git} or
409 @uref{http://code.google.com/p/msysgit/downloads/list, msysgit} on windows system and
411 @example
412 $ git clone git://repo.or.cz/hkl.git
413 @end example
415 @example
416 $ git clone http://repo.or.cz/r/hkl.git (slower)
417 @end example
418 then checkout the next branch like this.
419 @example
420 $ cd hkl
421 $ git checkout -b next origin/next
422 @end example
424 @section Building hkl
426 To build hkl you need @uref{http://www.python.org, Python 2.3+} and the
427 @uref{http://www.gnu.org/software/gsl/, GNU Scientific Library 1.12+}
428 @example
429 $ ./waf configure
430 $ ./waf
431 $ ./waf install (as root)
432 @end example
434 This command compile the library and the test suit if everythings goes fine you
435 must have a @file{libhkl.so.@value{VERSION}} or @file{libhkl.lib} depending on your
436 platform in the @file{build/default/src} directory. If your platform is not supported yet please
437 contact the @email{picca@@synchrotron-soleil.fr}.
439 @section Hacking hkl
441 you can send your patch to the @email{picca@@synchrotron-soleil.fr} using
444 The developpement process is like this. suppose you wan to add a new feature to
445 hkl create first a new branch from the next one
446 @example
447 $ git checkout -b my-next next
448 @end example
449 then work...
450 @example
451 $ git commit -a
452 @end example
453 more work...
454 @example
455 $ git commit -a
456 @end example
457 now that your great feature is ready for publication, you can send by mail your
458 patches process like this:
459 @example
460 $ git format-patch origin/next
461 @end example
462 and send files @file{0001_xxx}  and @file{0002_xxx} created to the author.
464 @subsection Howto add a diffractometer
466 In this section we will describe all steps needed to add a diffractometer. We
467 will use the kappa 4 circles exemple.
469 @subsection Adding Geometry
471 The first thing to do is to add the Geometry of this diffractometer.  you need
472 to edit the @file{hkl/hkl-geometry.h} file
474 add a new @code{HKL_GEOMETRY_KAPPA4C_VERTICAL} const to the @code{_HklGeometryType}
476 @verbatim
477 enum _HklGeometryType
479         ...
480         HKL_GEOMETRY_KAPPA4C_VERTICAL
482 @end verbatim
484 Then you need to add it to the static hkl_geometry_factory_configs constant in the
485 @file{hkl/hkl-geometry-factory.h}
487 @verbatim
488 static const HklGeometryConfig hkl_geometry_factory_configs[] =
490         ...
491         {"K4CV", HKL_GEOMETRY_TYPE_KAPPA4C_VERTICAL},
493 @end verbatim
495 Now you must describe the diffractometer axes and the way they are connected
496 all togethers.  This diffractometer have one sample holder and one detecter
497 holder and four axes ("komega", "kappa", "kphi" and "tth") So you need to add a
498 new init method for this diffractometer.
500 @verbatim
501 static void hkl_geometry_init_kappa4C_vertical(HklGeometry *self, double alpha)
503         HklHolder *h;
505         self->name = "K4CV";
506         h = hkl_geometry_add_holder(self);
507         hkl_holder_add_rotation_axis(h, "komega", 0, -1, 0);
508         hkl_holder_add_rotation_axis(h, "kappa", 0, -cos(alpha), -sin(alpha));
509         hkl_holder_add_rotation_axis(h, "kphi", 0, -1, 0);
511         h = hkl_geometry_add_holder(self);
512         hkl_holder_add_rotation_axis(h, "tth", 0, -1, 0);
514 @end verbatim
516 first we set the diffractometer name by
519 @verbatim
520 self->name = "K4CV";
521 @end verbatim
523 This name is used in the Tango diffractometer device to refer this diffractometer.
525 Then you can create the first holder with it's three axes. The order of the axis is from
526 the farest to the closest of the sample. In this case, komega -> kappa -> kphi.
528 @verbatim
529 h = hkl_geometry_add_holder(self);
530 hkl_holder_add_rotation_axis(h, "komega", 0, -1, 0);
531 hkl_holder_add_rotation_axis(h, "kappa", 0, -cos(alpha), -sin(alpha));
532 hkl_holder_add_rotation_axis(h, "kphi", 0, -1, 0);
533 @end verbatim
535 Same thing for the other holder holding the detector.
537 @verbatim
538 h = hkl_geometry_add_holder(self);
539 hkl_holder_add_rotation_axis(h, "tth", 0, -1, 0);
540 @end verbatim
542 now it is almost finish for the geometry part. you just need to add it in the factory
544 @verbatim
545 Hklgeometry *hkl_geometry_factory_new(HklGeometryType type, ...)
547         ...
548         switch(type){
549                 ...
550                 case HKL_GEOMETRY_KAPPA4C_VERTICAL:
551                         va_start(ap, type);
552                         alpha = va_arg(ap, double);
553                         va_end(ap);
554                         hkl_geometry_init_kappa4C_vertical(geom, alpha);
555                 break;
556         }
557         ...
559 @end verbatim
561 in this exemple the geometry take one parameter. The fatory can have a variable
562 number of parameters you just need to take care of this with the va_arg
563 methods.
565 @subsection Adding PseudoAxis mode
567 Suppose you want to add a new mode to the hkl pseudo axes.
568 lets call it "psi constant vertical" to the eulerian 6 circle geometry.
570 The starting point is to look in the @file{src/hkl-pseudoaxis-factory.c} for
572 @verbatim
573 HklEngineList *hkl_engine_list_factory(HklGeometryType type)
574 @end verbatim
576 in that method you can see this in the eulerian 6 circle part
578 @verbatim
579 case HKL_GEOMETRY_EULERIAN6C:
580      hkl_engine_list_add(self, hkl_engine_e6c_hkl_new());
581      hkl_engine_list_add(self, hkl_engine_e6c_psi_new());
582      hkl_engine_list_add(self, hkl_engine_q2_new());
583      break;
584 @end verbatim
586 so as you can see there is three pseudo axis engine for this geometry. Your mode if for
587 the hkl pseudo axis. so let look in the @code{hkl_engine_e6c_hkl_new()} method.
588 You can find it in the @file{include/hkl/hkl-pseudoaxis-e6c.h} which contain this:
590 @verbatim
591 #ifndef __HKL_PSEUDOAXIS_E6C_H__
592 #define __HKL_PSEUDOAXIS_E6C_H__
594 #include <hkl/hkl-pseudoaxis-auto.h>
596 HKL_BEGIN_DECLS
598 extern HklEngine *hkl_engine_e6c_hkl_new(void);
599 extern HklEngine *hkl_engine_e6c_psi_new(void);
601 HKL_END_DECLS
603 #endif /* __HKL_PSEUDOAXIS_E6C_H__ */
604 @end verbatim
606 strange only 2 methods nothing about @code{hkl_engine_q2_new()}. This is because
607 the implementation of this method is common to more than one geometry. So you can find it in
608 @file{hkl/hkl-pseudoaxis-common-q.h}
610 now you need to change the code of @code{hkl_engine_e6c_hkl_new(void)}. Lets
611 look about it in @file{src/hkl-pseudoaxis-e6c-hkl.c}
613 @verbatim
614 HklEngine *hkl_engine_e6c_hkl_new(void)
616         HklEngine *self;
617         HklMode *mode;
619         self = hkl_engine_hkl_new();
621         /* bissector_vertical */
622         mode = hkl_mode_new(
623                 "bissector_vertical",
624                 NULL,
625                 hkl_mode_get_hkl_real,
626                 hkl_engine_setter_func_bissector_vertical,
627                 0,
628                 4, "omega", "chi", "phi", "delta");
629         hkl_engine_add_mode(self, mode);
631         /* constant_omega_vertical */
632         mode = hkl_mode_new(
633                 "constant_omega_vertical",
634                 NULL,
635                 hkl_mode_get_hkl_real,
636                 hkl_mode_set_hkl_real,
637                 0,
638                 3, "chi", "phi", "delta");
639         hkl_engine_add_mode(self, mode);
641         /* constant_chi_vertical */
642         mode = hkl_mode_new(
643                 "constant_chi_vertical",
644                 NULL,
645                 hkl_mode_get_hkl_real,
646                 hkl_mode_set_hkl_real,
647                 0,
648                 3, "omega", "phi", "delta");
649         hkl_engine_add_mode(self, mode);
651         /* constant_phi_vertical */
652         mode = hkl_mode_new(
653                 "constant_phi_vertical",
654                 NULL,
655                 hkl_mode_get_hkl_real,
656                 hkl_mode_set_hkl_real,
657                 0,
658                 3, "omega", "chi", "delta");
659         hkl_engine_add_mode(self, mode);
661         /* lifting_detector_phi */
662         mode = hkl_mode_new(
663                 "lifting_detector_phi",
664                 NULL,
665                 hkl_mode_get_hkl_real,
666                 hkl_mode_set_hkl_real,
667                 0,
668                 3, "phi", "gamma", "delta");
669         hkl_engine_add_mode(self, mode);
671         /* lifting_detector_omega */
672         mode = hkl_mode_new(
673                 "lifting_detector_omega",
674                 NULL,
675                 hkl_mode_get_hkl_real,
676                 hkl_mode_set_hkl_real,
677                 0,
678                 3, "omega", "gamma", "delta");
679         hkl_engine_add_mode(self, mode);
681         /* lifting_detector_mu */
682         mode = hkl_mode_new(
683                 "lifting_detector_mu",
684                 NULL,
685                 hkl_mode_get_hkl_real,
686                 hkl_mode_set_hkl_real,
687                 0,
688                 3, "mu", "gamma", "delta");
689         hkl_engine_add_mode(self, mode);
691         /* double_diffraction vertical*/
692         HklParameter h2;
693         HklParameter k2;
694         HklParameter l2;
696         hkl_parameter_init(&h2, "h2", -1, 1, 1,
697                            HKL_TRUE, HKL_TRUE,
698                            NULL, NULL);
699         hkl_parameter_init(&k2, "k2", -1, 1, 1,
700                            HKL_TRUE, HKL_TRUE,
701                            NULL, NULL);
702         hkl_parameter_init(&l2, "l2", -1, 1, 1,
703                            HKL_TRUE, HKL_TRUE,
704                            NULL, NULL);
706         mode = hkl_mode_new(
707                 "double_diffraction_vertical",
708                 NULL,
709                 hkl_mode_get_hkl_real,
710                 hkl_mode_set_double_diffraction_real,
711                 3, &h2, &k2, &l2,
712                 4, "omega", "chi", "phi", "delta");
713         hkl_engine_add_mode(self, mode);
715         /* bissector_horizontal */
716         mode = hkl_mode_new(
717                 "bissector_horizontal",
718                 NULL,
719                 hkl_mode_get_hkl_real,
720                 hkl_engine_setter_func_bissector_horizontal,
721                 0,
722                 5, "mu", "omega", "chi", "phi", "gamma");
723         hkl_engine_add_mode(self, mode);
725         /* double_diffraction_horizontal */
726         mode = hkl_mode_new(
727                 "double_diffraction_horizontal",
728                 NULL,
729                 hkl_mode_get_hkl_real,
730                 hkl_mode_set_double_diffraction_real,
731                 3, &h2, &k2, &l2,
732                 4, "mu", "chi", "phi", "gamma");
733         hkl_engine_add_mode(self, mode);
735         hkl_engine_select_mode(self, 0);
737         return self;
739 @end verbatim
741 so you "just" need to add a new mode like this
743 @verbatim
744         /* double_diffraction_horizontal */
745         mode = hkl_mode_new(
746                 "psi_constant_vertical",
747                 NULL,
748                 hkl_mode_get_hkl_real,
749                 hkl_mode_set_psi_constant_vertical,
750                 3, &h2, &k2, &l2,
751                 4, "omega", "chi", "phi", "delta");
752         hkl_engine_add_mode(self, mode);
753 @end verbatim
755 So the first parameter of the hkl_mode_new method
756 @itemize
757 @item name is the name of the mode
758 @item then the init functions (usually you need to store the current state of the geometry
759  to be able to use the pseudo axis). Here no need for this init method
760 so we put @code{NULL}.
761 @item then the get method which compute for a given geometry the pseudo axis value.
762 the hkl get method @code{hkl_mode_get_hkl_real} is completely generic
763 and do not depend of the geometry. No need to write it.
764 @item then the set method which compute a geometry for the given pseudo axis values.
765 Now you need to work a little bit and write the set method.
766 @item the parameters of your mode 
767 @item * first the number of parameters : 3
768 @item * then each parameters (pointer on the right parameters)
769 for this mode we have 3 parameters h2, k2, l2 which are the coordinates of a
770 sample reference direction use to compute the psi value.
771 @item the name of axes used by the set method.
772 @item * first the number of axes used by the set method : 4
773 @item * then all axes names.
774 @end itemize
776 In fact the "set" method know nothing about the axes names.
777 so you can use a set method with different kind of geometries.
778 the association is only done during the mode creation.
780 At the end you need to add this mode to the pseudo axis engine with
781 @code{hkl_engine_add_mode(self, mode)};
783 that's all.
785 Now let see how this "set" method could be written. In our case we want
786 to compute the geometry angles for a given h, k, l pseudo axis values keeping the
787 angle between the reference reciprocal space vector (h2, k2, l2) and the
788 diffraction plane defined by the incomming beam and the outgoing beam.
790 @verbatim
791 static int hkl_mode_set_psi_constant_vertical(HklEngine *engine,
792                                                                  HklGeometry *geometry,
793                                                                  HklDetector *detector,
794                                                                  HklSample *sample)
796         hkl_engine_prepare_internal(engine, geometry, detector,
797                                                 sample);
799         return hkl_engine_solve_function(engine, psi_constant_vertical);
801 @end verbatim
803 the prepare internal part is about initializing the solver with the given
804 geometry, detector and sample. Then comes the hkl_engine_solve_function
805 which need the psi_constant_vertical function to work. This method use the GSL library
806 to find the given function roots (where f(x) = 0).
807 Lets see how it works for the "bissector_horizontal" mode.
809 @verbatim
810 static int bissector_horizontal(const gsl_vector *x, void *params, gsl_vector *f)
812         double mu, omega, gamma;
813         double const *x_data = gsl_vector_const_ptr(x, 0);
814         double *f_data = gsl_vector_ptr(f, 0);
816         RUBh_minus_Q(x_data, params, f_data);
818         mu = x_data[0];
819         omega = x_data[1];
820         gamma = x_data[4];
822         f_data[3] = omega;
823         f_data[4] = gamma - 2 * fmod(mu, M_PI);
825         return  GSL_SUCCESS;
827 @end verbatim
829 The bissector_horizotal method is used by the setter method of the mode to
830 compute the right set of axes angles corresponding to the pseudo axes values
831 you want to reach. This method compute the difference between these pseudo axes
832 values and the ones computed from the axes angles. It can be decompose in three
833 parts:
835 The first three of these equations are given for the function @code{RUBH_minus_Q}:
836 they are the  diference between the h,k,l values that want to be set and the h,k,l
837 values computed for a possible combination of angles:
839 @example
840 f_data[0] = h-h(x)
841 f_data[1] = k-k(x)
842 f_data[2] = l-l(x)
843 @end example
845 As the bissector_horizontal mode use 5 axes you need to find 2 other
846 equations to be able to solve your mode. The first one
847 is @math{omega = 0} for an horizontal mode:
849 @example
850 f_data[3] = omega
851 @end example
853 and the last one is for the bissector parameter @math{gamma = 2 * mu}.
855 @example
856 f_data[4] = gamma - 2 * fmod(mu, M_PI)
857 @end example
859 One question could be why this complicate @code{f4 = gamma - 2 * fmod(mu, M_PI)}
860 equation instead of a simpler @code{f4 = gamma - 2 * mu} ?
861 this is because the bissector_horizontal method is also called by a solution
862 multiplicator to gives the user plenty of equivalent solutions. This multiplicator
863 do some operations like @code{omega = pi - omega} or @code{omega = - omega} on the axes.
864 Then it check that the new angles combination gives also @math{f(x) = 0}. This is the
865 explaination of this more complicate equation.
867 So in our case we need to build something like
869 @verbatim
870 static int psi_constant_vertical(const gsl_vector *x, void *params, gsl_vector *f)
872         double mu, omega, gamma;
873         double const *x_data = gsl_vector_const_ptr(x, 0);
874         double *f_data = gsl_vector_ptr(f, 0);
876         RUBh_minus_Q(x_data, params, f_data);
878         f_data[3] = ???;
880         return  GSL_SUCCESS;
882 @end verbatim
884 The missing part is about the psi computation. f3 = psi (target) - psi(x).
885 Calculation psi is done in the psi pseudo axis common part.
887 @example
888 static int psi(const gsl_vector *x, void *params, gsl_vector *f)
889 @end example
891 This psi method is the equivalent of psi_constant_vertical. So you need
892 to factorize the psi calculation in between psi_constant_vertical and
893 psi.
895 @node Index,  , Developpement, Top
896 @unnumbered Index
898 @printindex cp
900 @bye