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
14 $ git clone git://repo.or.cz/hkl.git
18 $ git clone http://repo.or.cz/r/hkl.git (slower)
20 then checkout the next branch like this::
23 $ git checkout -b next origin/next
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
35 you can also build a GUI interfaces which use `gtkmm <http://www.gtkmm.org>`_::
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
54 you can send your patch to `Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>`_ using
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
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.
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::
97 HKL_GEOMETRY_KAPPA4C_VERTICAL
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)
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);
119 first we set the diffractometer name by::
123 This name is used in the Tango diffractometer device to refer to this
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
143 Hklgeometry *hkl_geometry_factory_new(HklGeometryType type, ...)
148 case HKL_GEOMETRY_KAPPA4C_VERTICAL:
150 alpha = va_arg(ap, double);
152 hkl_geometry_init_kappa4C_vertical(geom, alpha);
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
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());
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>
192 extern HklPseudoAxisEngine *hkl_pseudo_axis_engine_e6c_hkl_new(void);
193 extern HklPseudoAxisEngine *hkl_pseudo_axis_engine_e6c_psi_new(void);
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)
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",
219 hkl_pseudo_axis_engine_mode_get_hkl_real,
220 hkl_pseudo_axis_engine_setter_func_bissector_vertical,
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",
229 hkl_pseudo_axis_engine_mode_get_hkl_real,
230 hkl_pseudo_axis_engine_mode_set_hkl_real,
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",
239 hkl_pseudo_axis_engine_mode_get_hkl_real,
240 hkl_pseudo_axis_engine_mode_set_hkl_real,
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",
249 hkl_pseudo_axis_engine_mode_get_hkl_real,
250 hkl_pseudo_axis_engine_mode_set_hkl_real,
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",
259 hkl_pseudo_axis_engine_mode_get_hkl_real,
260 hkl_pseudo_axis_engine_mode_set_hkl_real,
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",
269 hkl_pseudo_axis_engine_mode_get_hkl_real,
270 hkl_pseudo_axis_engine_mode_set_hkl_real,
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",
279 hkl_pseudo_axis_engine_mode_get_hkl_real,
280 hkl_pseudo_axis_engine_mode_set_hkl_real,
282 3, "mu", "gamma", "delta");
283 hkl_pseudo_axis_engine_add_mode(self, mode);
285 /* double_diffraction vertical*/
290 hkl_parameter_init(&h2, "h2", -1, 1, 1,
293 hkl_parameter_init(&k2, "k2", -1, 1, 1,
296 hkl_parameter_init(&l2, "l2", -1, 1, 1,
300 mode = hkl_pseudo_axis_engine_mode_new(
301 "double_diffraction_vertical",
303 hkl_pseudo_axis_engine_mode_get_hkl_real,
304 hkl_pseudo_axis_engine_mode_set_double_diffraction_real,
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",
313 hkl_pseudo_axis_engine_mode_get_hkl_real,
314 hkl_pseudo_axis_engine_setter_func_bissector_horizontal,
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",
323 hkl_pseudo_axis_engine_mode_get_hkl_real,
324 hkl_pseudo_axis_engine_mode_set_double_diffraction_real,
326 4, "mu", "chi", "phi", "gamma");
327 hkl_pseudo_axis_engine_add_mode(self, mode);
329 hkl_pseudo_axis_engine_select_mode(self, 0);
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",
340 hkl_pseudo_axis_engine_mode_get_hkl_real,
341 hkl_pseudo_axis_engine_mode_set_psi_constant_vertical,
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);``
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,
385 hkl_pseudo_axis_engine_prepare_internal(engine, geometry, detector,
388 return hkl_pseudo_axis_engine_solve_function(engine, psi_constant_vertical);
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)
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);
411 f_data[4] = gamma - 2 * fmod(mu, M_PI);
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::
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::
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)
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);
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
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