put the diffractometers pictures at the right place.
[hkl.git] / Documentation / sphinx / source / development.rst
blobb44562631e1a2d61ad083d5fb62849a96871d5d3
1 .. _development:
3 Developpement
4 #############
6 Getting hkl
7 ***********
9 To get hkl, you can download the last stable version from sourceforge or if you
10 want the latest development version use `git <http://git.or.cz/>`_ or
11 `msysgit <http://code.google.com/p/msysgit/downloads/list>`_ on windows system and
12 do::
14         $ git clone git://repo.or.cz/hkl.git
16 or::
18         $ git clone http://repo.or.cz/r/hkl.git (slower)
20 then checkout the next branch like this::
22         $ cd hkl
23         $ git checkout -b next origin/next
25 Building hkl
26 ************
28 To build hkl you need `Python 2.3+ <http://www.python.org>`_ and the
29 `GNU Scientific Library 1.12 <http://www.gnu.org/software/gsl/>`_::
31      $ ./configure --disable-ghkl
32      $ make
33      $ sudo make install
35 you can also build a GUI interfaces which use `gtkmm <http://www.gtkmm.org>`_::
37     $ ./configure
38     $ make
39     $ sudo make install
41 eventually if you want to work also on the documentation you need
43 + `gtk-doc <http://www.gtk.org/gtk-doc/>`_ for the api
44 + `sphinx <http://sphinx.pocoo.org/>`_ for the html and latex doc.
45 + `asymptote <http://asymptote.sourceforge.net/>`_ for the figures::
47   $ ./configure --enable-gtk-doc
48   $ make
49   $ make html
51 Hacking hkl
52 ***********
54 you can send your patch to `Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>`_ using
55 ``git``
57 The developpement process is like this. Suppose you wan to add a new feature to
58 hkl create first a new branch from the next one::
60     $ git checkout -b my-next next
62 hack, hack::
64      $ git commit -a
66 more hacks::
68      $ git commit -a
70 now that your new feature is ready for a review, you can send by
71 email your work using git format-patch::
73      $ git format-patch origin/next
75 and send generated files `0001_xxx`, `0002_xxx`, ... to the author.
77 Howto add a diffractometer
78 **************************
80 In this section we will describe all steps requiered to add a new
81 diffractometer. We will use the kappa 4 circles exemple.
83 Adding Geometry
84 ===============
86 .. highlight:: c
87    :linenothreshold: 5
89 The first thing to do is to add the Geometry of this
90 diffractometer. You need to edit the `hkl/hkl-geometry-factory.h` file
92 add a new type ``HKL_GEOMETRY_KAPPA4C_VERTICAL`` into the ``_HklGeometryType`` enum::
94     enum _HklGeometryType
95     {
96         ...
97         HKL_GEOMETRY_KAPPA4C_VERTICAL
98     }
100 Now you must describe the diffractometer axes and the way they are
101 connected all togethers.  This diffractometer have one sample holder
102 and one detecter holder and four axes ("komega", "kappa", "kphi" and
103 "tth") So you need to add a new init method for this diffractometer.::
105        static void hkl_geometry_init_kappa4C_vertical(HklGeometry *self, double alpha)
106        {
107                 HklHolder *h;
109                 self->name = "K4CV";
110                 h = hkl_geometry_add_holder(self);
111                 hkl_holder_add_rotation_axis(h, "komega", 0, -1, 0);
112                 hkl_holder_add_rotation_axis(h, "kappa", 0, -cos(alpha), -sin(alpha));
113                 hkl_holder_add_rotation_axis(h, "kphi", 0, -1, 0);
115                 h = hkl_geometry_add_holder(self);
116                 hkl_holder_add_rotation_axis(h, "tth", 0, -1, 0);
117         }
119 first we set the diffractometer name by::
121       self->name = "K4CV";
123 This name is used in the Tango diffractometer device to refer to this
124 diffractometer.
126 Then you can create the first holder with it's three axes. The order
127 of the axis is from the farest to the closest of the sample. In this
128 case, komega -> kappa -> kphi::
130       h = hkl_geometry_add_holder(self);
131       hkl_holder_add_rotation_axis(h, "komega", 0, -1, 0);
132       hkl_holder_add_rotation_axis(h, "kappa", 0, -cos(alpha), -sin(alpha));
133       hkl_holder_add_rotation_axis(h, "kphi", 0, -1, 0);
135 Same thing for the other holder holding the detector::
137      h = hkl_geometry_add_holder(self);
138      hkl_holder_add_rotation_axis(h, "tth", 0, -1, 0);
140 now it is almost finish for the geometry part. you just need to add it
141 in the factory::
143    Hklgeometry *hkl_geometry_factory_new(HklGeometryType type, ...)
144    {
145         ...
146         switch(type){
147                 ...
148                 case HKL_GEOMETRY_KAPPA4C_VERTICAL:
149                         va_start(ap, type);
150                         alpha = va_arg(ap, double);
151                         va_end(ap);
152                         hkl_geometry_init_kappa4C_vertical(geom, alpha);
153                 break;
154         }
155         ...
156    }
158 in this exemple the geometry take one parameter. The fatory can have a
159 variable number of parameters you just need to take care of this with
160 the va_arg methods.
162 Adding PseudoAxis mode
163 ======================
165 Suppose you want to add a new mode to the hkl pseudo axes. Lets call
166 it ``psi constant vertical`` to the eulerian 6 circle geometry.
168 The starting point is to look in the file ``src/hkl-pseudoaxis-factory.c`` for::
170     HklPseudoAxisEngineList *hkl_pseudo_axis_engine_list_factory(HklGeometryType type)
172 in that method you can see this in the eulerian 6 circle part::
174    case HKL_GEOMETRY_EULERIAN6C:
175         hkl_pseudo_axis_engine_list_add(self, hkl_pseudo_axis_engine_e6c_hkl_new());
176         hkl_pseudo_axis_engine_list_add(self, hkl_pseudo_axis_engine_e6c_psi_new());
177         hkl_pseudo_axis_engine_list_add(self, hkl_pseudo_axis_engine_q2_new());
178    break;
180 so as you can see there is three pseudo axis engine for this
181 geometry. Your mode if for the hkl pseudo axis. so let look in the
182 ``hkl_pseudo_axis_engine_e6c_hkl_new()`` method.  You can find it
183 in the file ``include/hkl/hkl-pseudoaxis-e6c.h`` which contain this::
185    #ifndef __HKL_PSEUDOAXIS_E6C_H__
186    #define __HKL_PSEUDOAXIS_E6C_H__
188    #include <hkl/hkl-pseudoaxis-auto.h>
190    HKL_BEGIN_DECLS
192    extern HklPseudoAxisEngine *hkl_pseudo_axis_engine_e6c_hkl_new(void);
193    extern HklPseudoAxisEngine *hkl_pseudo_axis_engine_e6c_psi_new(void);
195    HKL_END_DECLS
197    #endif /* __HKL_PSEUDOAXIS_E6C_H__ */
199 strange only 2 methods nothing about
200 ``hkl_pseudo_axis_engine_q2_new()``. This is because the
201 implementation of this method is common to more than one geometry. So
202 you can find it in the file ``hkl/hkl-pseudoaxis-common-q.h``
204 now you need to change the code of
205 ``hkl_pseudo_axis_engine_e6c_hkl_new(void)``. Lets look about it in
206 the file ``src/hkl-pseudoaxis-e6c-hkl.c``::
208     HklPseudoAxisEngine *hkl_pseudo_axis_engine_e6c_hkl_new(void)
209     {
210         HklPseudoAxisEngine *self;
211         HklPseudoAxisEngineMode *mode;
213         self = hkl_pseudo_axis_engine_hkl_new();
215         /* bissector_vertical */
216         mode = hkl_pseudo_axis_engine_mode_new(
217                 "bissector_vertical",
218                 NULL,
219                 hkl_pseudo_axis_engine_mode_get_hkl_real,
220                 hkl_pseudo_axis_engine_setter_func_bissector_vertical,
221                 0,
222                 4, "omega", "chi", "phi", "delta");
223         hkl_pseudo_axis_engine_add_mode(self, mode);
225         /* constant_omega_vertical */
226         mode = hkl_pseudo_axis_engine_mode_new(
227                 "constant_omega_vertical",
228                 NULL,
229                 hkl_pseudo_axis_engine_mode_get_hkl_real,
230                 hkl_pseudo_axis_engine_mode_set_hkl_real,
231                 0,
232                 3, "chi", "phi", "delta");
233         hkl_pseudo_axis_engine_add_mode(self, mode);
235         /* constant_chi_vertical */
236         mode = hkl_pseudo_axis_engine_mode_new(
237                 "constant_chi_vertical",
238                 NULL,
239                 hkl_pseudo_axis_engine_mode_get_hkl_real,
240                 hkl_pseudo_axis_engine_mode_set_hkl_real,
241                 0,
242                 3, "omega", "phi", "delta");
243         hkl_pseudo_axis_engine_add_mode(self, mode);
245         /* constant_phi_vertical */
246         mode = hkl_pseudo_axis_engine_mode_new(
247                 "constant_phi_vertical",
248                 NULL,
249                 hkl_pseudo_axis_engine_mode_get_hkl_real,
250                 hkl_pseudo_axis_engine_mode_set_hkl_real,
251                 0,
252                 3, "omega", "chi", "delta");
253         hkl_pseudo_axis_engine_add_mode(self, mode);
255         /* lifting_detector_phi */
256         mode = hkl_pseudo_axis_engine_mode_new(
257                 "lifting_detector_phi",
258                 NULL,
259                 hkl_pseudo_axis_engine_mode_get_hkl_real,
260                 hkl_pseudo_axis_engine_mode_set_hkl_real,
261                 0,
262                 3, "phi", "gamma", "delta");
263         hkl_pseudo_axis_engine_add_mode(self, mode);
265         /* lifting_detector_omega */
266         mode = hkl_pseudo_axis_engine_mode_new(
267                 "lifting_detector_omega",
268                 NULL,
269                 hkl_pseudo_axis_engine_mode_get_hkl_real,
270                 hkl_pseudo_axis_engine_mode_set_hkl_real,
271                 0,
272                 3, "omega", "gamma", "delta");
273         hkl_pseudo_axis_engine_add_mode(self, mode);
275         /* lifting_detector_mu */
276         mode = hkl_pseudo_axis_engine_mode_new(
277                 "lifting_detector_mu",
278                 NULL,
279                 hkl_pseudo_axis_engine_mode_get_hkl_real,
280                 hkl_pseudo_axis_engine_mode_set_hkl_real,
281                 0,
282                 3, "mu", "gamma", "delta");
283         hkl_pseudo_axis_engine_add_mode(self, mode);
285         /* double_diffraction vertical*/
286         HklParameter h2;
287         HklParameter k2;
288         HklParameter l2;
290         hkl_parameter_init(&h2, "h2", -1, 1, 1,
291                            HKL_TRUE, HKL_TRUE,
292                            NULL, NULL);
293         hkl_parameter_init(&k2, "k2", -1, 1, 1,
294                            HKL_TRUE, HKL_TRUE,
295                            NULL, NULL);
296         hkl_parameter_init(&l2, "l2", -1, 1, 1,
297                            HKL_TRUE, HKL_TRUE,
298                            NULL, NULL);
300         mode = hkl_pseudo_axis_engine_mode_new(
301                 "double_diffraction_vertical",
302                 NULL,
303                 hkl_pseudo_axis_engine_mode_get_hkl_real,
304                 hkl_pseudo_axis_engine_mode_set_double_diffraction_real,
305                 3, &h2, &k2, &l2,
306                 4, "omega", "chi", "phi", "delta");
307         hkl_pseudo_axis_engine_add_mode(self, mode);
309         /* bissector_horizontal */
310         mode = hkl_pseudo_axis_engine_mode_new(
311                 "bissector_horizontal",
312                 NULL,
313                 hkl_pseudo_axis_engine_mode_get_hkl_real,
314                 hkl_pseudo_axis_engine_setter_func_bissector_horizontal,
315                 0,
316                 5, "mu", "omega", "chi", "phi", "gamma");
317         hkl_pseudo_axis_engine_add_mode(self, mode);
319         /* double_diffraction_horizontal */
320         mode = hkl_pseudo_axis_engine_mode_new(
321                 "double_diffraction_horizontal",
322                 NULL,
323                 hkl_pseudo_axis_engine_mode_get_hkl_real,
324                 hkl_pseudo_axis_engine_mode_set_double_diffraction_real,
325                 3, &h2, &k2, &l2,
326                 4, "mu", "chi", "phi", "gamma");
327         hkl_pseudo_axis_engine_add_mode(self, mode);
329         hkl_pseudo_axis_engine_select_mode(self, 0);
331         return self;
332     }
334 so you "just" need to add a new mode like this::
336         /* double_diffraction_horizontal */
337         mode = hkl_pseudo_axis_engine_mode_new(
338                 "psi_constant_vertical",
339                 NULL,
340                 hkl_pseudo_axis_engine_mode_get_hkl_real,
341                 hkl_pseudo_axis_engine_mode_set_psi_constant_vertical,
342                 3, &h2, &k2, &l2,
343                 4, "omega", "chi", "phi", "delta");
344         hkl_pseudo_axis_engine_add_mode(self, mode);
346 So the first parameter of the hkl_pseudo_axis_engine_mode_new method
348 + name is the name of the mode
349 + then the init functions (usually you need to store the current state of the geometry to be able to use the pseudo axis). Here no need for this init method so we put ``NULL``.
351 + then the get method which compute for a given geometry the pseudo axis value. the hkl get method ``hkl_pseudo_axis_engine_mode_get_hkl_real`` is completely generic and do not depend of the geometry. No need to write it.
353 + then the set method which compute a geometry for the given pseudo axis values. Now you need to work a little bit and write the set method.
355 + the parameters of your mode
357   + first the number of parameters : 3
358   + then each parameters (pointer on the right parameters) for this mode we have 3 parameters h2, k2, l2 which are the coordinates of a sample reference direction use to compute the psi value.
360 + the name of axes used by the set method.
362   + first the number of axes used by the set method : 4
363   + then all axes names.
365 In fact the "set" method know nothing about the axes names, so you can
366 use a set method with different kind of geometries. The association is
367 only done during the mode creation.
369 At the end you need to add this mode to the pseudo axis engine with
370 ``hkl_pseudo_axis_engine_add_mode(self, mode);``
372 that's all.
374 Now let see how this "set" method could be written. In our case we
375 want to compute the geometry angles for a given h, k, l pseudo axis
376 values keeping the angle between the reference reciprocal space vector
377 (h2, k2, l2) and the diffraction plane defined by the incomming beam
378 and the outgoing beam::
380             static int hkl_pseudo_axis_engine_mode_set_psi_constant_vertical(HklPseudoAxisEngine *engine,
381                                                                              HklGeometry *geometry,
382                                                                              HklDetector *detector,
383                                                                              HklSample *sample)
384             {
385                 hkl_pseudo_axis_engine_prepare_internal(engine, geometry, detector,
386                                                         sample);
388                 return hkl_pseudo_axis_engine_solve_function(engine, psi_constant_vertical);
389             }
391 the prepare internal part is about initializing the solver with the
392 given geometry, detector and sample. Then comes the
393 hkl_pseudo_axis_engine_solve_function which need the
394 psi_constant_vertical function to work. This method use the GSL
395 library to find the given function roots (where f(x) = 0).  Lets see
396 how it works for the "bissector_horizontal" mode::
398     static int bissector_horizontal(const gsl_vector *x, void *params, gsl_vector *f)
399     {
400         double mu, omega, gamma;
401         double const *x_data = gsl_vector_const_ptr(x, 0);
402         double *f_data = gsl_vector_ptr(f, 0);
404         RUBh_minus_Q(x_data, params, f_data);
406         mu = x_data[0];
407         omega = x_data[1];
408         gamma = x_data[4];
410         f_data[3] = omega;
411         f_data[4] = gamma - 2 * fmod(mu, M_PI);
413         return  GSL_SUCCESS;
414     }
416 The bissector_horizotal method is used by the setter method of the
417 mode to compute the right set of axes angles corresponding to the
418 pseudo axes values you want to reach. This method compute the
419 difference between these pseudo axes values and the ones computed from
420 the axes angles. It can be decompose in three parts:
422 The first three of these equations are given for the function
423 ``RUBH_minus_Q``: they are the diference between the h,k,l values that
424 want to be set and the h,k,l values computed for a possible
425 combination of angles::
427             f_data[0] = h-h(x)
428             f_data[1] = k-k(x)
429             f_data[2] = l-l(x)
431 As the bissector_horizontal mode use 5 axes you need to find 2 other
432 equations to be able to solve your mode. The first one is :math:`omega
433 = 0`} for an horizontal mode::
435   f_data[3] = omega
437 and the last one is for the bissector parameter :math:`gamma=2*mu`::
439     f_data[4] = gamma - 2 * fmod(mu, M_PI)
441 One question could be why this complicate ``f4 = gamma - 2 * fmod(mu,
442 M_PI)`` equation instead of a simpler ``f4 = gamma - 2 * mu`` ?  this
443 is because the bissector_horizontal method is also called by a
444 solution multiplicator to gives the user plenty of equivalent
445 solutions. This multiplicator do some operations like ``omega = pi -
446 omega`` or ``omega = - omega`` on the axes.  Then it check that the
447 new angles combination gives also :math:`f(x) = 0`. This is the
448 explaination of this more complicate equation.
450 So in our case we need to build something like::
452    static int psi_constant_vertical(const gsl_vector *x, void *params, gsl_vector *f)
453    {
454         double mu, omega, gamma;
455         double const *x_data = gsl_vector_const_ptr(x, 0);
456         double *f_data = gsl_vector_ptr(f, 0);
458         RUBh_minus_Q(x_data, params, f_data);
460         f_data[3] = ???;
462         return  GSL_SUCCESS;
463     }
465 The missing part is about the psi computation. ``f3 = psi (target) -
466 psi(x)``.  Calculation psi is done in the psi pseudo axis common
467 part::
469            static int psi(const gsl_vector *x, void *params, gsl_vector *f)
471 This psi method is the equivalent of psi_constant_vertical. So you
472 need to factorize the psi calculation in between psi_constant_vertical
473 and psi.